1

I'm building a single-page web application and I'm planning on leveraging a server-side event stream to keep the client up to date with changes.

While thinking this through there is a concurrency scenario that I thought of and I'm not sure if the solution I'm planning to implement would be best:

  1. The UI subscribes to the event stream and receives all new events from time X.
  2. The UI loads data from various endpoints at time X. E.g. total number of active users.
  3. A user gets deactivated at time X and a UserDeactivated event is added to the stream.
  4. The UI receives a UserDeactivated event at time X+1.

At that point, the UI cannot just blindly reduce the total number of active users by 1 because the total it already loaded might reflect the deactivation already.

The approach I'm planning to use for this problem is to keep track of the date and time at which every bits of data were loaded and subscribe to the event stream at application_loaded_time - some_offset to make sure I'm not missing any events.

Then, when an event is received every subscriber would check the date and time the data they are responsible to maintain was fetched and they would either apply the event if the event occurred after data_fetching_datetime or ignore it if the event occurred before data_fetching_datetime.

Would that be a good synchronization strategy? Is there something that I'm missing with the approach? Is there a better way?

One thing I'd have to make sure is that the client time is not out of sync with the server. I'd probably have to add a custom HTTP header that specifies when the request has started to get processed.

For individual aggregates I can just rely on the version of the AR I've loaded and the version of the event, but for aggregated queries such as counts there is no such version to leverage.

NOTE: I'm not doing event-sourcing nor CQRS and I do not have projections. With projections I'd have a sequence number (last processed event) for each view which I could return with the result, but that is not the case here. I guess that I could compute a version number for every query by counting the number of events of specific types at the time the query was made, but I'm not sure that would perform well.

plalx
  • 42,889
  • 6
  • 74
  • 90
  • I think you'll be closer to something practical if you replace "time" with "sequenceNumber". – VoiceOfUnreason Sep 15 '16 at 20:20
  • @VoiceOfUnreason A sequence number seems to be fine for single aggregates, but how would I use a sequence number for aggregated operations like a count or a sum? I could compute a hash of all versions implicated in the count, but I'm not so sure how I'd use that hash afterwards. – plalx Sep 15 '16 at 20:58
  • Wouldn't a sequence number per stream suffice? Then you could build up views knowing exactly where you are in processing each stream – tomliversidge Sep 16 '16 at 07:13
  • @tomliversidge I added a note in the question regarding your comment. – plalx Sep 16 '16 at 13:19
  • How are the events initially loaded? Don't you know the position in the stream they are loaded from? I'm confused why you cannot just decrement when receiving a new Deactivated event – tomliversidge Sep 16 '16 at 16:48
  • @tomliversidge The UI gets it's initial state from a current state query. E.g. totalActiveUsers = totalActiveUsers(). The totalActiveUsers query is a COUNT based on the current state. From that point I wanted to keep the totalActiveUsers count up to date on the UI by consuming an event stream. What is hard however is to determine wheter the events the UI will receive should by applied or not since the query doesn't have any version. It's not a projection... – plalx Sep 16 '16 at 17:17
  • Are the events themselves sequenced (or at least timestamped by the server)? [I just noticed this is several years old... :P] – Mike B. Mar 25 '19 at 19:32
  • @MikeB. Yes they are versioned. I guess I could compute the max sequence number from all events that were aggregated in the COUNT and use that as a query version. My current solution is to always query the server for the count when I receive a new event. Not optimal, but works for the time being. – plalx Mar 25 '19 at 20:36
  • Exactly what I was going to suggest. :) If you get the max sequence included with the count, you can either start your subscription from there (if your API allows) or subscribe prior to the count query, cache the events until the query returns, then discard any cached events that are below the "count" sequence. – Mike B. Mar 25 '19 at 20:44

0 Answers0