-
Notifications
You must be signed in to change notification settings - Fork 36
[Peras 6] Improve generation of Peras events in ChainDB state machine tests #1772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
6973759 to
e56667a
Compare
7ad8dca to
3420b36
Compare
tbagrel1
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
ouroboros-consensus/changelog.d/20251124_140035_agustin.mista_improve_chaindb_qsm_tests.md
Outdated
Show resolved
Hide resolved
nbacquey
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
| tabulate "TraceEvents" (map traceEventName trace) $ | ||
| res === Ok | ||
| .&&. prop_trace testCfg (dbModel model) trace | ||
| .&&. counterexample | ||
| "ImmutableDB is leaking file handles" | ||
| (Mock.numOpenHandles (nodeDBsImm fses) === 0) | ||
| .&&. counterexample | ||
| "VolatileDB is leaking file handles" | ||
| (Mock.numOpenHandles (nodeDBsVol fses) === 0) | ||
| .&&. counterexample | ||
| "LedgerDB is leaking file handles" | ||
| (Mock.numOpenHandles (nodeDBsLgr fses) === 0) | ||
| .&&. counterexample | ||
| "There were registered clean-up actions" | ||
| (remainingCleanups === 0) | ||
| tabulate "Security Parameter (k)" [show secParam] $ | ||
| tabulate "Chain length >= k" [show (Chain.length modelChain >= fromIntegral secParam)] $ | ||
| tabulate "TraceEvents" (map traceEventName trace) $ | ||
| res | ||
| === Ok | ||
| .&&. prop_trace testCfg (dbModel model) trace | ||
| .&&. counterexample | ||
| "ImmutableDB is leaking file handles" | ||
| (Mock.numOpenHandles (nodeDBsImm fses) === 0) | ||
| .&&. counterexample | ||
| "VolatileDB is leaking file handles" | ||
| (Mock.numOpenHandles (nodeDBsVol fses) === 0) | ||
| .&&. counterexample | ||
| "LedgerDB is leaking file handles" | ||
| (Mock.numOpenHandles (nodeDBsLgr fses) === 0) | ||
| .&&. counterexample | ||
| "There were registered clean-up actions" | ||
| (remainingCleanups === 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NOTE: this hunk only contains formatting changes.
The formatter in my editor is very annoyed by this block not being properly formatted, so I need to remember to manually bypass format-on-save every time I touch this file.
I would appreciate if we can merge this hunk, but I can also revert it back if nobody agrees 🥲
801c16e to
3523934
Compare
geo2a
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've asked for some clarifications on how the new generator state is used.
Also, does this PR actually depend on #1679 or could it target main directly? I think we would prefer to merge directly to main to keep things independent.
ouroboros-consensus/test/storage-test/Test/Ouroboros/Storage/ChainDB/StateMachine.hs
Outdated
Show resolved
Hide resolved
ouroboros-consensus/test/storage-test/Test/Ouroboros/Storage/ChainDB/StateMachine.hs
Outdated
Show resolved
Hide resolved
| -- under the hood. | ||
| data GenState blk | ||
| = GenState | ||
| { seenBlocks :: Map (HeaderHash blk) blk |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not understand where exactly these are used and what for. The only place I've found is in genBlk, where we check if the seen blocks are empty or not:
savedGapBlocks = seenBlocks genState
noSavedGapBlocks = Map.null savedGapBlocks
could you describe the use if this state in more detail? Preferably in its haddock in Model:
-- | Execution model
data Model blk m r = Model
{ dbModel :: DBModel blk
, knownIters :: KnownIters blk m r
, knownFollowers :: KnownFollowers blk m r
, modelConfig :: Opaque (TopLevelConfig blk)
, genState :: GenState blk
-- ^ TODO: briefly describe how it's used
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main idea here is to keep intermediate blocks generated by genBlk around, so that we no longer need to pray for them to be (re)generated from scratch in subsequent AddBlock and AddPerasCert invocations.
To illustrate, suppose the test has so far built the following chain:
A -> B -> C
Without this change, one of the options genBlk has is to generate a dangling block via genBlockAfterGap, by first building a random fork and then returning its tip. For instance, let's assume that genBlockAfterGap branches from B, and generates a block F, that fits on top of B via some (hypothetical) intermediate blocks D and E:
A -> B -> C
|
+ -> (D) -> (E) -> F
From here, the state machine runs AddBlock F, leaving the gap between B and F to be potentially filled by other random invocations to AddBlock later on.
Now, the problem with this approach is that generating blocks compatible with D and E to fill the gap created by F completely at random is very unlikely. And since genBlk can only do it by adding new blocks on top of existing ones, it's very likely that any potential fork created this way will diverge and never reach F, rendering F rather useless IMO.
With this commit, whenever we genBlockAfterGap, we also save the hypothetical gap blocks (D and E) for later in the model state, so that genBlk can sample from this set later on (see the new genGapBlock subgenerator). This allows the hypothetical fork created by genBlockAfterGap to be recreated without having to redo all the original hard work leading to F.
Note that in the future we could also be more clever and store collections of gap blocks indexed by their tip (e.g. { F :-> {D, E} }) instead of individual ones:
seenBlocks ::
Map
(HeaderHash blk) -- tip of the fork
( Map
(HeaderHash blk) -- gap blocks
blk
)(Using Map (HeaderHash blk) instead of Set just to satisfy Ord.) From there, we could first sample a fork, and then sample one of its gap blocks without replacement (i.e. removing the sampled value from the collection). This would avoid degrading the chances of filling gaps after a while by only remembering the gap blocks that haven't been sampled from yet.
Would adding something like this somewhere help justifying the changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see, genGapBlock was the missing piece for me to understand how the gap blocks were actually used. Thanks for the explanation @agustinmista! I think it would be great to have this comment incorporated into the test file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a similar explanation (that doesn't focus on the before vs. after but rather on why we want this) in a6d4a00
|
Thanks for the review @geo2a, I left a comment trying to explain the rationale a bit better. As for:
This PR makes some trivial changes to ouroboros-consensus/test/consensus-test/Test/Consensus/MiniProtocol/ObjectDiffusion/PerasCert/Smoke.hs, which doesn't exist in Up to you 😸 |
|
Thanks @agustinmista! If it's not too much work, please separate this PR into a standalone one targeting |
cf6d81f to
38fbb8c
Compare
38fbb8c to
1827392
Compare
68f5043 to
d5b1cea
Compare
dnadales
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. I left some comments where I think the code would benefit from additional explanations.
ouroboros-consensus/test/storage-test/Test/Ouroboros/Storage/ChainDB/StateMachine.hs
Show resolved
Hide resolved
ouroboros-consensus/test/storage-test/Test/Ouroboros/Storage/ChainDB/StateMachine.hs
Show resolved
Hide resolved
ouroboros-consensus/test/storage-test/Test/Ouroboros/Storage/ChainDB/StateMachine.hs
Show resolved
Hide resolved
e3cf626 to
cc3212c
Compare
Since the Peras boost per certificate will likely become a protocol parameter, we proactively avoid exposing the current hardcoded value, replacing it with an instantiation of the (currently trivial) PerasCfg record. In the special cases where it's interesting to vary the boost dynamically (ChainDB q-s-m), validated Peras certs now contain randomly generated boost weights. Co-authored-by: Agustin Mista <[email protected]> Co-authored-by: Alexander Esgen <[email protected]> Co-authored-by: Georgy Lukyanov <[email protected]> Co-authored-by: Thomas BAGREL <[email protected]> Co-authored-by: Nicolas BACQUEY <[email protected]>
This commit addresses #1745 by selectively disabling EBB generation when the security parameter (k) is larger than 2. This is needed to allow generating k>2 on the fly, which is necessary for testing that certain Peras-related events are properly handled by weight-based chain selection algorithm. Co-authored-by: Agustin Mista <[email protected]> Co-authored-by: Alexander Esgen <[email protected]> Co-authored-by: Georgy Lukyanov <[email protected]> Co-authored-by: Thomas BAGREL <[email protected]> Co-authored-by: Nicolas BACQUEY <[email protected]>
This commit increases the generation frequencies of both the
'genAddBlock' and 'genAddPerasCert' constructions to help producing
denser chains of blocks. This way, some of the events that were
harder to trigger (especially TagSwitchedToShorterChain) are much
more common now:
* Before:
Tags (5867 in total):
39.36% TagGetIsValidJust
29.76% TagGetIsValidNothing
28.50% TagChainSelReprocessKeptSelection
2.35% TagChainSelReprocessChangedSelection
0.03% TagSwitchedToShorterChain
* After:
Tags (5202 in total):
37.57% TagGetIsValidJust
28.24% TagGetIsValidNothing
27.25% TagChainSelReprocessKeptSelection
6.41% TagChainSelReprocessChangedSelection
0.53% TagSwitchedToShorterChain
Co-authored-by: Agustin Mista <[email protected]>
Co-authored-by: Alexander Esgen <[email protected]>
Co-authored-by: Georgy Lukyanov <[email protected]>
Co-authored-by: Thomas BAGREL <[email protected]>
Co-authored-by: Nicolas BACQUEY <[email protected]>
Extends the ChainDB model with generator state to support carrying gap
blocks in state machine tests. This increases the chances of generating
and adding (possibly out-of-order) branching sequences of blocks. This,
in turn increases the chances of observing the event where the
chain selection logic switches from a longer to a shorter (but heavier)
chain containing a boosted block.
Before:
Tags (5202 in total):
37.57% TagGetIsValidJust
28.24% TagGetIsValidNothing
27.25% TagChainSelReprocessKeptSelection
6.41% TagChainSelReprocessChangedSelection
0.53% TagSwitchedToShorterChain
After:
Tags (5176 in total):
39.10% TagGetIsValidJust
27.94% TagChainSelReprocessKeptSelection
26.49% TagGetIsValidNothing
5.37% TagChainSelReprocessChangedSelection
1.10% TagSwitchedToShorterChain
Co-authored-by: Agustin Mista <[email protected]>
Co-authored-by: Alexander Esgen <[email protected]>
Co-authored-by: Georgy Lukyanov <[email protected]>
Co-authored-by: Thomas BAGREL <[email protected]>
Co-authored-by: Nicolas BACQUEY <[email protected]>
After analysing the effect of varying the security parameter (`k`) of the ChainDB state machine tests (currently hardcoded with 2), we have observed a tension between: 1) generating enough tests exercising the new Peras behavior where the chain selection mechanism switches to a shorter but heavier chain (cert boost is derived from k and must be large enough to overcome the weight of a longer chain), and 2) generating enough tests exercising the ImmutableDB logic (the chain must have at least k blocks) Here are some empirical results: k -> P(switch to shorter chain), P(generate a chain with >= k blocks) k=2 -> ~1.3%, ~40% k=3 -> ~1.9%, ~20% k=4 -> ~2.4%, ~9% k=5 -> ~2.5%, ~3% k=10 -> ~3%, ~0.05% We believe that the sweet spot between both desiderata appears to be around `k=2` and `k=4`. This commit introduces a random generator for `k` using a geometric distribution to bias the randomly generated `k`s to be relatively small, while still allowing larger ones to appear from time to time. Under the current parameters, roughly 87.5% of the tests use `k<=4`; ``` Security Parameter (k) (10000 in total): 50.82% 2 23.83% 3 12.62% 4 6.69% 5 3.08% 6 1.54% 7 0.74% 8 0.37% 9 0.16% 10 0.06% 11 0.05% 12 0.02% 13 0.01% 14 0.01% 17 ``` Yielding the following distributions for 1) and 2), respectively: ``` Tags (5398 in total): 38.70% TagGetIsValidJust 29.23% TagChainSelReprocessKeptSelection 26.25% TagGetIsValidNothing 3.98% TagChainSelReprocessChangedSelection 1.83% TagSwitchedToShorterChain <- HERE ``` ``` Chain length >= k (10000 in total): 73.25% False 26.75% True <- HERE ``` Co-authored-by: Agustin Mista <[email protected]> Co-authored-by: Alexander Esgen <[email protected]> Co-authored-by: Georgy Lukyanov <[email protected]> Co-authored-by: Thomas BAGREL <[email protected]> Co-authored-by: Nicolas BACQUEY <[email protected]>
cc3212c to
ee92a0b
Compare
|
Rebased and reapplied the patch that depended on #1679. This should be ready to go on our end at soon as it passes CI 🥳 |
This PR introduces a sequence of changes to progressively improve the chances of observing possible-but-currently-uncommon Peras events in the ChainDB state machine tests. Concretely, we are interested in observing the chain selection algorithm switching from a longer to a shorter chain. For this, we rely on the following changes:
Cmdgeneration frequencies to produce moreAddBlockandAddPerasCertscommands.Cmdgenerator to "remember" previously generated blocks so that generated gaps can actually be filled sometimes (this previously relied entirely on randomness).p=0.5.Please check the commit messages to find the empirical effect of these changes between commits. In short, we improve the chances of observing
TagSwitchedToShorterChainfrom ~0.03% to about ~1.8%.Closes #1745