Sequencing Events (timestamp vs. nanos vs. sequential uuids vs. DB id vs. ?)

934 views
Skip to first unread message

Joel Trunick

unread,
Feb 17, 2016, 11:35:38 AM2/17/16
to DDD/CQRS

What is the best practice for sequencing the event store? I need something to hand clients so that they know where they are.

I'll be using timestamp anyway, but I'm guessing its too coarse to sequence by, particularly if a command generates multiple events. Maybe nanos would be fine enough resolution, and it's also a timestamp at the same time? Otherwise, I guess I'd lean toward a UUID for each event, possibly sequenced or maybe a DB ID, which makes me cringe a bit is the right way to go?

FWIW, I'm using Java/Postgres.

Joel

Danil Suits

unread,
Feb 17, 2016, 12:11:27 PM2/17/16
to DDD/CQRS
Monotonically increasing sequence number.

I think in the long run you're solution will be a lot easier to understand if you use identifiers for identity, timestamps as an estimate of when things happen, and sequence numbers for historical ordering.

So keep the UUID; that's useful for knowing if an event has already been observed.  Tuck the timestamp(s) into the meta data.  But track each persisted event with a globally unique sequence number.


As far as I can tell, it shouldn't be important that the sequence number is globally unique; events in two different streams with no "happens before" ordering don't have a global ordering.  You should be able to process the events in any order that respects the ordering of the stream, and get the same result.

But supporting "append" semantics requires that the current position in the stream is stable, and I don't know how to ensure that happens without a global sequence.

Example
Stream A includes events [0..2]
Stream B includes events [0..3]

Stream union(A,B) includes [A0..A2,B0..B3] in some order.  If this stream is going to be stable, then we need the "next" event that appears in A or B to also appear as U(a,b)7.  Which either means that you have a global sequence number....


OR perhaps that union(A,B) is actually a sequence of links back to the events in the original streams.  That's an inflection point in my build/buy function; once I scale past global sequence numbers, I'm noping out of rolling my own.


Jeff Harris

unread,
Feb 25, 2016, 7:07:29 PM2/25/16
to DDD/CQRS
Personally, I have two sequence numbers:

1. Stream Sequence
Because I'm using events to back aggregates, I use this sequentially monotonically increasing number.

Each event must be exactly one greater than the one before it in the stream. I enforce this at the DB level with a unique index, so this effectively serves as concurrency protection. If the same aggregate was modified in two concurrent transactions, one of them would fail to save because they both would be trying to save the same stream id + sequence.

2. Global Sequence
Publishers and denormalizers need to know where they are at, so I just use a db IDENTITY column to do a monotonically increasing number.

If you're not needing to back aggregates and just generate events, then there are any number of ways of doing it depending on your application (one example: you could use a GUID-like thing or something http://www.informit.com/articles/article.aspx?p=25862)

Jeff Hicks

unread,
Mar 21, 2016, 12:49:26 AM3/21/16
to DDD/CQRS
Have you looked at vector clocks? 

Ruslan Rusu

unread,
Mar 21, 2016, 10:04:14 AM3/21/16
to DDD/CQRS
Yes, we looked at version clock

This is what you mean ?

nextAggVersion = max(cmdVersion, inc(currentAggvesion))

Greg Young

unread,
Mar 21, 2016, 10:10:28 AM3/21/16
to ddd...@googlegroups.com
No he means vector clocks https://en.wikipedia.org/wiki/Vector_clock
> --
> You received this message because you are subscribed to the Google Groups
> "DDD/CQRS" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to dddcqrs+u...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.



--
Studying for the Turing test
Reply all
Reply to author
Forward
0 new messages