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:
- The UI subscribes to the event stream and receives all new events from time X.
- The UI loads data from various endpoints at time
X
. E.g. total number of active users. - A user gets deactivated at time
X
and a UserDeactivated event is added to the stream. - 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.