Why Can’t I Update an Event

Last week on a call with someone the question came up about the Event Store about why can they not update and event and how should they handle the case where they need to. The conversation that came out of this was very rich in architectural insight into how the event store works as well as overall event sourcing understanding so I thought that it would be worth spending a bit of time to write up where the constraint comes from.

An Event Sourcing Perspective

Let’s start with why you want to update an event? An event is a fact that happened at a point in time with the understanding of it from that point in time, a new understanding of the fact would be a new fact(naturally this does not apply to politicians). To update a previous event is generally a very bad idea. Many want to go back and update events to new versions, this is not the best way to handle versioning!

The prefered mechanism from an event sourcing perspective is to write a new face that supercedes the old fact. As an example I could write that event 7 is a mistake, this is a correction, I might as well put in a comment “this was due to bug #1342” (similar to a journal entry in accounting). There are a few reasons this is a better way of handling things.

The first is my ability to look back at my history. If I were to change the fact and I look back at that point in time I have changed what it means. What about others who made decisions at that point in time? I can no longer see what it was they made decisions off of. Beyond this I might have a very valid query to ask your event streams of “how long on average does it take us to find bugs in previous events”.

The second model leads us to two types of queries supported on event streams (as-of vs as-at).

Beyond that with Event Sourcing the updating of an event can be inherently evil. How do you update any projections that the update occured? What about other subscribers who may be listening to the streams? Any easy answer might be to replay fully all involved with the stream but this quickly falls apart.

These are the primary reasons why the Event Store does not support an update operation on an event. There are however some wonderful architectural benefits that come from this particular constraint.

Architectural Goodness

If we prevent an event from ever being updated, what would the cachability of that event be? Yes it would be infinite. The Event Store supports a RESTful API (ATOM). All events served from the event store have infinite cachability, what does that mean?

Imagine you have a projection updating into a SQL table that has been running for the past eight weeks. You make a change and need to restart it (replaying from event 0). When the replay occurs and it requests events from the Event Store where do they likely come from? Your hard drive! You don’t make requests to the Event Store for them.

