A sends a message to "x = 2" to B and C. B receives this message and sends "x = 3" to C.
Now, C receives two messages one from A and one from B. It could receive them in any order. However, there is no way for it to tell whether there is a causal order or not.
With a clock embedded in the messages, one can state that "x=2" happened before "x=3". (or more strictly, "x=3" def did not happen before "x=2").
Extending this, if C and D receive copies of the above messages from A and B, we want C and D to both process them in the same (deterministic) order.
In the context of a city simulator, every road segment could be an actor, as well as every simulated vehicle. Two vehicles might try to enter a road segment at the same time, so they both send a message :enter(entity_1) to :road_segment_1. Then the :road_segment_1 actor decides what to do with those messages.
Structuring it this way allows you to massively distribute these kinds of simulations, which is exactly why citybound attempted an actor-based approach. There's a load of overhead, and there's still issues with buffers and backpressure, but in theory you could infinitely scale a simulation this way.
In your example, the vehicles' messages have no causal relationship; they are concurrent. Order does not matter. However, for the sake of determinism in a simulation, the above constraint is necessary even for concurrent messages.
For example, one of the projects I have worked on is one of the largest trading engines in the world, with high concurrency. To make it testable and fault-tolerant, all incoming messages are timestamped and handled in time-serial order. The same timestamp helps with idempotence.
When testing the engine core and downstream systems, a previous day's test data can be input in timestamp order (instead of concurrently), so that we know the behaviour of the system in the face of a fix.
Dataflow systems (which is most software) should be idempotent and deterministic. If random() is called, then fixing the seed should make it deterministic.
Basing the behavior of your simulator on yesterdays data is a neat trick, but it has no relationship to the actual world. Not in a million years would the exact same market behavior of yesterday happen again.
Instead of building it deterministic, you could simply run your system a hundred times, and maybe purposefully mess with the timings to make execution more non-deterministic. And instead of a precise number "yesterday we would have made X amount of money with our trading engine", you would come up with a distribution that is much more predictive of the future "with market circumstances similar to yesterday we would have made between X and Y amount of money with our trading engine".
Anyway, I don't work on trading systems so I probably shouldn't tell you how to do your job. This is just how I see simulation systems.
With good reason. It is called caution. We're talking about a nation's economy.
You misunderstand me about the reason for running yesterday's or any other day's full load. The idea is that after you have made a bug fix or a performance fix, you want to see that the output reflects the fix; the best way to do that is do a diff with yesterday's output given the same input, and one should be able to ensure both correctness (all changes accounted for) and completeness (all the input tuples that ought to reflect a difference do reflect the correct difference).
Given that there are a hundreds of thousands of events a second and terabytes of data, it is impractical to run a test 100 different ways. Besides, how do you know you got it correct? Events are not commutative because there are limits. Deposit is not commutative with Withdraw, because Withdraw can throw an OutOfMoneyException. Order matters.
Please refer to [1] Parallel and Distributed Simulation, fig. 9.4, pg 265 for an example of an anomaly introduced by the lack of a total order. The text also covers why it is necessary to have a model of time; it is not enough to have agents sending messages to each other and let them duke it out.
[1] https://doc.lagout.org/science/0_Computer%20Science/3_Theory...
When you said financial simulation I thought you were modeling the behaviours of actors in a market, and how your system would trade against those actors, but what you're doing is more like banking or an exchange?
Rand() and friends obviously needs to be handled appropriately but as long as you have only one owner of data (and thus one writer), by passing messages of the form “try to add 1 to x” rather than “x=5” or “add 1 to x” message-queues have a strict ordering by nature.