Skip to content

Commit 973586b

Browse files
committed
LDB V2: Fix leaked handle when not committing a forker
1 parent 833e922 commit 973586b

File tree

3 files changed

+44
-18
lines changed
  • ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/LedgerDB

3 files changed

+44
-18
lines changed

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

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@
1717
module Ouroboros.Consensus.Storage.LedgerDB.V2 (mkInitDb) where
1818

1919
import Control.Arrow ((>>>))
20-
import qualified Control.Monad as Monad (void, (>=>))
20+
import qualified Control.Monad as Monad (join, void)
2121
import Control.Monad.Except
2222
import Control.RAWLock
2323
import qualified Control.RAWLock as RAWLock
2424
import Control.ResourceRegistry
2525
import Control.Tracer
26-
import Data.Foldable (traverse_)
2726
import qualified Data.Foldable as Foldable
2827
import Data.Functor.Contravariant ((>$<))
2928
import Data.Kind (Type)
@@ -151,7 +150,8 @@ mkInitDb args bss getBlock snapManager =
151150
InMemoryHandleEnv -> InMemory.newInMemoryLedgerTablesHandle v2Tracer lgrHasFS
152151
LSMHandleEnv lsmRes ->
153152
\values -> do
154-
table <- LSM.tableFromValuesMK lgrRegistry (sessionResource lsmRes) (forgetLedgerTables st) values
153+
table <-
154+
LSM.tableFromValuesMK v2Tracer lgrRegistry (sessionResource lsmRes) (forgetLedgerTables st) values
155155
LSM.newLSMLedgerTablesHandle v2Tracer lgrRegistry table
156156

