Search code examples
phpevent-sourcing

Can this cause a race condition in Event Sourcing


We are using Event Sourcing (specifically PHP, Laravel & Spatie's EventSourcing library, but I think my question pertains to Event Sourcing in general)

We have two Projectors - (ie, "Listeners" with code that will run)

ProjectorA::onEnrollmentCreated(){
    // does a db update to set status to 'pending'
}

ProjectorB::onEnrollmentCreated(){
    // does some own code, AND THEN records event `onEnrollmentApproved`,
    // which does a db update to set status to 'approved'
}

For this question, I think this is enough code to show. The desired effect is that the enrollment ends up with status "approved"

My question is:

To me, these listeners seem like asynchronously running functions, and so it is possible for there to be a hiccup in ProjectorA which would cause it to finish last and set the status back to "pending"

My teammate says that the way the projectors work, onEnrollmentCreated will always complete before onEnrollmentApproved is allowed to start. This makes no sense to me at all, and so my question is:

Can you explain this to me, or give me some links I can read more deeply about this particular aspect of Event Sourcing?

TIA!


Solution

  • You are right. There is no real guarantee that ProjectorB is alway run after ProjectorA. Although events do run in a synchronous manner by default(unless tweaked) but not projectors listening to them.

    To manage the order, you can add a weight property to your projector class and make sure that your projector class doesn't implement ShouldQueue interface as mentioned in the docs.

    Quoting from there:

    You can add a weight property to a projector to tweak the order projectors are run in. Projectors with a lower weight run first. When no explicit weight is provided, the weight is considered 0.

    namespace App\Projectors;
    
    use Spatie\EventSourcing\EventHandlers\Projectors\Projector;
    
    class MyProjector extends Projector
    {
        public int $weight = 5;
        
        //
    }