@@ -25,7 +25,6 @@ import Control.Monad.Except
2525import  Control.Monad.Trans  (lift )
2626import  Control.ResourceRegistry 
2727import  Control.Tracer 
28- import  Data.Bifunctor  (first )
2928import  qualified  Data.Foldable  as  Foldable 
3029import  Data.Functor.Contravariant  ((>$<) )
3130import  Data.Kind  (Type )
@@ -83,24 +82,29 @@ mkInitDb ::
8382  Complete  LedgerDbArgs  m  blk  -> 
8483  Complete  V1. LedgerDbFlavorArgs  m  -> 
8584  ResolveBlock  m  blk  -> 
86-   InitDB  (DbChangelog'  blk , BackingStore'  m  blk ) m  blk 
85+   InitDB  (DbChangelog'  blk , ResourceKey   m ,  BackingStore'  m  blk ) m  blk 
8786mkInitDb args bss getBlock = 
8887  InitDB 
8988    { initFromGenesis =  do 
9089        st <-  lgrGenesis
9190        let  genesis =  forgetLedgerTables st
9291            chlog =  DbCh. empty genesis
93-         (_ , backingStore) <- 
92+         (bsKey , backingStore) <- 
9493          allocate
9594            lgrRegistry
9695            (\ _ ->  newBackingStore bsTracer baArgs lgrHasFS' genesis (projectLedgerTables st))
9796            bsClose
98-         pure  (chlog, backingStore)
97+         pure  (chlog, bsKey,  backingStore)
9998    , initFromSnapshot = 
10099        runExceptT
101-           .  loadSnapshot bsTracer baArgs (configCodec .  getExtLedgerCfg .  ledgerDbCfg $  lgrConfig) lgrHasFS'
102-     , closeDb =  bsClose .  snd 
103-     , initReapplyBlock =  \ cfg blk (chlog, bstore) ->  do 
100+           .  loadSnapshot
101+             bsTracer
102+             baArgs
103+             (configCodec .  getExtLedgerCfg .  ledgerDbCfg $  lgrConfig)
104+             lgrHasFS'
105+             lgrRegistry
106+     , closeDb =  \ (_, r, _) ->  void $  release r
107+     , initReapplyBlock =  \ cfg blk (chlog, r, bstore) ->  do 
104108        ! chlog' <-  reapplyThenPush cfg blk (readKeySets bstore) chlog
105109        --  It's OK to flush without a lock here, since the `LedgerDB` has not
106110        --  finished initializing, only this thread has access to the backing
@@ -113,10 +117,10 @@ mkInitDb args bss getBlock =
113117                mapM_  (flushIntoBackingStore bstore) toFlush
114118                pure  toKeep
115119              else  pure  chlog'
116-         pure  (chlog'', bstore)
117-     , currentTip =  ledgerState .  current .   fst 
118-     , pruneDb =  pure   .  first  pruneToImmTipOnly
119-     , mkLedgerDb =  \ (db, lgrBackingStore ) ->  do 
120+         pure  (chlog'', r,  bstore)
121+     , currentTip =  \ (ch, _, _)  ->   ledgerState .  current $  ch 
122+     , pruneDb =  \ (ch, r, bs)  ->   pure  ( pruneToImmTipOnly ch, r, bs) 
123+     , mkLedgerDb =  \ (db, ldbBackingStoreKey, ldbBackingStore ) ->  do 
120124        (varDB, prevApplied) <- 
121125          (,) <$>  newTVarIO db <*>  newTVarIO Set. empty
122126        flushLock <-  mkLedgerDBLock
@@ -125,7 +129,8 @@ mkInitDb args bss getBlock =
125129        let  env = 
126130              LedgerDBEnv 
127131                { ldbChangelog =  varDB
128-                 , ldbBackingStore =  lgrBackingStore
132+                 , ldbBackingStore =  ldbBackingStore
133+                 , ldbBackingStoreKey =  ldbBackingStoreKey
129134                , ldbLock =  flushLock
130135                , ldbPrevApplied =  prevApplied
131136                , ldbForkers =  forkers
@@ -329,13 +334,14 @@ implCloseDB (LDBHandle varState) = do
329334        --  Idempotent
330335        LedgerDBClosed  ->  return  Nothing 
331336        LedgerDBOpen  env ->  do 
337+           --  By writing this tvar, we already make sure that no
338+           --  forkers can perform operations other than closing, as
339+           --  they rely on accessing the LedgerDB, which is now closed.
332340          writeTVar varState LedgerDBClosed 
333341          return  $  Just  env
334342
335343  --  Only when the LedgerDB was open
336-   whenJust mbOpenEnv $  \ env ->  do 
337-     closeAllForkers env
338-     bsClose (ldbBackingStore env)
344+   whenJust mbOpenEnv $  void .  release .  ldbBackingStoreKey
339345
340346mkInternals  :: 
341347  ( IOLike  m 
@@ -351,7 +357,7 @@ mkInternals h =
351357    , push =  getEnv1 h implIntPush
352358    , reapplyThenPushNOW =  getEnv1 h implIntReapplyThenPush
353359    , wipeLedgerDB =  getEnv h $  void .  destroySnapshots .  snapshotsFs .  ldbHasFS
354-     , closeLedgerDB =  getEnv h $  bsClose  .  ldbBackingStore 
360+     , closeLedgerDB =  getEnv h $  void  .  release  .  ldbBackingStoreKey 
355361    , truncateSnapshots =  getEnv h $  void .  implIntTruncateSnapshots .  ldbHasFS
356362    , getNumLedgerTablesHandles =  pure  0 
357363    }
@@ -482,6 +488,10 @@ data LedgerDBEnv m l blk = LedgerDBEnv
482488  , ldbBackingStore  ::  ! (LedgerBackingStore  m  l )
483489  --  ^  Handle to the ledger's backing store, containing the parts that grow too 
484490  --  big for in-memory residency
491+   , ldbBackingStoreKey  ::  ! (ResourceKey  m )
492+   --  ^  When deallocating the backing store upon closing the LedgerDB 
493+   --  (via the ChainDB shutting down), we will release the backing
494+   --  store with this action.
485495  , ldbLock  ::  ! (LedgerDBLock  m )
486496  --  ^  The flush lock to the 'BackingStore'. This lock is crucial when it 
487497  --  comes to keeping the data in memory consistent with the data on-disk.
@@ -512,6 +522,18 @@ data LedgerDBEnv m l blk = LedgerDBEnv
512522  --  ^  Open forkers. 
513523  -- 
514524  --  INVARIANT: a forker is open iff its 'ForkerKey' is in this 'Map.
525+   -- 
526+   --  The resources that could possibly be held by these forkers will
527+   --  be released by each one of the client's registries. This means
528+   --  that for example ChainSelection will, upon closing its registry,
529+   --  release its forker and any resources associated.
530+   -- 
531+   --  Upon closing the LedgerDB we will overwrite this variable such
532+   --  that existing forkers can only be closed, as closing doesn't
533+   --  involve accessing this map (other than possibly removing the
534+   --  forker from it if the map still exists).
535+   -- 
536+   --  As the LedgerDB should outlive any clients, this is fine.
515537  , ldbNextForkerKey  ::  ! (StrictTVar  m  ForkerKey )
516538  , ldbSnapshotPolicy  ::  ! SnapshotPolicy 
517539  , ldbTracer  ::  ! (Tracer  m  (TraceEvent  blk ))
@@ -691,21 +713,6 @@ newForkerByRollback ::
691713newForkerByRollback h rr n =  getEnv h $  \ ldbEnv ->  do 
692714  withReadLock (ldbLock ldbEnv) (acquireAtTarget ldbEnv (Left   n) >>=  traverse  (newForker h ldbEnv rr))
693715
694- --  |  Close all open block and header 'Forker's. 
695- closeAllForkers  :: 
696-   IOLike  m  => 
697-   LedgerDBEnv  m  l  blk  -> 
698-   m  () 
699- closeAllForkers ldbEnv = 
700-   do 
701-     forkerEnvs <-  atomically $  do 
702-       forkerEnvs <-  Map. elems <$>  readTVar forkersVar
703-       writeTVar forkersVar Map. empty
704-       return  forkerEnvs
705-     mapM_  closeForkerEnv forkerEnvs
706-  where 
707-   forkersVar =  ldbForkers ldbEnv
708- 
709716--  |  Acquire both a value handle and a db changelog at the tip. Holds a read lock 
710717--  while doing so. 
711718acquireAtTarget  :: 
@@ -784,7 +791,7 @@ newForker h ldbEnv rr dblog = readLocked $ do
784791    --  read access we acquired above.
785792    modifyTVar (ldbForkers ldbEnv) $  Map. insert forkerKey forkerEnv
786793  traceWith (foeTracer forkerEnv) ForkerOpen 
787-   pure  $  mkForker h (ldbQueryBatchSize ldbEnv) forkerKey
794+   pure  $  mkForker h (ldbQueryBatchSize ldbEnv) forkerKey forkerEnv 
788795
789796mkForker  :: 
790797  ( IOLike  m 
@@ -795,10 +802,11 @@ mkForker ::
795802  LedgerDBHandle  m  l  blk  -> 
796803  QueryBatchSize  -> 
797804  ForkerKey  -> 
805+   ForkerEnv  m  l  blk  -> 
798806  Forker  m  l  blk 
799- mkForker h qbs forkerKey = 
807+ mkForker h qbs forkerKey forkerEnv  = 
800808  Forker 
801-     { forkerClose =  implForkerClose h forkerKey
809+     { forkerClose =  implForkerClose h forkerKey forkerEnv 
802810    , forkerReadTables =  getForkerEnv1 h forkerKey implForkerReadTables
803811    , forkerRangeReadTables =  getForkerEnv1 h forkerKey (implForkerRangeReadTables qbs)
804812    , forkerGetLedgerState =  getForkerEnvSTM h forkerKey implForkerGetLedgerState
@@ -807,18 +815,27 @@ mkForker h qbs forkerKey =
807815    , forkerCommit =  getForkerEnvSTM h forkerKey implForkerCommit
808816    }
809817
818+ --  |  This function receives an environment instead of reading it from 
819+ --  the DB such that we can close the forker even if the LedgerDB is 
820+ --  closed. In fact this should never happen as clients of the LedgerDB 
821+ --  (which are the ones opening forkers) should never outlive the 
822+ --  LedgerDB. 
810823implForkerClose  :: 
811824  IOLike  m  => 
812825  LedgerDBHandle  m  l  blk  -> 
813826  ForkerKey  -> 
827+   ForkerEnv  m  l  blk  -> 
814828  m  () 
815- implForkerClose (LDBHandle  varState) forkerKey =  do 
816-   envMay  <- 
829+ implForkerClose (LDBHandle  varState) forkerKey env  =  do 
830+   frk  <- 
817831    atomically $ 
818832      readTVar varState >>=  \ case 
819833        LedgerDBClosed  ->  pure  Nothing 
820834        LedgerDBOpen  ldbEnv ->  do 
821835          stateTVar
822836            (ldbForkers ldbEnv)
823-             (Map. updateLookupWithKey (\ _ _ ->  Nothing ) forkerKey)
824-   whenJust envMay closeForkerEnv
837+             (\ m ->  Map. updateLookupWithKey (\ _ _ ->  Nothing ) forkerKey m)
838+   case  frk of 
839+     Nothing  ->  pure  () 
840+     Just  e ->  traceWith (foeTracer e) DanglingForkerClosed 
841+   closeForkerEnv env
0 commit comments