157157
loadSnapshot ::
@@ -562,13 +562,14 @@ getEnvSTM (LDBHandle varState) f =
562562
getStateRef ::
563563
(IOLike m, Traversable t) =>
564564
LedgerDBEnv m l blk ->
565+
ResourceRegistry m ->
565566
(LedgerSeq m l -> t (StateRef m l)) ->
566567
m (t (StateRef m l))
567-
getStateRef ldbEnv project =
568+
getStateRef ldbEnv reg project =
568569
RAWLock.withReadAccess (ldbOpenHandlesLock ldbEnv) $ \() -> do
569570
tst <- project <$> readTVarIO (ldbSeq ldbEnv)
570571
for tst $ \st -> do
571-
tables' <- duplicate $ tables st
572+
(_, tables') <- allocate reg (\_ -> duplicate $ tables st) close
572573
pure st{tables = tables'}
573574

574575
-- | Like 'StateRef', but takes care of closing the handle when the given action
@@ -579,8 +580,8 @@ withStateRef ::
579580
(LedgerSeq m l -> t (StateRef m l)) ->
580581
(t (StateRef m l) -> m a) ->
581582
m a
582-
withStateRef ldbEnv project =
583-
bracket (getStateRef ldbEnv project) (traverse_ (close . tables))
583+
withStateRef ldbEnv project f =
584+
withRegistry $ \reg -> getStateRef ldbEnv reg project >>= f
584585

585586
acquireAtTarget ::
586587
( HeaderHash l ~ HeaderHash blk
@@ -591,9 +592,10 @@ acquireAtTarget ::
591592
) =>
592593
LedgerDBEnv m l blk ->
593594
Either Word64 (Target (Point blk)) ->
595+
ResourceRegistry m ->
594596
m (Either GetForkerError (StateRef m l))
595-
acquireAtTarget ldbEnv target =
596-
getStateRef ldbEnv $ \l -> case target of
597+
acquireAtTarget ldbEnv target reg =
598+
getStateRef ldbEnv reg $ \l -> case target of
597599
Right VolatileTip -> pure $ currentHandle l
598600
Right ImmutableTip -> pure $ anchorHandle l
599601
Right (SpecificPoint pt) -> do
@@ -627,7 +629,7 @@ newForkerAtTarget ::
627629
Target (Point blk) ->
628630
m (Either GetForkerError (Forker m l blk))
629631
newForkerAtTarget h rr pt = getEnv h $ \ldbEnv ->
630-
acquireAtTarget ldbEnv (Right pt) >>= traverse (newForker h ldbEnv rr)
632+
acquireAtTarget ldbEnv (Right pt) rr >>= traverse (newForker h ldbEnv rr)
631633

632634
newForkerByRollback ::
633635
( HeaderHash l ~ HeaderHash blk
@@ -642,14 +644,14 @@ newForkerByRollback ::
642644
Word64 ->
643645
m (Either GetForkerError (Forker m l blk))
644646
newForkerByRollback h rr n = getEnv h $ \ldbEnv ->
645-
acquireAtTarget ldbEnv (Left n) >>= traverse (newForker h ldbEnv rr)
647+
acquireAtTarget ldbEnv (Left n) rr >>= traverse (newForker h ldbEnv rr)
646648

647649
closeForkerEnv ::
648650
IOLike m => ForkerEnv m l blk -> m ()
649651
closeForkerEnv ForkerEnv{foeResourcesToRelease = (lock, key, toRelease)} =
650652
RAWLock.withWriteAccess lock $
651653
const $ do
652-
id =<< atomically (swapTVar toRelease (pure ()))
654+
Monad.join $ atomically (swapTVar toRelease (pure ()))
653655
_ <- release key
654656
pure ((), ())
655657

@@ -746,7 +748,7 @@ newForker h ldbEnv rr st = do
746748
let tr = LedgerDBForkerEvent . TraceForkerEventWithKey forkerKey >$< ldbTracer ldbEnv
747749
traceWith tr ForkerOpen
748750
lseqVar <- newTVarIO . LedgerSeq . AS.Empty $ st
749-
(k, toRelease) <- allocate rr (\_ -> newTVarIO (pure ())) (readTVarIO Monad.>=> id)
751+
(k, toRelease) <- allocate rr (\_ -> newTVarIO (close (tables st))) (Monad.join . readTVarIO)
750752
let forkerEnv =
751753
ForkerEnv
752754
{ foeLedgerSeq = lseqVar

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,17 @@ implForkerCommit env = do
167167
_ AS.:< closeOld' -> closeLedgerSeq (LedgerSeq closeOld')
168168
-- Finally, close the anchor of @lseq@ (which is a duplicate of
169169
-- the head of @olddb'@).
170+
--
171+
-- Note if the resource registry used to create the Forker is
172+
-- ephemeral as it should (one created on each Chain selection
173+
-- or each Forging loop iteration), this first duplicated state
174+
-- will be closed by the resource registry closing down, so this
175+
-- will be a double release, which is fine. We prefer keeping
176+
-- this action just in case some client doesn't respect the
177+
-- condition of registries being ephemeral.
178+
--
179+
-- The rest of the states in the forker will be closed via
180+
-- @foeResourcesToRelease@ instead of via the registry.
170181
close $ tables $ AS.anchor lseq
171182
pure (closeDiscarded, s)
172183
)

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,19 +163,23 @@ instance LSM.SerialiseKey TxInBytes where
163163
tableFromValuesMK ::
164164
forall m l.
165165
(IOLike m, IndexedMemPack (l EmptyMK) (TxOut l), MemPack (TxIn l)) =>
166+
Tracer m V2.FlavorImplSpecificTrace ->
166167
ResourceRegistry m ->
167168
Session m ->
168169
l EmptyMK ->
169170
LedgerTables l ValuesMK ->
170171
m (ResourceKey m, UTxOTable m)
171-
tableFromValuesMK rr session st (LedgerTables (ValuesMK values)) = do
172+
tableFromValuesMK tracer rr session st (LedgerTables (ValuesMK values)) = do
172173
res@(_, table) <-
173174
allocate
174175
rr
175176
( \_ ->
176177
LSM.newTableWith (LSM.defaultTableConfig{LSM.confFencePointerIndex = LSM.OrdinaryIndex}) session
177178
)
178-
LSM.closeTable
179+
( \tb -> do
180+
traceWith tracer V2.TraceLedgerTablesHandleClose
181+
LSM.closeTable tb
182+
)
179183
mapM_ (go table) $ chunks 1000 $ Map.toList values
180184
pure res
181185
where
@@ -232,9 +236,15 @@ newLSMLedgerTablesHandle tracer rr (resKey, t) = do
232236
LedgerTablesHandle
233237
{ close = do
234238
Monad.void $ release resKey
235-
traceWith tracer V2.TraceLedgerTablesHandleClose
236239
, duplicate = do
237-
table <- allocate rr (\_ -> LSM.duplicate t) LSM.closeTable
240+
table <-
241+
allocate
242+
rr
243+
(\_ -> LSM.duplicate t)
244+
( \t' -> do
245+
traceWith tracer V2.TraceLedgerTablesHandleClose
246+
LSM.closeTable t'
247+
)
238248
newLSMLedgerTablesHandle tracer rr table
239249
, read = \st (LedgerTables (KeysMK keys)) -> do
240250
let vec' = V.create $ do
@@ -431,7 +441,10 @@ loadSnapshot tracer rr ccfg fs session ds = do
431441
(fromString $ snapshotToDirName ds)
432442
(LSM.SnapshotLabel $ Text.pack $ "UTxO table")
433443
)
434-
LSM.closeTable
444+
( \t -> do
445+
traceWith tracer V2.TraceLedgerTablesHandleClose
446+
LSM.closeTable t
447+
)
435448
Monad.when (checksumAsRead /= snapshotChecksum snapshotMeta) $
436449
throwE $
437450
InitFailureRead ReadSnapshotDataCorruption

0 commit comments

Comments
 (0)