Description
⚠ This will be a non-trivial refactor ⚠
Describe desired feature
SimLN can currently be run with a fixed seed for its random activity setting, but payments produced by the simulator will not be fully deterministic.
This is because we have a single DestinationGenerator
which is used across many produce_events
tasks*. We can hit the following sequence of events:
- We have an RNG that produces values 1, 2, 3 then 4
- We have two
produce_events
tasks A and B that will both sleep for 5 seconds in between payments** - Both A and B are competing for the same lock on our
Arc<Mutex<DestinationGeneartor>>
First run:
- A gets lock first: A gets value 1, B gets value 2
- A gets lock first: A gets value 3, B gets value 4
Second run:
- A gets lock first: A gets value 1, B gets value 2
- B gets lock first: A gets value 4, B gets value 3
Across the two runs, the nodes get different destinations based on the order in which they acquire the lock. This averages out across multiple runs, but isn't properly deterministic. We should fix that.
* this was intentional, we don't want to have to store our lightning graph many times over
** the sleep time is random, so it's not that likely that they sleep for exactly the same time, but not impossible (especially if we start to speed up the clock)
Use case for feature
Fully deterministic runs are important for reproducible simulations.
Design Considerations
Part of this issue will be proposing a solution for this issue and discussing it here before opening up a PR, because it'll likely be a larger change to the codebase.
Incomplete thoughts that I have had about this are:
- Bite the memory bullet and copy the network graph for each node so that they have a distinct
DestinationGenerator
This gets rid of the locking issue, as each will lock their own destination and always get the same fixed values.
- Have a single
produce_events
loop that deterministically queues events for each node
Rather than spinning up a task for each node to produce its own events, have one master task that's responsible for dispatching events for each of the nodes. This would look something like:
- Get the next payment time for each node
- Drop them in a sorted heap
- Pull the next item off the heap, get the destination from the shared
DestinationGenerator
- Once dispatched, get the next payment time for the node and push it onto the heap
- There are probably better ways to do this, but my potato-brain hasn't thought of them.
Would you like to contribute code for this feature?
I'll collaborate on design + contribute review, but won't be able to champion it myself.