Skip to content

Commit 8da1177

Browse files
committed
LedgerDB.V2: make sure to actually close handles
1 parent 2c06471 commit 8da1177

File tree

4 files changed

+67
-33
lines changed

4 files changed

+67
-33
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
### Patch
2+
3+
- Changed the V2 LedgerDB `LedgerTablesHandle`s to actually be closed in all
4+
cases. With the current (only) backend (in-memory), this doesn't matter, but
5+
on-disk backends (like LSM trees) need this.

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/LedgerDB/V2.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,12 @@ mkInitDb args flavArgs getBlock =
8585
, closeDb = closeLedgerSeq
8686
, initReapplyBlock = \a b c -> do
8787
(x, y) <- reapplyThenPush lgrRegistry a b c
88-
closeLedgerSeq x
88+
x
8989
pure y
9090
, currentTip = ledgerState . current
9191
, pruneDb = \lseq -> do
92-
let (LedgerSeq rel, dbPrunedToImmDBTip) = pruneToImmTipOnly lseq
93-
mapM_ (close . tables) (AS.toOldestFirst rel)
92+
let (rel, dbPrunedToImmDBTip) = pruneToImmTipOnly lseq
93+
rel
9494
pure dbPrunedToImmDBTip
9595
, mkLedgerDb = \lseq -> do
9696
varDB <- newTVarIO lseq

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/LedgerDB/V2/Forker.hs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ implForkerCommit env = do
143143
LedgerSeq lseq <- readTVar foeLedgerSeq
144144
let intersectionSlot = getTipSlot $ state $ AS.anchor lseq
145145
let predicate = (== getTipHash (state (AS.anchor lseq))) . getTipHash . state
146-
(discardedBySelection, LedgerSeq discardedByPruning) <- do
146+
closeDiscarded <- do
147147
stateTVar
148148
foeSwitchVar
149149
( \(LedgerSeq olddb) -> fromMaybe theImpossible $ do
@@ -153,17 +153,25 @@ implForkerCommit env = do
153153
-- Join the prefix of the selection with the sequence in the forker
154154
newdb <- AS.join (const $ const True) olddb' lseq
155155
-- Prune the resulting sequence to keep @k@ states
156-
let (l, s) = prune (LedgerDbPruneKeeping (foeSecurityParam env)) (LedgerSeq newdb)
157-
pure ((toClose, l), s)
156+
let (closePruned, s) = prune (LedgerDbPruneKeeping (foeSecurityParam env)) (LedgerSeq newdb)
157+
closeDiscarded = do
158+
closePruned
159+
-- Do /not/ close the anchor of @toClose@, as that is also the
160+
-- tip of @olddb'@ which will be used in @newdb@.
161+
case toClose of
162+
AS.Empty _ -> pure ()
163+
_ AS.:< closeOld' -> closeLedgerSeq (LedgerSeq closeOld')
164+
-- Finally, close the anchor of @lseq@ (which is a duplicate of
165+
-- the head of @olddb'@).
166+
close $ tables $ AS.anchor lseq
167+
pure (closeDiscarded, s)
158168
)
159169

