Skip to content

Commit 1886094

Browse files
committed
LedgerDB.StateMachine: test that we do not leak handles
1 parent aa271ff commit 1886094

File tree

1 file changed

+48
-14
lines changed
  • ouroboros-consensus/test/storage-test/Test/Ouroboros/Storage/LedgerDB

1 file changed

+48
-14
lines changed

ouroboros-consensus/test/storage-test/Test/Ouroboros/Storage/LedgerDB/StateMachine.hs

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
{-# LANGUAGE FlexibleContexts #-}
88
{-# LANGUAGE FlexibleInstances #-}
99
{-# LANGUAGE GADTs #-}
10+
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1011
{-# LANGUAGE InstanceSigs #-}
12+
{-# LANGUAGE LambdaCase #-}
1113
{-# LANGUAGE MultiParamTypeClasses #-}
1214
{-# LANGUAGE NamedFieldPuns #-}
1315
{-# LANGUAGE PackageImports #-}
@@ -41,7 +43,7 @@ import qualified Control.Monad as Monad
4143
import Control.Monad.Except
4244
import Control.Monad.State hiding (state)
4345
import Control.ResourceRegistry
44-
import Control.Tracer (nullTracer)
46+
import Control.Tracer (Tracer (..))
4547
import qualified Data.List as L
4648
import Data.Map.Strict (Map)
4749
import qualified Data.Map.Strict as Map
@@ -55,6 +57,7 @@ import Ouroboros.Consensus.Ledger.Tables.Utils
5557
import qualified Ouroboros.Consensus.Storage.ChainDB.Impl.BlockCache as BlockCache
5658
import Ouroboros.Consensus.Storage.ImmutableDB.Stream
5759
import Ouroboros.Consensus.Storage.LedgerDB
60+
import qualified Ouroboros.Consensus.Storage.LedgerDB as LedgerDB
5861
import Ouroboros.Consensus.Storage.LedgerDB.Snapshots
5962
import Ouroboros.Consensus.Storage.LedgerDB.V1 as V1
6063
import Ouroboros.Consensus.Storage.LedgerDB.V1.Args hiding
@@ -64,6 +67,7 @@ import Ouroboros.Consensus.Storage.LedgerDB.V2 as V2
6467
import Ouroboros.Consensus.Storage.LedgerDB.V2.Args hiding
6568
( LedgerDbFlavorArgs
6669
)
70+
import qualified Ouroboros.Consensus.Storage.LedgerDB.V2.Args as V2
6771
import Ouroboros.Consensus.Util hiding (Some)
6872
import Ouroboros.Consensus.Util.Args
6973
import Ouroboros.Consensus.Util.IOLike
@@ -111,7 +115,8 @@ prop_sequential ::
111115
prop_sequential maxSuccess mkTestArguments getLmdbDir fsOps as = QC.withMaxSuccess maxSuccess $
112116
QC.monadicIO $ do
113117
ref <- lift $ initialEnvironment fsOps getLmdbDir mkTestArguments =<< initChainDB
114-
(_, Environment _ testInternals _ _ _ clean) <- runPropertyStateT (runActions as) ref
118+
(_, env@(Environment _ testInternals _ _ _ _ clean)) <- runPropertyStateT (runActions as) ref
119+
checkNoLeakedHandles env
115120
QC.run $ closeLedgerDB testInternals >> clean
116121
QC.assert True
117122

@@ -136,6 +141,7 @@ initialEnvironment fsOps getLmdbDir mkTestArguments cdb = do
136141
cdb
137142
(flip mkTestArguments lmdbDir)
138143
sfs
144+
(pure $ NumOpenHandles 0)
139145
(cleanupFS >> cleanupLMDB)
140146

141147
{-------------------------------------------------------------------------------
@@ -462,19 +468,20 @@ openLedgerDB ::
462468
ChainDB IO ->
463469
LedgerDbCfg (ExtLedgerState TestBlock) ->
464470
SomeHasFS IO ->
465-
IO (LedgerDB' IO TestBlock, TestInternals' IO TestBlock)
471+
IO (LedgerDB' IO TestBlock, TestInternals' IO TestBlock, IO NumOpenHandles)
466472
openLedgerDB flavArgs env cfg fs = do
467473
(stream, volBlocks) <- dbStreamAPI (ledgerDbCfgSecParam cfg) env
468474
let getBlock f = Map.findWithDefault (error blockNotFound) f <$> readTVarIO (dbBlocks env)
469475
replayGoal <- fmap (realPointToPoint . last . Map.keys) . atomically $ readTVar (dbBlocks env)
470476
rr <- unsafeNewRegistry
477+
(tracer, getNumOpenHandles) <- mkTrackOpenHandles
471478
let args =
472479
LedgerDbArgs
473480
(SnapshotPolicyArgs DisableSnapshots DefaultNumOfDiskSnapshots)
474481
(pure genesis)
475482
fs
476483
cfg
477-
nullTracer
484+
tracer
478485
flavArgs
479486
rr
480487
DefaultQueryBatchSize
@@ -501,7 +508,7 @@ openLedgerDB flavArgs env cfg fs = do
501508
atomically (forkerCommit forker)
502509
forkerClose forker
503510
_ -> error "Couldn't restart the chain, failed to apply volatile blocks!"
504-
pure (ldb, od)
511+
pure (ldb, od, getNumOpenHandles)
505512

506513
{-------------------------------------------------------------------------------
507514
RunModel
@@ -515,26 +522,27 @@ data Environment
515522
(ChainDB IO)
516523
(SecurityParam -> TestArguments IO)
517524
(SomeHasFS IO)
525+
(IO NumOpenHandles)
518526
(IO ())
519527

520528
instance RunModel Model (StateT Environment IO) where
521529
perform _ (Init secParam) _ = do
522-
Environment _ _ chainDb mkArgs fs cleanup <- get
523-
(ldb, testInternals) <- lift $ do
530+
Environment _ _ chainDb mkArgs fs _ cleanup <- get
531+
(ldb, testInternals, getNumOpenHandles) <- lift $ do
524532
let args = mkArgs secParam
525533
openLedgerDB (argFlavorArgs args) chainDb (argLedgerDbCfg args) fs
526-
put (Environment ldb testInternals chainDb mkArgs fs cleanup)
534+
put (Environment ldb testInternals chainDb mkArgs fs getNumOpenHandles cleanup)
527535
perform _ WipeLedgerDB _ = do
528-
Environment _ testInternals _ _ _ _ <- get
536+
Environment _ testInternals _ _ _ _ _ <- get
529537
lift $ wipeLedgerDB testInternals
530538
perform _ GetState _ = do
531-
Environment ldb _ _ _ _ _ <- get
539+
Environment ldb _ _ _ _ _ _ <- get
532540
lift $ atomically $ (,) <$> getImmutableTip ldb <*> getVolatileTip ldb
533541
perform _ ForceTakeSnapshot _ = do
534-
Environment _ testInternals _ _ _ _ <- get
542+
Environment _ testInternals _ _ _ _ _ <- get
535543
lift $ takeSnapshotNOW testInternals TakeAtImmutableTip Nothing
536544
perform _ (ValidateAndCommit n blks) _ = do
537-
Environment ldb _ chainDb _ _ _ <- get
545+
Environment ldb _ chainDb _ _ _ _ <- get
538546
lift $ do
539547
atomically $
540548
modifyTVar (dbBlocks chainDb) $
@@ -549,13 +557,13 @@ instance RunModel Model (StateT Environment IO) where
549557
ValidateExceededRollBack{} -> error "Unexpected Rollback"
550558
ValidateLedgerError (AnnLedgerError forker _ _) -> forkerClose forker >> error "Unexpected ledger error"
551559
perform state@(Model _ secParam) (DropAndRestore n) lk = do
552-
Environment _ testInternals chainDb _ _ _ <- get
560+
Environment _ testInternals chainDb _ _ _ _ <- get
553561
lift $ do
554562
atomically $ modifyTVar (dbChain chainDb) (drop (fromIntegral n))
555563
closeLedgerDB testInternals
556564
perform state (Init secParam) lk
557565
perform _ TruncateSnapshots _ = do
558-
Environment _ testInternals _ _ _ _ <- get
566+
Environment _ testInternals _ _ _ _ _ <- get
559567
lift $ truncateSnapshots testInternals
560568
perform UnInit _ _ = error "Uninitialized model created a command different than Init"
561569

@@ -586,3 +594,29 @@ instance RunModel Model (StateT Environment IO) where
586594
]
587595
pure $ volSt == vol && immSt == imm
588596
postcondition _ _ _ _ = pure True
597+
598+
{-------------------------------------------------------------------------------
599+
Additional checks
600+
-------------------------------------------------------------------------------}
601+
602+
newtype NumOpenHandles = NumOpenHandles Word64
603+
deriving newtype (Show, Eq, Enum)
604+
605+
mkTrackOpenHandles :: IO (Tracer IO (TraceEvent TestBlock), IO NumOpenHandles)
606+
mkTrackOpenHandles = do
607+
varOpen <- uncheckedNewTVarM (NumOpenHandles 0)
608+
let tracer = Tracer $ \case
609+
LedgerDBFlavorImplEvent (FlavorImplSpecificTraceV2 ev) ->
610+
atomically $ modifyTVar varOpen $ case ev of
611+
V2.TraceLedgerTablesHandleCreate -> succ
612+
V2.TraceLedgerTablesHandleClose -> pred
613+
_ -> pure ()
614+
pure (tracer, readTVarIO varOpen)
615+
616+
-- | Check that we didn't leak any 'LedgerTablesHandle's (with V2 only).
617+
checkNoLeakedHandles :: Environment -> QC.PropertyM IO ()
618+
checkNoLeakedHandles (Environment _ testInternals _ _ _ getNumOpenHandles _) = do
619+
expected <- liftIO $ NumOpenHandles <$> LedgerDB.getNumLedgerTablesHandles testInternals
620+
actual <- liftIO getNumOpenHandles
621+
QC.assertWith (actual == expected) $
622+
"leaked handles, expected " <> show expected <> ", but actual " <> show actual

0 commit comments

Comments
 (0)