12

I've been reading on event sourcing lately and really like the ideas behind it but am stuck with the following problem.

Let's say you have N concurrent processes which receive commands (e.g. web servers), generate events as a result and store them in a centralised store. Let's also assume that all transient application state is maintained in the memory of the individual processes by sequentially applying events from the store.

Now, let's say we have the following business rule: each distinct user must have a unique username.

If two processes receive a user registration command for the same username X, they both check that X isn't in their list of usernames, the rule validates for both processes and they both store a "new user with username X" event in the store.

We have now entered an inconsistent global state because the business rule is violated (there are two distinct users with the same username).

In a traditional N server <-> 1 RDBMS style system, the database is used as a central point of synchronisation which helps prevent such inconsistencies.

My question is: how do event sourced systems typically approach this problem? Do they simply process every commands sequentially (e.g. limit the amount of process which can write to the store to 1)?

  • 1
    Is such restriction controlled by code or is it a db constrain? N events may or may not be dispatched-processed in sequence... N events may go through validations at the same time no discarting each other. If order matters you will need to sync the validation. Or to use queue to enqueue events do dispatch'em in sequentialy – Laiv May 19 '16 at 19:03
  • @Laiv right. For simplicity I assumed that there was no database, all state kept in memory. Processing specific types of commands sequentially through a queue would be an option but it feels like it could be complex deciding which commands can causally affect others and I would likely end up putting all commands in the same queue which amounts to having a single process processing commands :/ For example if I have a user adding comment on a blog post, the "delete user", "suspend user", "delete blog post", "disable blog post comments", etc. should all go in the same queue. – Olivier Lalonde May 19 '16 at 20:45
  • 1
    Im in agreement with you, to work with queues or semaphores is not simple. Neither to work with concurrency or event-source patterns. But basically all the solutions ends up with a system orchestrating event's traffic. However it's an interesting paradigm. There are also external caches oriented to tuples like Redis that could help to manage this traffic between nodes, like cacheing the last state of an entity or if such entity is being processed right now. Shared caches are quite common in this kind of developments. It may seem complex but don't giveup ;-) it's quite interesting – Laiv May 19 '16 at 21:00
6

In a traditional N server <-> 1 RDBMS style system, the database is used as a central point of synchronisation which helps prevent such inconsistencies.

In event sourced systems, the "event store" serves the same role. For an event sourced object, your write is an append of your new events to a particular version of the event stream. So, just as with concurrent programming, you could acquire a lock on that history when processing the command. It's more common for event sourced systems to take a more optimistic approach -- load the previous history, calculate the new history, then compare-and-swap. If some other command has also written to that stream, then your compare and swap fails. From there, you either rerun your command, or abandon your command, or perhaps even merge your results into the history.

Contention becomes a major problem if all N servers with their M commands are trying to write into a single stream. The usual answer here is to allocate a history to each event sourced entity in your model. So User(Bob) would have a distinct history from User(Alice), and writes to one won't block writes to the other.

My question is: how do event sourced systems typically approach this problem? Do they simply process every commands sequentially?

Greg Young on Set Validation

Is there an elegant way to check unique contraints on domain object attributes without moving business logic into service layer?

Short answer, in many cases, investigating that requirement more deeply reveals that either (a) it's a poorly understood proxy for some other requirement, or (b) that violations of the "rule" are acceptable if they can be detected (exception report), mitigated within some time window, or are low frequency (eg: clients can check if a name is available before dispatching a command to use it).

In some cases, where your event store is good at set validation (ie: a relational database), then you implement the requirement by writing to a "unique names" table in the same transaction that persists the events.

In some cases, you can only enforce the requirement by having all of the user names published into the same stream (which allows you to evaluate the set of names in memory, as part of your domain model). -- In this case, two processes will update attempt to update "the" stream history, but one of the compare-and-swap operations will fail, and the retry of that command will be able to detect the conflict.

  • 1) Thanks for the suggestions and references. When you say "compare-and-swap" do you mean that the process would, at the time of storing an event, detect new events have arrived since it started processing the command? I guess this would require an event store which supports "compare-and-swap" semantics, correct? (e.g. "write this event only and only if the last event has ID X")? – Olivier Lalonde May 19 '16 at 21:00
  • 2) I also like the idea of accepting temporary inconsistencies and repairing them eventually but I'm not sure how I would code for that in a reliable way... maybe have a dedicated process that validates events sequentially and creates rollback events when it detects something went wrong? Thanks! – Olivier Lalonde May 19 '16 at 21:00
  • (1) I would say "new version of the history" rather than "new events", but you have the idea; only replace the history if it is the one we are expecting. – VoiceOfUnreason May 19 '16 at 21:07
  • (2) Yup. It's a bit of logic that reads events from the store in batches, and at the end of batch broadcasts an exception report ("we have too many users named Bob"), or dispatches commands to compensate for the problem (assuming the right response is computable without human intervention). – VoiceOfUnreason May 19 '16 at 21:14
2

Sounds like you could implement a business process (saga in context of Domain Driven Design) for the user registration where the user is treated like a CRDT.

Resources

  1. https://doc.akka.io/docs/akka/current/distributed-data.html http://archive.is/t0QIx

  2. "CRDTs with Akka Distributed Data" https://www.slideshare.net/markusjura/crdts-with-akka-distributed-data to learn about

    • CmRDTs - operation based CRDTs
    • CvRDTs - state based CRTDs
  3. Code examples in Scala https://github.com/akka/akka-samples/tree/master/akka-sample-distributed-data-scala. Maybe "shopping cart" is most suitable.

  4. Tour of Akka Cluster – Akka Distributed Data https://manuel.bernhardt.io/2018/01/03/tour-akka-cluster-akka-distributed-data/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.