160170
-- We are discarding the previous value in the TVar because we had accumulated
161171
-- actions for closing the states pushed to the forker. As we are committing
162172
-- those we have to close the ones discarded in this function and forget about
163173
-- those releasing actions.
164-
writeTVar foeResourcesToRelease $
165-
mapM_ (close . tables) $
166-
AS.toOldestFirst discardedBySelection ++ AS.toOldestFirst discardedByPruning
174+
writeTVar foeResourcesToRelease closeDiscarded
167175
where
168176
ForkerEnv
169177
{ foeLedgerSeq

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/LedgerDB/V2/LedgerSeq.hs

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ module Ouroboros.Consensus.Storage.LedgerDB.V2.LedgerSeq
5454

5555
import Cardano.Ledger.BaseTypes
5656
import Control.ResourceRegistry
57-
import qualified Data.Bifunctor as B
5857
import Data.Function (on)
5958
import Data.Word
6059
import GHC.Generics
@@ -183,8 +182,11 @@ empty' ::
183182
m (LedgerSeq m l)
184183
empty' st = empty (forgetLedgerTables st) (ltprj st)
185184

185+
-- | Close all 'LedgerTablesHandle' in this 'LedgerSeq', in particular that on
186+
-- the anchor.
186187
closeLedgerSeq :: Monad m => LedgerSeq m l -> m ()
187-
closeLedgerSeq = mapM_ (close . tables) . toOldestFirst . getLedgerSeq
188+
closeLedgerSeq (LedgerSeq l) =
189+
mapM_ (close . tables) $ AS.anchor l : AS.toOldestFirst l
188190

189191
{-------------------------------------------------------------------------------
190192
Apply blocks
@@ -193,15 +195,14 @@ closeLedgerSeq = mapM_ (close . tables) . toOldestFirst . getLedgerSeq
193195
-- | Apply a block on top of the ledger state and extend the LedgerSeq with
194196
-- the result ledger state.
195197
--
196-
-- The @fst@ component of the result should be closed as it contains the pruned
197-
-- states.
198+
-- The @fst@ component of the result should be run to close the pruned states.
198199
reapplyThenPush ::
199200
(IOLike m, ApplyBlock l blk) =>
200201
ResourceRegistry m ->
201202
LedgerDbCfg l ->
202203
blk ->
203204
LedgerSeq m l ->
204-
m (LedgerSeq m l, LedgerSeq m l)
205+
m (m (), LedgerSeq m l)
205206
reapplyThenPush rr cfg ap db =
206207
(\current' -> prune (LedgerDbPruneKeeping (ledgerDbCfgSecParam cfg)) $ extend current' db)
207208
<$> reapplyBlock (ledgerDbCfgComputeLedgerEvents cfg) (ledgerDbCfg cfg) ap rr db
@@ -229,31 +230,22 @@ reapplyBlock evs cfg b _rr db = do
229230
-- | Prune older ledger states until at we have at most @k@ volatile states in
230231
-- the LedgerDB, plus the one stored at the anchor.
231232
--
232-
-- The @fst@ component of the returned value has to be @close@ed.
233+
-- The @fst@ component of the returned value is an action closing the pruned
234+
-- ledger states.
233235
--
234236
-- >>> ldb = LedgerSeq $ AS.fromOldestFirst l0 [l1, l2, l3]
235237
-- >>> ldb' = LedgerSeq $ AS.fromOldestFirst l1 [l2, l3]
236238
-- >>> snd (prune (LedgerDbPruneKeeping (SecurityParam (unsafeNonZero 2))) ldb) == ldb'
237239
-- True
238240
prune ::
239-
GetTip l =>
241+
(Monad m, GetTip l) =>
240242
LedgerDbPrune ->
241243
LedgerSeq m l ->
242-
(LedgerSeq m l, LedgerSeq m l)
243-
prune (LedgerDbPruneKeeping (SecurityParam k)) (LedgerSeq ldb) =
244-
if toEnum nvol <= unNonZero k
245-
then (LedgerSeq $ Empty (AS.anchor ldb), LedgerSeq ldb)
246-
else
247-
-- We remove the new anchor from the @fst@ component so that its handle is
248-
-- not closed.
249-
B.bimap (LedgerSeq . dropNewest 1) LedgerSeq $ AS.splitAt (nvol - fromEnum (unNonZero k)) ldb
250-
where
251-
nvol = AS.length ldb
252-
prune LedgerDbPruneAll (LedgerSeq ldb) =
253-
B.bimap (LedgerSeq . dropNewest 1) LedgerSeq $ AS.splitAt nvol ldb
254-
where
255-
nvol = AS.length ldb
256-
244+
(m (), LedgerSeq m l)
245+
prune (LedgerDbPruneKeeping (SecurityParam k)) =
246+
pruneAt (fromIntegral (unNonZero k))
247+
prune LedgerDbPruneAll =
248+
pruneAt 0
257249
-- NOTE: we must inline 'prune' otherwise we get unexplained thunks in
258250
-- 'LedgerSeq' and thus a space leak. Alternatively, we could disable the
259251
-- @-fstrictness@ optimisation (enabled by default for -O1). See
@@ -263,6 +255,35 @@ prune LedgerDbPruneAll (LedgerSeq ldb) =
263255
-- needed anymore.
264256
{-# INLINE prune #-}
265257

258+
pruneAt ::
259+
(Monad m, GetTip l) =>
260+
-- | New maximum length of the 'LedgerSeq'.
261+
Int ->
262+
LedgerSeq m l ->
263+
(m (), LedgerSeq m l)
264+
pruneAt i (LedgerSeq ldb)
265+
| nvol <= i = (pure (), LedgerSeq ldb)
266+
| otherwise = (closePruned, LedgerSeq after)
267+
where
268+
(toPrune, after) = AS.splitAt (nvol - i) ldb
269+
270+
-- By construction (see the docs of 'AS.splitAt'), @AS.anchor after ==
271+
-- AS.headAnchor toPrune@. Therefore, we must not close the handle
272+
-- corresponding to the head of @toPrune@.
273+
closePruned = case toPrune of
274+
-- If @toPrune@ is empty, then we have nothing to prune (in particular /not/
275+
-- the anchor).
276+
AS.Empty _ -> pure ()
277+
-- Otherwise, the head of @toPrune@ is the anchor @after@, which we don't
278+
-- want to close. All other handles in @toPrune@ (in particular its anchor)
279+
-- are to be closed.
280+
toPrune' AS.:> _ -> closeLedgerSeq (LedgerSeq toPrune')
281+
282+
nvol = AS.length ldb
283+
284+
-- Cargo-culted from 'prune' above.
285+
{-# INLINE pruneAt #-}
286+
266287
-- | Extending the LedgerDB with a valid ledger state.
267288
--
268289
-- >>> ldb = LedgerSeq $ AS.fromOldestFirst l0 [l1, l2, l3]
@@ -296,9 +317,9 @@ extend newState =
296317
-- >>> AS.anchor ldb' == l3 && AS.toOldestFirst ldb' == []
297318
-- True
298319
pruneToImmTipOnly ::
299-
GetTip l =>
320+
(Monad m, GetTip l) =>
300321
LedgerSeq m l ->
301-
(LedgerSeq m l, LedgerSeq m l)
322+
(m (), LedgerSeq m l)
302323
pruneToImmTipOnly = prune LedgerDbPruneAll
303324

304325
{-------------------------------------------------------------------------------

0 commit comments

Comments
 (0)