
Event ordering is always a problem in distributed systems. As it is not a centralized system, it has the following constraints:
- Absence of shared memory
- Absence of a global clock
Typically you do something like this in your code: DateTime.Now. The problem behind this is your are receiving the time on your node/machine executing this code. If you have multiple instances of your program on different nodes, you will run in the phenomena called "clock drift". This mean a clock drifts apart from a reference clock. So even if the clocks are synchronized once or more per day, the issue is not solved. Crystal based clocks will always drift especially when it comes to high speed communication.
Depending on your concrete situation you have the following options:
- Use a logical clock (monotonic clocks, lamport clocks, vector clocks)
- Use some kind of optimistic locking information
I would like to give a sample of the optimic locking approach.
First of all if you have to deal with a create command you are on the better side as there is no data existing. The situation changes if you face an update command. Here things can get worse. If you have an object "A" which is simultaneously edited by user "U1" and user "U2" and both of them press the "update button" together the one or the other will win. Even if "U1" is a little bit earlier and your server node does a DateTime.Now "U2" can still win because of the clock thrift as the request of "U2" was balanced to the other node running the other instance of your program.
​
In this scenario where you have a user interaction an optimistic approach can help. The idea is simple: At the time object "A" gets loaded some kind of "UpdateCount" field is sent to the client. As soon the first user sends the update to your backend, your data repository validates the existing tuple against the received updatecount. If it is the same: Increment and update the tuple in one atomic operation. When the other user sends his update command, the mechanism can reject the update or whatever action will be triggered. With this approach we also have some kind of event ordering: We know at least that the second event arrived later than the other eliminating the clock drift.