Rework forker usage pattern to avoid Registries#1910
Conversation
| Complete VolatileDbArgs m blk -> | ||
| (forall st. WithTempRegistry st m (VolatileDB m blk, st) -> ans) -> | ||
| ans | ||
| openDB VolatileDbArgs{volHasFS = SomeHasFS hasFS, ..} cont = cont $ do |
There was a problem hiding this comment.
For reviewers: this change removes the need of runInnerWithTempRegistry.
| , HasCallStack | ||
| ) => | ||
| Complete ImmutableDbArgs m blk -> | ||
| (forall st. WithTempRegistry st m (ImmutableDB m blk, st) -> ans) -> |
There was a problem hiding this comment.
For reviewers: this change removes the need of runInnerWithTempRegistry.
| -- Currently, we have two distinct approaches to resource management | ||
| -- and database closure: | ||
| -- | ||
| -- - In the LedgerDB, closing the database does not close any resources | ||
| -- created by its clients. We rely on the resource registry to deallocate | ||
| -- these resources before the LedgerDB is closed. However, after closing | ||
| -- the LedgerDB, the only permitted action on these resources is to free them. | ||
| -- See 'ldbForkers'. | ||
| -- | ||
| -- - In the ChainDB, closing the database also closes all followers and | ||
| -- iterators. | ||
| -- | ||
| -- TODO: Ideally, the ChainDB and LedgerDB should follow a consistent | ||
| -- approach to resource deallocation. |
There was a problem hiding this comment.
For reviewers: This was not even the case before. But now it is irrelevant
There was a problem hiding this comment.
For reviewers: only change is that creating the resources is WithTempRegistry. The rest is just renaming and re-shaping the values to uniformize both init functions to return a StateRef.
There was a problem hiding this comment.
For reviewers: it only modifies the streaming backend functions
4ec580f to
e1181b3
Compare
ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Mempool/Impl/Common.hs
Outdated
Show resolved
Hide resolved
...boros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/ChainDB/Impl/ChainSel.hs
Outdated
Show resolved
Hide resolved
...boros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/ChainDB/Impl/ChainSel.hs
Outdated
Show resolved
Hide resolved
ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/ChainDB/Impl/Types.hs
Outdated
Show resolved
Hide resolved
ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/ChainDB/Impl/Query.hs
Show resolved
Hide resolved
ouroboros-consensus/src/ouroboros-consensus-lsm/Ouroboros/Consensus/Storage/LedgerDB/V2/LSM.hs
Outdated
Show resolved
Hide resolved
ouroboros-consensus/src/ouroboros-consensus-lsm/Ouroboros/Consensus/Storage/LedgerDB/V2/LSM.hs
Show resolved
Hide resolved
ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/LedgerDB/V2.hs
Outdated
Show resolved
Hide resolved
ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/LedgerDB/V1/Forker.hs
Outdated
Show resolved
Hide resolved
ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/LedgerDB/V1/Forker.hs
Outdated
Show resolved
Hide resolved
nfrisby
left a comment
There was a problem hiding this comment.
This Review is the result of my 3 hour pair review with Javier.
ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Mempool/Impl/Common.hs
Outdated
Show resolved
Hide resolved
ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Mempool/Impl/Common.hs
Show resolved
Hide resolved
| cse <- chainSelEnv | ||
| chainSelection cse rr (first Diff.extend <$> candidates) | ||
| fmap (getSuffix . fst) | ||
| <$> chainSelection cse (first Diff.extend <$> candidates) (\_ _ -> join . atomically . forkerCommit) |
There was a problem hiding this comment.
ChainSel is now accepting a success continuation (ie better chain continuation) because it has a bracket in it for the Forker now.
Maybe it's name should include with* or brackedted* etc.
| atomically $ forkerCommit (getLedger newChain) | ||
| forkerClose (getLedger newChain) | ||
| pure $ getSuffix $ getChainDiff newChain | ||
| fromMaybe curChain <$> chainSelection' curChain chains' |
There was a problem hiding this comment.
Primary changes:
- ChainSel now never explicitly closes a Forker; every ChainSel forker is opened via
bracket. - Therefore, the ChainSel success continuation is now passed as an argument.
| -- ^ Get a 'HeaderStateHistory' populated with the 'HeaderState's of the | ||
| -- last @k@ blocks of the current chain. | ||
| , getReadOnlyForkerAtPoint :: | ||
| , allocInRegistryReadOnlyForkerAtPoint :: |
There was a problem hiding this comment.
Since ChainSel is a thread we've been focused on in the past regarding this Forker tracking stuff, I'll explicitly note that ChainSel doesn't use either of these.
(ChainSel relies on validateForker, which directly uses the LedgerDB API to open a read-write Forker.)
| lift $ traceWith tracer NoValidLastLocation | ||
| mkOpenState hasFS index chunk Origin MustBeNew | ||
| traceWith tracer NoValidLastLocation | ||
| -- It is OK to disable the temp registry at this point because this |
There was a problem hiding this comment.
Notes from our call:
- At the very least, "disable" is the wrong word. You mean "execute" maybe?
- But there's also the broader topic we discussed of: if
mkOpenStatefails, then the node is shutting down, so we don't need to bother using a registry to enforcehCloseis called. But that's probably for a follow-up PR.
There was a problem hiding this comment.
A follow-up point: the claim above would not be true if any resources in the temporary registry had a clean-up function that was more interesting than hClose. But we're pretty sure they're all just hClose.
| volTracer | ||
| volMaxBlocksPerFile | ||
| stVar <- lift $ RAWLock.new (DbOpen ost) | ||
| -- It is OK to disable the temp registry at this point because this |
There was a problem hiding this comment.
Same probably applies as my similar GitHub Comment on ImmutableDB.openDB
| -- Therefore, the 'BackingStore' is not allocated in any resource or tracked | ||
| -- in any way as a resource. | ||
| -- | ||
| -- For the value handles, all the usages of those are bracketed or tracked in |
There was a problem hiding this comment.
In the call, Javier just explained a key distinction very nicely. It should probably be in this comment.
The LedgerDB retains one LSM handle per block on the volatile suffix. That's the whole idea behind "immutable" databases.
That's not true for LMDB, since in the V1 interface the on-disk state of the database only ever corresponds to whichever immutable block's changelog was flushed most recently.
And a note to self about LMDB/V1:
An LMDB V1 forker (even a read-write Forker!) only retains an open read-only LMDB transaction (which it opens lazily, the first time it handles a query that depends on the UTxO, b/c there's a limit on how many LMDB txs can be open simultaneously). LSQ is the only thing that would hold such a Forker for a long time; Mempool and ChainSel both have one too, but they are updated frequently and/or ephemeral.
| ResolveBlock m blk -> | ||
| GetVolatileSuffix m blk -> | ||
| m (LedgerDB' m blk, Word64) | ||
| WithTempRegistry st m (LedgerDB' m blk, Word64) |
There was a problem hiding this comment.
Note from call:
It's crucial that this is scoped within the same
runWithTempRegistrycall that includes the allocation of the ChainDB itself into the top-level resource registry. That's why the chain ofopen*functions are inWithTempRegistryeven though there's just the one part of them that actually puts stuff in that registry.
ouroboros-consensus/test/storage-test/Test/Ouroboros/Storage/LedgerDB/StateMachine.hs
Outdated
Show resolved
Hide resolved
1803f26 to
fdfe814
Compare
0a895bb to
d9ffae7
Compare
d9ffae7 to
922925e
Compare
Description
Forkers have been a source of headaches for a very long time already, and all due to the limitation of the Resource registry that enforces registries to only be used from known threads. This has lead to a situation where we had hierarchies of registries just to satisfy that rule and even we had to extend the resource-registry API to allow transferring between registries which is unsafe.
The general pattern followed now is:
with*combinator style, so the handles are deallocated when exiting the scope (or committed).Sadly, the change is so fundamental that there is no meaningful way of splitting the changes into reviewable commits that compile on their own.
Proposed review order (recommended to disable whitespace-diff in Github diff view and hide already viewed files):
The rest of the changes are just adapting tests or adapting the cardano-tools to use the new access pattern. I did not do any meaningful change in the tests by my will, I only followed GHC errors where they took me.