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
4143import Control.Monad.Except
4244import Control.Monad.State hiding (state )
4345import Control.ResourceRegistry
44- import Control.Tracer (nullTracer )
46+ import Control.Tracer (Tracer ( .. ) )
4547import qualified Data.List as L
4648import Data.Map.Strict (Map )
4749import qualified Data.Map.Strict as Map
@@ -55,6 +57,7 @@ import Ouroboros.Consensus.Ledger.Tables.Utils
5557import qualified Ouroboros.Consensus.Storage.ChainDB.Impl.BlockCache as BlockCache
5658import Ouroboros.Consensus.Storage.ImmutableDB.Stream
5759import Ouroboros.Consensus.Storage.LedgerDB
60+ import qualified Ouroboros.Consensus.Storage.LedgerDB as LedgerDB
5861import Ouroboros.Consensus.Storage.LedgerDB.Snapshots
5962import Ouroboros.Consensus.Storage.LedgerDB.V1 as V1
6063import Ouroboros.Consensus.Storage.LedgerDB.V1.Args hiding
@@ -64,6 +67,7 @@ import Ouroboros.Consensus.Storage.LedgerDB.V2 as V2
6467import Ouroboros.Consensus.Storage.LedgerDB.V2.Args hiding
6568 ( LedgerDbFlavorArgs
6669 )
70+ import qualified Ouroboros.Consensus.Storage.LedgerDB.V2.Args as V2
6771import Ouroboros.Consensus.Util hiding (Some )
6872import Ouroboros.Consensus.Util.Args
6973import Ouroboros.Consensus.Util.IOLike
@@ -111,7 +115,8 @@ prop_sequential ::
111115prop_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 )
466472openLedgerDB 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
520528instance 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