It didn’t get into read after write consistency well enough in my opinion, which breaks the event sourcing pattern for many use cases. E.g. in the create user example, there are states in the system where a user could create an account and then reload their page and have the account not be there if the write hasn’t propagated to the database used to satisfy reads.
While I'm no expert in the subject. Shouldn't read after write in event sourcing be nonsensical in terms of correctness? Instead you have to convert that problem into asynchronously waiting for a ACK message that your message had its intended effect. And only then would you ask to read the state?
EDIT: This of course does not cover the optimistic concurrency models in say PostgreSQL where you can effectively begin-write-read-rollback to extract information about a hypothetical state. Sadly I've had to use patterns like that before and there seem to be no alternative to that hack in event sourcing.
I don't quite understand this comment. Are you looking for a confirmation? Is it good enough to just have the most updated data for the state you want to track? I'm curious about what the specific use case was.
if you are waiting for ACK, you could alternatively create another stream that informs you of whatever write you were waiting for and therefore pushes you the most updated value or triggers the read. you can use the correlationID to make sure it's the set of changes that are bound to the original event you're tracking.
the "correctness" issue will crop up in whatever model you use since concurrency is the primary means for scaling a system. wrong order writes will have "correctness" problems with any db.
if you need strict ordering, the solution in ES will probably be the same as in pg-- separate the streams that need to be ordered and optimize them up front as much as you can. then post process when you no longer can't.
if data corruption is the issue, a changeset validation prior to the write probably works better than a rollback. if the validation is bad, catch it and send it to an exception stream you can track. event sourcing allows you to track by event/causation/correlation ids so you'll probably have an easier time debugging that.
The comment is talking about implementing sequential consistency: simplistically, if you have f();g() in a thread, and f() modifies the system's state, those modifications should be visible to g().
That's a distributed systems problem, rather than an event sourcing one. I'm sure we've all done something like comment on a site like HN and not seen our comment appear when we reload. The more distributed the system, the more likely it is we're hitting a stale cache somewhere.
Even the most absurdly reduced system running on a single machine, taking an HTTP request in and processing it fully to completion in all aspects before returning any response, is a distributed system - the browser is at the other end, running asynchronously. The user may tell the browser to reload before the single server has finished processing. When do both the user and the server agree that the account has been created?
To "fix" the problem with event sourcing, just don't add distributed components if you don't need them. Synchronise your "on event" action handlers with your event creation, and don't return success to the command until the handlers have completed.
You can even choose to wrap it all in a transaction so the event doesn't write unless the handlers all succeed, side-stepping the problem of desynchronised views due to handler bugs.
You still keep the (IMO) main benefit of event sourcing: you can define new views you didn't have to foresee and build them from the complete history of the system as if you'd known about them from the start.
With the reload before the server has acknowledged situation, you have’t told the user we have fully done action X, so there would be no expectation it persisted.
Yes, it is a distributed systems problem, but with a pure event sourcing approach as advocated in this article, every action is a potential data race.
Compare this to an application that uses a distributed data store like DybanoDB where read after write consistency is possible, while availability is still quite high. Apps that use it are easy to reason about for user actions, yet you can still use its event log for asynchronous events like sending mail.
That said, delaying acknowledging the write until you know it has propagated to all critical data stores is an interesting way to solve the problem.
Sequential consistency issues can appear in pretty much any system, not just distributed systems. E.g., a consumer thread that processes items from a queue. If you push into the queue and need subsequent read operations to see the processed state, you need to block after push until the item is processed.
It didn’t get into read after write consistency well enough in my opinion, which breaks the event sourcing pattern for many use cases. E.g. in the create user example, there are states in the system where a user could create an account and then reload their page and have the account not be there if the write hasn’t propagated to the database used to satisfy reads.