Beyond the events being infinitely cachable if you look through our atom implementation in fact every single request we serve with the exception of the head uri (http://somewhere.com/streams/{stream}) is also infinitely cachable. In other words when you want to reread $all (say for 5m events) you will hit exactly one non-cachable request!

This is very important when we start talking about scalability and performance. The Event Store can pretty easily serve 3-5k atom requests/second on a laptop (per node in clustered version) but how many will actually get to the Event Store? In order to scale you focus on commoditized reverse proxies in front of the Event Store not scaling the Event Store itself. nginx or varnish can easily saturate your network, just drop them in front only head calls make it through (and there is even a setting per stream to allow caching for x seconds of head links).

This is often a hard lesson to learn for developers. More often than not you should not try to scale your own software but instead prefer to scale commoditized things. Building performant and scalable things is hard, the smaller the surface area the better. Which is a more complex problem a basic reverse proxy or your business domain?

This also affects performance of replays for subscribers as you can place proxies near the subscribers (local http cache is a great start!). This is especially true for say an occasionally connected system. Gmail uses this trick to provide “offlining out of the box” for web apps. Since much of the data will already be in the http cache your hits will be hitting it, in many cases you can build say a mobile app with no further support.

Over Atom if we allowed updates, NO uris could be cachable!

This is all cool but I actually need to update!

Occasionally there might be a valid reason why an event actually needs to be updated, I am not sure what they are but I imagine there must be some that exist. There are a few ways this can actually be done.

The generally accepted way of handling the scenario while staying within the no-update constraintis to create an entire new stream, copy all the events from the old stream (manipulating as they are copied). Then delete the old stream. This may seem like a PITA but remember all of the discussion above (especially about updating subscribers!).

Some however may be using the TCP API and are willing to take the pains and complexity that come from subscribers (you can imagine they have none). In this one case, updates would be acceptable and simpler than adding new events. We have been going back and forth on whether or not to support this. It would not be much work at all for us but I imagine that it would be misused 1000 times for every 1 time it was used reasonably. I am reminded of the examples of being able to call .NET code from a biztalk orchestration or being able to execute .NET code inside my SQL database both have valid uses but should rarely be used. Perhaps we will make a command line parameter –run-with-scissors-updates or make people build from source to enable.

7 Comments

  1. Posted May 29, 2013 at 11:03 am | Permalink | Reply

    Just to add to what you are saying above, there could be legal reasons why events need to be updated (or deleted). Such as, you are not allowed to store sensitive information for more than a certain amount of time. Or would that be a different scenario?

    • Posted May 29, 2013 at 11:11 am | Permalink | Reply

      This is absolutely the case in many industries and we 100% support it.

      While we may not allow the updating to delete a single event we do support the deleting of a stream. In such organizations the requirement is almost always to purge a whole streams worth of information (eg medical records of patient 201).

      For time sensitive data we also support on stream metadata two options for a stream $maxage and $maxcount. These are window operators that will automatically delete any information outside of the window.

  2. Posted July 1, 2013 at 6:04 pm | Permalink | Reply

    “The generally accepted way of handling the scenario while staying within the no-update constraintis to create an entire new stream, copy all the events from the old stream (manipulating as they are copied). Then delete the old stream.”
    Why we can not create stream with the same name as deleted stream (I can solve it by deleting files but I wouldn’t do it).

    if (slice.Status == SliceReadStatus.StreamNotFound
    || slice.Status == SliceReadStatus.StreamDeleted)
    {
    try
    {
    result.CreateStream(StreamName, Guid.NewGuid(), true, new byte[0]);
    }
    catch (Exception ex)
    {
    // If stream is deleted Exception “O kurde” (In Polish it mean “damn” but sounds like occured)
    throw;
    }

    break;
    }

    • Posted July 2, 2013 at 9:04 am | Permalink | Reply

      The deletion/recreation of a stream will be coming shortly after the 2.0 release.

    • Posted July 14, 2013 at 9:25 pm | Permalink | Reply

      Isn’t the main reason the cache as you write in the post? Will creating a new stream with the same name invalidate any caches?

      • Posted July 15, 2013 at 6:38 am | Permalink

        When it is released it will not invalidate any caches. It will instead start you off at position X where X is deleted position + 1 and the head for the stream will point last -> x. It will not be invalidating any intermediary caches.

  3. Posted March 13, 2014 at 1:29 am | Permalink | Reply

    Hi Greg, what’s your preferred approach to updating (versioning) the event objects and populating them from event streams containing “old” event versions?

    I’m currently using a system inspired by http://abdullin.com/event-sourcing/versioning, but looking for ways to improve my methodology.

    At present, with each deploy I can freely update the aggregates and handlers to use the latest version of all commands and events.

    Events are being serialized as Json with the fully-qualified type and assembly name included so that Newtonsoft.Json can deserialize each event into the object type that was originally serialized. As each event object is deserialized from the event stream, it is passed through an IEventUpdater object that returns an equivalent event (or events) of the latest version.

    This requires me to keep the event versions from each deploy in namespaces xx.V001, xx.V002 etc over the life of the project.

    The current state of each aggregate is represented by DOM objects I have created. These DOM objects have inheritance and contain other DOM objects though they are simple in the sense they have public fields but no methods or properties. They easily serialize to Json.

    Instead of writing events as POCO objects, I have allowed some of them to contain these DOM objects, especially events related to “adding” a DOM object.

    Because I have gone this way, I now find myself having to version the entire DOM with each deploy. The aggregate itself doesn’t need to be versioned, but the DOM does. And now the IEventUpdater object has to deal with complex and detailed changes to the DOM with each version.

    I don’t think it’s practical in my case to convert all event objects to POCO, because some of them, especially the “add_something” events, are adding an entire upper-level DOM object with all the objects (and variable-size lists) it contains.

    Well OK, I have just answered my own question and got the clarity I need. I must keep my event objects very small and granular (and POCO). The “add_something” events should add only a little bit at a time and there should be many different types of them to incrementally update the DOM object. It’s ok if a single “add dom object” command ends up firing off a dozen tiny “add this part of the dom object” events to handle it. Upgrading the many tiny events will be much simpler and require less logic in the long run. And I won’t have to version the entire DOM.

    However since I started writing this to ask your advice, I continue to submit it and look forward to receiving any more light on the topic.
    Do you have any of your own code samples dealing with versioning best practises?

    Thank you in advance for your time spent answering this question. I hope I have worded it clearly.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: