From 2b12878d9e1524a034a7c7f50aff2f60c5aec241 Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Tue, 4 Mar 2025 13:52:18 +0000 Subject: [PATCH 01/15] Refactor IncomingRun constructor functions We want to share merging run types and code better once we flesh out the merging tree functionality -- which uses merging runs. In particulur snapshot restore will need to construct merging runs in three places, and we want to reuse the same code in each place. Currently, the higher level constructors for merging runs are coupled with incoming runs, because that's the only place they are used now. But once we have merging runs in merging trees, then they're no longer in an incoming run. So we want to be able to reuse that without needing to rely on an incoming run. So we have to make incoming runs "thinner", and break up the policy functions for making merging runs at a given level. --- .../LSMTree/Internal/MergeSchedule.hs | 195 +++++++++--------- src/Database/LSMTree/Internal/Snapshot.hs | 54 +++-- 2 files changed, 128 insertions(+), 121 deletions(-) diff --git a/src/Database/LSMTree/Internal/MergeSchedule.hs b/src/Database/LSMTree/Internal/MergeSchedule.hs index ebe143c7c..5a5b2457b 100644 --- a/src/Database/LSMTree/Internal/MergeSchedule.hs +++ b/src/Database/LSMTree/Internal/MergeSchedule.hs @@ -26,11 +26,11 @@ module Database.LSMTree.Internal.MergeSchedule ( , IncomingRun (..) , MergePolicyForLevel (..) , newIncomingSingleRun - , newIncomingCompletedMergingRun , newIncomingMergingRun , releaseIncomingRun , supplyCreditsIncomingRun , snapshotIncomingRun + , mergingRunParamsForLevel -- * Union level , UnionLevel (..) -- * Flushes and scheduled merges @@ -45,6 +45,8 @@ module Database.LSMTree.Internal.MergeSchedule ( , creditThresholdForLevel , NominalDebt (..) , NominalCredits (..) + , nominalDebtAsCredits + , nominalDebtForLevel -- * Exported for testing , addWriteBufferEntries ) where @@ -67,14 +69,14 @@ import Database.LSMTree.Internal.Assertions (assert) import Database.LSMTree.Internal.Config import Database.LSMTree.Internal.Entry (Entry, NumEntries (..), unNumEntries) -import Database.LSMTree.Internal.Index (Index) +import Database.LSMTree.Internal.Index (Index, IndexType) import Database.LSMTree.Internal.Lookup (ResolveSerialisedValue) import Database.LSMTree.Internal.MergingRun (MergeCredits (..), MergeDebt (..), MergingRun, NumRuns (..)) import qualified Database.LSMTree.Internal.MergingRun as MR import Database.LSMTree.Internal.MergingTree (MergingTree) import Database.LSMTree.Internal.Paths (ActiveDir, RunFsPaths (..), - SessionRoot (..)) + SessionRoot) import qualified Database.LSMTree.Internal.Paths as Paths import Database.LSMTree.Internal.Run (Run, RunDataCaching (..)) import qualified Database.LSMTree.Internal.Run as Run @@ -121,9 +123,6 @@ data MergeTrace = | TraceNewMergeSingleRun NumEntries -- ^ Size of run RunNumber - | TraceNewMergeCompletedRun - NumEntries -- ^ Size of run - RunNumber | TraceCompletedMerge -- TODO: currently not traced for Incremental merges NumEntries -- ^ Size of output run RunNumber @@ -414,100 +413,23 @@ releaseIncomingRun :: releaseIncomingRun (Single r) = releaseRef r releaseIncomingRun (Merging _ _ _ mr) = releaseRef mr -{-# SPECIALISE newIncomingSingleRun :: - Tracer IO (AtLevel MergeTrace) - -> LevelNo - -> Ref (Run IO h) - -> IO (IncomingRun IO h) #-} +{-# INLINE newIncomingSingleRun #-} newIncomingSingleRun :: (PrimMonad m, MonadThrow m) - => Tracer m (AtLevel MergeTrace) - -> LevelNo - -> Ref (Run m h) - -> m (IncomingRun m h) -newIncomingSingleRun tr ln r = do - r' <- dupRef r - traceWith tr $ AtLevel ln $ - TraceNewMergeSingleRun (Run.size r') (Run.runFsPathsNumber r') - return (Single r') - -{-# SPECIALISE newIncomingCompletedMergingRun :: - Tracer IO (AtLevel MergeTrace) - -> TableConfig - -> LevelNo - -> MergePolicyForLevel - -> NumRuns - -> MergeDebt - -> Ref (Run IO h) - -> IO (IncomingRun IO h) #-} -newIncomingCompletedMergingRun :: - (MonadMask m, MonadMVar m, MonadSTM m, MonadST m) - => Tracer m (AtLevel MergeTrace) - -> TableConfig - -> LevelNo - -> MergePolicyForLevel - -> NumRuns - -> MergeDebt - -> Ref (Run m h) + => Ref (Run m h) -> m (IncomingRun m h) -newIncomingCompletedMergingRun tr conf ln mergePolicy nr mergeDebt r = do - traceWith tr $ AtLevel ln $ - TraceNewMergeCompletedRun (Run.size r) (Run.runFsPathsNumber r) - mr <- MR.newCompleted nr mergeDebt r - let nominalDebt = nominalDebtForLevel conf ln - nominalCredits = nominalDebtAsCredits nominalDebt - nominalCreditsVar <- newPrimVar nominalCredits - return (Merging mergePolicy nominalDebt nominalCreditsVar mr) +newIncomingSingleRun r = Single <$> dupRef r -{-# SPECIALISE newIncomingMergingRun :: - Tracer IO (AtLevel MergeTrace) - -> HasFS IO h - -> HasBlockIO IO h - -> ActiveDir - -> UniqCounter IO - -> TableConfig - -> ResolveSerialisedValue - -> MergePolicyForLevel - -> MR.LevelMergeType - -> LevelNo - -> V.Vector (Ref (Run IO h)) - -> IO (IncomingRun IO h) #-} +{-# INLINE newIncomingMergingRun #-} newIncomingMergingRun :: - (MonadMask m, MonadMVar m, MonadSTM m, MonadST m) - => Tracer m (AtLevel MergeTrace) - -> HasFS m h - -> HasBlockIO m h - -> ActiveDir - -> UniqCounter m - -> TableConfig - -> ResolveSerialisedValue - -> MergePolicyForLevel - -> MR.LevelMergeType - -> LevelNo - -> V.Vector (Ref (Run m h)) + PrimMonad m + => MergePolicyForLevel + -> NominalDebt + -> Ref (MergingRun MR.LevelMergeType m h) -> m (IncomingRun m h) -newIncomingMergingRun tr hfs hbio activeDir uc - conf@TableConfig { - confDiskCachePolicy, - confFencePointerIndex - } - resolve mergePolicy mergeType ln rs = do - !rn <- uniqueToRunNumber <$> incrUniqCounter uc - let !caching = diskCachePolicyForLevel confDiskCachePolicy ln - !alloc = bloomFilterAllocForLevel conf ln - !indexType = indexTypeForRun confFencePointerIndex - !runPaths = Paths.runPath activeDir rn - traceWith tr $ AtLevel ln $ - TraceNewMerge (V.map Run.size rs) (runNumber runPaths) - caching alloc mergePolicy mergeType - mr <- MR.new hfs hbio resolve caching - alloc indexType mergeType - runPaths rs - let nominalDebt = nominalDebtForLevel conf ln - nominalCredits = NominalCredits 0 - nominalCreditsVar <- newPrimVar nominalCredits - assert (MR.totalMergeDebt mr <= maxMergeDebt conf mergePolicy ln) $ - return (Merging mergePolicy nominalDebt nominalCreditsVar mr) +newIncomingMergingRun mergePolicy nominalDebt mr = do + nominalCreditsVar <- newPrimVar (NominalCredits 0) + return (Merging mergePolicy nominalDebt nominalCreditsVar mr) {-# SPECIALISE supplyCreditsIncomingRun :: TableConfig @@ -1063,13 +985,9 @@ addRunToLevels tr conf@TableConfig{..} resolve hfs hbio root uc r0 reg levels ul -> m (IncomingRun m h) newMerge mergePolicy mergeType ln rs = do ir <- withRollback reg - (case V.uncons rs of - Just (r, rest) | V.null rest - -> newIncomingSingleRun tr ln r - _ -> newIncomingMergingRun tr hfs hbio - (Paths.activeDir root) uc - conf resolve mergePolicy mergeType - ln rs) + (newIncomingRunAtLevel tr hfs hbio + root uc conf resolve + mergePolicy mergeType ln rs) releaseIncomingRun -- The runs will end up inside the incoming/merging run, with fresh -- references (since newIncoming* will make duplicates). @@ -1079,6 +997,81 @@ addRunToLevels tr conf@TableConfig{..} resolve hfs hbio root uc r0 reg levels ul Incremental -> pure () OneShot -> immediatelyCompleteIncomingRun tr conf ln ir return ir +{-# SPECIALISE newIncomingRunAtLevel :: + Tracer IO (AtLevel MergeTrace) + -> HasFS IO h + -> HasBlockIO IO h + -> SessionRoot + -> UniqCounter IO + -> TableConfig + -> ResolveSerialisedValue + -> MergePolicyForLevel + -> MR.LevelMergeType + -> LevelNo + -> V.Vector (Ref (Run IO h)) + -> IO (IncomingRun IO h) #-} +newIncomingRunAtLevel :: + (MonadMVar m, MonadMask m, MonadSTM m, MonadST m) + => Tracer m (AtLevel MergeTrace) + -> HasFS m h + -> HasBlockIO m h + -> SessionRoot + -> UniqCounter m + -> TableConfig + -> ResolveSerialisedValue + -> MergePolicyForLevel + -> MR.LevelMergeType + -> LevelNo + -> V.Vector (Ref (Run m h)) + -> m (IncomingRun m h) +newIncomingRunAtLevel tr hfs hbio + root uc conf resolve + mergePolicy mergeType ln rs + | Just (r, rest) <- V.uncons rs, V.null rest = do + + traceWith tr $ AtLevel ln $ + TraceNewMergeSingleRun (Run.size r) (Run.runFsPathsNumber r) + + newIncomingSingleRun r + + | otherwise = do + + uniq <- incrUniqCounter uc + let (caching, alloc, indexType, runPaths) = + mergingRunParamsForLevel (Paths.activeDir root) conf uniq ln + + traceWith tr $ AtLevel ln $ + TraceNewMerge (V.map Run.size rs) (runNumber runPaths) + caching alloc mergePolicy mergeType + + mr <- MR.new hfs hbio resolve caching + alloc indexType mergeType + runPaths rs + + assert (MR.totalMergeDebt mr <= maxMergeDebt conf mergePolicy ln) $ pure () + + let nominalDebt = nominalDebtForLevel conf ln + newIncomingMergingRun mergePolicy nominalDebt mr + +mergingRunParamsForLevel :: + ActiveDir + -> TableConfig + -> Unique + -> LevelNo + -> (RunDataCaching, RunBloomFilterAlloc, IndexType, RunFsPaths) +mergingRunParamsForLevel dir + conf@TableConfig { + confDiskCachePolicy, + confFencePointerIndex + } + unique ln = + (caching, alloc, indexType, runPaths) + where + !caching = diskCachePolicyForLevel confDiskCachePolicy ln + !alloc = bloomFilterAllocForLevel conf ln + !indexType = indexTypeForRun confFencePointerIndex + !runNum = uniqueToRunNumber unique + !runPaths = Paths.runPath dir runNum -- | We use levelling on the last level, unless that is also the first level. mergePolicyForLevel :: diff --git a/src/Database/LSMTree/Internal/Snapshot.hs b/src/Database/LSMTree/Internal/Snapshot.hs index d225abd67..170afc164 100644 --- a/src/Database/LSMTree/Internal/Snapshot.hs +++ b/src/Database/LSMTree/Internal/Snapshot.hs @@ -32,7 +32,6 @@ import Control.Monad.Class.MonadST (MonadST) import Control.Monad.Class.MonadThrow (MonadMask, bracketOnError) import Control.Monad.Primitive (PrimMonad) import Control.RefCount -import Control.Tracer (Tracer, nullTracer) import Data.Foldable (sequenceA_, traverse_) import Data.Text (Text) import Data.Traversable (for) @@ -467,8 +466,6 @@ fromSnapLevels reg hfs hbio conf uc resolve dir (SnapLevels levels) = V.iforM levels $ \i -> fromSnapLevel (LevelNo (i+1)) where -- TODO: we may wish to trace the merges created during snapshot restore: - tr :: Tracer m (AtLevel MergeTrace) - tr = nullTracer fromSnapLevel :: LevelNo -> SnapLevel (Ref (Run m h)) -> m (Level m h) fromSnapLevel ln SnapLevel{snapIncoming, snapResidentRuns} = do @@ -485,23 +482,40 @@ fromSnapLevels reg hfs hbio conf uc resolve dir (SnapLevels levels) = LevelNo -> SnapIncomingRun (Ref (Run m h)) -> m (IncomingRun m h) - fromSnapIncomingRun ln (SnapSingleRun run) = - newIncomingSingleRun tr ln run - - fromSnapIncomingRun ln (SnapMergingRun mpfl nr md _nc - (SnapCompletedMerge r)) = - newIncomingCompletedMergingRun tr conf ln mpfl nr md r - - fromSnapIncomingRun ln (SnapMergingRun mpfl _nr _md nc - (SnapOngoingMerge rs mt)) = do - bracketOnError (newIncomingMergingRun tr hfs hbio dir uc - conf resolve - mpfl mt ln rs) releaseIncomingRun $ \ir -> do - -- When a snapshot is created, merge progress is lost, so we have to - -- redo merging work here. The MergeCredits in SnapMergingRun tracks - -- how many credits were supplied before the snapshot was taken. - supplyCreditsIncomingRun conf ln ir nc - return ir + fromSnapIncomingRun _ln (SnapSingleRun run) = + newIncomingSingleRun run + + fromSnapIncomingRun ln (SnapMergingRun mergePolicy nr mergeDebt _nc + (SnapCompletedMerge r)) = do + mr <- MR.newCompleted nr mergeDebt r + let nominalDebt = nominalDebtForLevel conf ln + nominalCredits = nominalDebtAsCredits nominalDebt + ir <- newIncomingMergingRun mergePolicy nominalDebt mr + -- This will do no real work, since the mr is completed, it'll just + -- set the final nominal credits + supplyCreditsIncomingRun conf ln ir nominalCredits + return ir + + fromSnapIncomingRun ln (SnapMergingRun mergePolicy _nr _md nc + (SnapOngoingMerge rs mergeType)) = + bracketOnError + (do uniq <- incrUniqCounter uc + let (caching, alloc, indexType, runPaths) = + mergingRunParamsForLevel dir conf uniq ln + MR.new hfs hbio resolve caching + alloc indexType mergeType + runPaths rs) + releaseRef $ \mr -> do + + let nominalDebt = nominalDebtForLevel conf ln + ir <- newIncomingMergingRun mergePolicy nominalDebt mr + + -- When a snapshot is created, merge progress is lost, so we have to + -- redo merging work here. The MergeCredits in SnapMergingRun tracks + -- how many credits were supplied before the snapshot was taken. + --TODO: bracketOnError the MR.new for this: + supplyCreditsIncomingRun conf ln ir nc + return ir {------------------------------------------------------------------------------- Hard links From 87a2aace9e21f612fbf33eafba8d767591469c39 Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Thu, 6 Mar 2025 10:17:41 +0000 Subject: [PATCH 02/15] Introduce RunParams data type to bundle several run building params These params are (almost) always used together, so this helps to reduce verbosity and the number of separate args that have to be passed around. We also slightly simplify when args are provided. Now all these params are provided to the RunBuilder, and none extra are needed when the RunBuilder is turned into the Run. Previously, two were passed to the RunBuilder and one more passed to the Run (though actually finalising the RunBuidler also needed the third param anyway). However, the real motivation for all this is to improve restoring snapshots of merging runs. At the moment, deserialising a merging run requires us to supply all three of these params from the surrounding context. This is ok in the context of levels, but becomes awkward for merging runs embedded in a merging tree. The obvious solution is to serialise this set of parameters with the merging run, and then they're directly available to use when reconstructing the merge during snapshot restore. Changes to serialisation are not in this patch, but the next. --- bench/macro/lsm-tree-bench-lookups.hs | 10 ++- .../Bench/Database/LSMTree/Internal/Lookup.hs | 5 +- .../Bench/Database/LSMTree/Internal/Merge.hs | 4 +- .../Database/LSMTree/Extras/MergingRunData.hs | 6 +- .../Database/LSMTree/Extras/NoThunks.hs | 9 +++ src-extras/Database/LSMTree/Extras/RunData.hs | 21 ++++-- src/Database/LSMTree/Internal.hs | 23 ++----- src/Database/LSMTree/Internal/Index.hs | 1 + src/Database/LSMTree/Internal/Merge.hs | 23 +++---- .../LSMTree/Internal/MergeSchedule.hs | 59 +++++++---------- src/Database/LSMTree/Internal/MergingRun.hs | 19 ++---- src/Database/LSMTree/Internal/Run.hs | 42 +++++------- src/Database/LSMTree/Internal/RunBuilder.hs | 65 +++++++++++++------ src/Database/LSMTree/Internal/Snapshot.hs | 6 +- test/Test/Database/LSMTree/Internal/Merge.hs | 9 ++- .../Database/LSMTree/Internal/RunBuilder.hs | 11 ++-- 16 files changed, 158 insertions(+), 155 deletions(-) diff --git a/bench/macro/lsm-tree-bench-lookups.hs b/bench/macro/lsm-tree-bench-lookups.hs index af6ee7589..a55d2a898 100644 --- a/bench/macro/lsm-tree-bench-lookups.hs +++ b/bench/macro/lsm-tree-bench-lookups.hs @@ -32,6 +32,7 @@ import Database.LSMTree.Internal.Paths (RunFsPaths (RunFsPaths)) import Database.LSMTree.Internal.Run (Run) import qualified Database.LSMTree.Internal.Run as Run import Database.LSMTree.Internal.RunAcc (RunBloomFilterAlloc (..)) +import Database.LSMTree.Internal.RunBuilder (RunParams (..)) import qualified Database.LSMTree.Internal.RunBuilder as RunBuilder import Database.LSMTree.Internal.RunNumber import Database.LSMTree.Internal.Serialise (SerialisedKey, @@ -349,10 +350,13 @@ lookupsEnv runSizes keyRng0 hfs hbio caching = do -- create the runs rbs <- sequence [ RunBuilder.new hfs hbio + RunParams { + runParamCaching = caching, + runParamAlloc = RunAllocFixed benchmarkNumBitsPerEntry, + runParamIndex = Index.Compact + } (RunFsPaths (FS.mkFsPath []) (RunNumber i)) (NumEntries numEntries) - (RunAllocFixed benchmarkNumBitsPerEntry) - Index.Compact | ((numEntries, _), i) <- zip runSizes [0..] ] -- fill the runs @@ -373,7 +377,7 @@ lookupsEnv runSizes keyRng0 hfs hbio caching = do putStr "DONE" -- return runs - runs <- V.fromList <$> mapM (Run.fromMutable caching) rbs + runs <- V.fromList <$> mapM Run.fromMutable rbs let blooms = V.map (\(DeRef r) -> Run.runFilter r) runs indexes = V.map (\(DeRef r) -> Run.runIndex r) runs handles = V.map (\(DeRef r) -> Run.runKOpsFile r) runs diff --git a/bench/micro/Bench/Database/LSMTree/Internal/Lookup.hs b/bench/micro/Bench/Database/LSMTree/Internal/Lookup.hs index bd88c2476..74fd2f8d1 100644 --- a/bench/micro/Bench/Database/LSMTree/Internal/Lookup.hs +++ b/bench/micro/Bench/Database/LSMTree/Internal/Lookup.hs @@ -19,16 +19,15 @@ import qualified Data.Vector as V import Database.LSMTree.Extras.Orphans () import Database.LSMTree.Extras.Random (frequency, randomByteStringR, sampleUniformWithReplacement, uniformWithoutReplacement) +import Database.LSMTree.Extras.RunData (defaultRunParams) import Database.LSMTree.Extras.UTxO import Database.LSMTree.Internal.Entry (Entry (..), NumEntries (..)) -import qualified Database.LSMTree.Internal.Index as Index (IndexType (Compact)) import Database.LSMTree.Internal.Lookup (bloomQueries, indexSearches, intraPageLookups, lookupsIO, prepLookups) import Database.LSMTree.Internal.Page (getNumPages) import Database.LSMTree.Internal.Paths (RunFsPaths (..)) import Database.LSMTree.Internal.Run (Run) import qualified Database.LSMTree.Internal.Run as Run -import Database.LSMTree.Internal.RunAcc (RunBloomFilterAlloc (..)) import Database.LSMTree.Internal.RunNumber import Database.LSMTree.Internal.Serialise import qualified Database.LSMTree.Internal.WriteBuffer as WB @@ -192,7 +191,7 @@ lookupsInBatchesEnv Config {..} = do wbblobs <- WBB.new hasFS (FS.mkFsPath ["0.wbblobs"]) wb <- WB.fromMap <$> traverse (traverse (WBB.addBlob hasFS wbblobs)) storedKeys let fsps = RunFsPaths (FS.mkFsPath []) (RunNumber 0) - r <- Run.fromWriteBuffer hasFS hasBlockIO caching (RunAllocFixed 10) Index.Compact fsps wb wbblobs + r <- Run.fromWriteBuffer hasFS hasBlockIO defaultRunParams fsps wb wbblobs let NumEntries nentriesReal = Run.size r assertEqual nentriesReal nentries $ pure () -- 42 to 43 entries per page diff --git a/bench/micro/Bench/Database/LSMTree/Internal/Merge.hs b/bench/micro/Bench/Database/LSMTree/Internal/Merge.hs index 9f7976f8a..731184609 100644 --- a/bench/micro/Bench/Database/LSMTree/Internal/Merge.hs +++ b/bench/micro/Bench/Database/LSMTree/Internal/Merge.hs @@ -23,7 +23,6 @@ import qualified Database.LSMTree.Internal.Merge as Merge import Database.LSMTree.Internal.Paths (RunFsPaths (..)) import Database.LSMTree.Internal.Run (Run) import qualified Database.LSMTree.Internal.Run as Run -import Database.LSMTree.Internal.RunAcc (RunBloomFilterAlloc (..)) import Database.LSMTree.Internal.RunNumber import Database.LSMTree.Internal.Serialise import Database.LSMTree.Internal.UniqCounter @@ -264,8 +263,7 @@ merge :: merge fs hbio Config {..} targetPaths runs = do let f = fromMaybe const mergeMappend m <- fromMaybe (error "empty inputs, no merge created") <$> - Merge.new fs hbio Run.CacheRunData (RunAllocFixed 10) Index.Compact - mergeType f targetPaths runs + Merge.new fs hbio defaultRunParams mergeType f targetPaths runs Merge.stepsToCompletion m stepSize fsPath :: FS.FsPath diff --git a/src-extras/Database/LSMTree/Extras/MergingRunData.hs b/src-extras/Database/LSMTree/Extras/MergingRunData.hs index 854f273b1..426b5934a 100644 --- a/src-extras/Database/LSMTree/Extras/MergingRunData.hs +++ b/src-extras/Database/LSMTree/Extras/MergingRunData.hs @@ -28,9 +28,7 @@ import Database.LSMTree.Internal.Lookup (ResolveSerialisedValue) import Database.LSMTree.Internal.MergingRun (MergingRun) import qualified Database.LSMTree.Internal.MergingRun as MR import Database.LSMTree.Internal.Paths -import Database.LSMTree.Internal.Run (RunDataCaching (..)) import qualified Database.LSMTree.Internal.Run as Run -import Database.LSMTree.Internal.RunAcc (RunBloomFilterAlloc (..)) import Database.LSMTree.Internal.RunNumber import Database.LSMTree.Internal.Serialise import Database.LSMTree.Internal.UniqCounter @@ -88,8 +86,8 @@ unsafeCreateMergingRun hfs hbio resolve indexType path counter = \case $ \runs -> do n <- incrUniqCounter counter let fsPaths = RunFsPaths path (RunNumber (uniqueToInt n)) - MR.new hfs hbio resolve CacheRunData (RunAllocFixed 10) indexType - mergeType fsPaths (V.fromList runs) + MR.new hfs hbio resolve defaultRunParams mergeType + fsPaths (V.fromList runs) {------------------------------------------------------------------------------- MergingRunData diff --git a/src-extras/Database/LSMTree/Extras/NoThunks.hs b/src-extras/Database/LSMTree/Extras/NoThunks.hs index 510a758d3..e8cf06bac 100644 --- a/src-extras/Database/LSMTree/Extras/NoThunks.hs +++ b/src-extras/Database/LSMTree/Extras/NoThunks.hs @@ -214,9 +214,18 @@ deriving stock instance Generic (Run m h) deriving anyclass instance (Typeable m, Typeable (PrimState m), Typeable h) => NoThunks (Run m h) +deriving stock instance Generic RunParams +deriving anyclass instance NoThunks RunParams + +deriving stock instance Generic RunBloomFilterAlloc +deriving anyclass instance NoThunks RunBloomFilterAlloc + deriving stock instance Generic RunDataCaching deriving anyclass instance NoThunks RunDataCaching +deriving stock instance Generic IndexType +deriving anyclass instance NoThunks IndexType + {------------------------------------------------------------------------------- Paths -------------------------------------------------------------------------------} diff --git a/src-extras/Database/LSMTree/Extras/RunData.hs b/src-extras/Database/LSMTree/Extras/RunData.hs index cdac062b0..431bc2a30 100644 --- a/src-extras/Database/LSMTree/Extras/RunData.hs +++ b/src-extras/Database/LSMTree/Extras/RunData.hs @@ -2,8 +2,10 @@ -- from them. Tests and benchmarks should preferably use these utilities instead -- of (re-)defining their own. module Database.LSMTree.Extras.RunData ( + -- * RunParams + defaultRunParams -- * Create runs - withRun + , withRun , withRunAt , withRuns , unsafeCreateRun @@ -48,12 +50,13 @@ import qualified Data.Vector as V import Database.LSMTree.Extras (showPowersOf10) import Database.LSMTree.Extras.Generators () import Database.LSMTree.Internal.Entry -import Database.LSMTree.Internal.Index (IndexType) +import Database.LSMTree.Internal.Index (IndexType (..)) import Database.LSMTree.Internal.Lookup (ResolveSerialisedValue) import Database.LSMTree.Internal.MergeSchedule (addWriteBufferEntries) import Database.LSMTree.Internal.Paths import qualified Database.LSMTree.Internal.Paths as Paths -import Database.LSMTree.Internal.Run (Run, RunDataCaching (..)) +import Database.LSMTree.Internal.Run (Run, RunDataCaching (..), + RunParams (..)) import qualified Database.LSMTree.Internal.Run as Run import Database.LSMTree.Internal.RunAcc (RunBloomFilterAlloc (..), entryWouldFitInPage) @@ -69,6 +72,15 @@ import qualified System.FS.BlockIO.API as FS import System.FS.BlockIO.API (HasBlockIO) import Test.QuickCheck + +defaultRunParams :: RunParams +defaultRunParams = + RunParams { + runParamCaching = CacheRunData, + runParamAlloc = RunAllocFixed 10, + runParamIndex = Compact + } + {------------------------------------------------------------------------------- Create runs -------------------------------------------------------------------------------} @@ -153,7 +165,8 @@ unsafeCreateRunAt fs hbio indexType fsPaths (RunData m) = do let blobpath = FS.addExtension (runBlobPath fsPaths) ".wb" bracket (WBB.new fs blobpath) releaseRef $ \wbblobs -> do wb <- WB.fromMap <$> traverse (traverse (WBB.addBlob fs wbblobs)) m - Run.fromWriteBuffer fs hbio CacheRunData (RunAllocFixed 10) indexType + Run.fromWriteBuffer fs hbio + defaultRunParams { runParamIndex = indexType } fsPaths wb wbblobs -- | Create a 'RunFsPaths' using an empty 'FsPath'. The empty path corresponds diff --git a/src/Database/LSMTree/Internal.hs b/src/Database/LSMTree/Internal.hs index 9f76f0d44..c80ee128b 100644 --- a/src/Database/LSMTree/Internal.hs +++ b/src/Database/LSMTree/Internal.hs @@ -1534,28 +1534,19 @@ writeBufferToNewRun SessionEnv { sessionHasBlockIO = hbio, sessionUniqCounter = uc } - conf@TableConfig { - confDiskCachePolicy, - confFencePointerIndex - } + conf TableContent{ tableWriteBuffer, tableWriteBufferBlobs } | WB.null tableWriteBuffer = pure Nothing | otherwise = Just <$> do - !n <- incrUniqCounter uc - let !ln = LevelNo 1 - !cache = diskCachePolicyForLevel confDiskCachePolicy ln - !alloc = bloomFilterAllocForLevel conf ln - !indexType = indexTypeForRun confFencePointerIndex - !path = Paths.runPath (Paths.activeDir root) - (uniqueToRunNumber n) - Run.fromWriteBuffer hfs hbio - cache - alloc - indexType - path + !uniq <- incrUniqCounter uc + let (!runParams, !runPaths) = mergingRunParamsForLevel + (Paths.activeDir root) conf uniq (LevelNo 1) + Run.fromWriteBuffer + hfs hbio + runParams runPaths tableWriteBuffer tableWriteBufferBlobs diff --git a/src/Database/LSMTree/Internal/Index.hs b/src/Database/LSMTree/Internal/Index.hs index 7a9552797..4fe1e1436 100644 --- a/src/Database/LSMTree/Internal/Index.hs +++ b/src/Database/LSMTree/Internal/Index.hs @@ -70,6 +70,7 @@ import Database.LSMTree.Internal.Serialise (SerialisedKey) -- | The type of supported index types. data IndexType = Compact | Ordinary + deriving stock (Eq, Show) -- * Indexes diff --git a/src/Database/LSMTree/Internal/Merge.hs b/src/Database/LSMTree/Internal/Merge.hs index 782f4fbe7..9efb7b82d 100644 --- a/src/Database/LSMTree/Internal/Merge.hs +++ b/src/Database/LSMTree/Internal/Merge.hs @@ -9,6 +9,7 @@ module Database.LSMTree.Internal.Merge ( , TreeMergeType (..) , Mappend , MergeState (..) + , RunParams (..) , new , abort , complete @@ -31,11 +32,9 @@ import Data.Traversable (for) import qualified Data.Vector as V import Database.LSMTree.Internal.BlobRef (RawBlobRef) import Database.LSMTree.Internal.Entry -import Database.LSMTree.Internal.Index (IndexType) -import Database.LSMTree.Internal.Run (Run, RunDataCaching) +import Database.LSMTree.Internal.Run (Run) import qualified Database.LSMTree.Internal.Run as Run -import Database.LSMTree.Internal.RunAcc (RunBloomFilterAlloc (..)) -import Database.LSMTree.Internal.RunBuilder (RunBuilder) +import Database.LSMTree.Internal.RunBuilder (RunBuilder, RunParams) import qualified Database.LSMTree.Internal.RunBuilder as Builder import qualified Database.LSMTree.Internal.RunReader as Reader import Database.LSMTree.Internal.RunReaders (Readers) @@ -60,8 +59,6 @@ data Merge t m h = Merge { , mergeMappend :: !Mappend , mergeReaders :: {-# UNPACK #-} !(Readers m h) , mergeBuilder :: !(RunBuilder m h) - -- | The caching policy to use for the output Run. - , mergeCaching :: !RunDataCaching -- | The result of the latest call to 'steps'. This is used to determine -- whether a merge can be 'complete'd. , mergeState :: !(MutVar (PrimState m) MergeState) @@ -152,9 +149,7 @@ type Mappend = SerialisedValue -> SerialisedValue -> SerialisedValue IsMergeType t => HasFS IO h -> HasBlockIO IO h - -> RunDataCaching - -> RunBloomFilterAlloc - -> IndexType + -> RunParams -> t -> Mappend -> Run.RunFsPaths @@ -166,21 +161,19 @@ new :: (IsMergeType t, MonadMask m, MonadSTM m, MonadST m) => HasFS m h -> HasBlockIO m h - -> RunDataCaching - -> RunBloomFilterAlloc - -> IndexType + -> RunParams -> t -> Mappend -> Run.RunFsPaths -> V.Vector (Ref (Run m h)) -> m (Maybe (Merge t m h)) -new hfs hbio mergeCaching alloc indexType mergeType mergeMappend targetPaths runs = do +new hfs hbio runParams mergeType mergeMappend targetPaths runs = do -- no offset, no write buffer mreaders <- Readers.new Readers.NoOffsetKey Nothing runs for mreaders $ \mergeReaders -> do -- calculate upper bounds based on input runs let numEntries = V.foldMap' Run.size runs - mergeBuilder <- Builder.new hfs hbio targetPaths numEntries alloc indexType + mergeBuilder <- Builder.new hfs hbio runParams targetPaths numEntries mergeState <- newMutVar $! Merging return Merge { mergeIsLastLevel = isLastLevel mergeType @@ -239,7 +232,7 @@ complete Merge{..} = do Merging -> error "complete: Merge is not done" MergingDone -> do -- the readers are already drained, therefore closed - r <- Run.fromMutable mergeCaching mergeBuilder + r <- Run.fromMutable mergeBuilder writeMutVar mergeState $! Completed pure r Completed -> error "complete: Merge is already completed" diff --git a/src/Database/LSMTree/Internal/MergeSchedule.hs b/src/Database/LSMTree/Internal/MergeSchedule.hs index 5a5b2457b..9b66596a4 100644 --- a/src/Database/LSMTree/Internal/MergeSchedule.hs +++ b/src/Database/LSMTree/Internal/MergeSchedule.hs @@ -69,18 +69,17 @@ import Database.LSMTree.Internal.Assertions (assert) import Database.LSMTree.Internal.Config import Database.LSMTree.Internal.Entry (Entry, NumEntries (..), unNumEntries) -import Database.LSMTree.Internal.Index (Index, IndexType) +import Database.LSMTree.Internal.Index (Index) import Database.LSMTree.Internal.Lookup (ResolveSerialisedValue) import Database.LSMTree.Internal.MergingRun (MergeCredits (..), - MergeDebt (..), MergingRun, NumRuns (..)) + MergeDebt (..), MergingRun, NumRuns (..), RunParams (..)) import qualified Database.LSMTree.Internal.MergingRun as MR import Database.LSMTree.Internal.MergingTree (MergingTree) import Database.LSMTree.Internal.Paths (ActiveDir, RunFsPaths (..), SessionRoot) import qualified Database.LSMTree.Internal.Paths as Paths -import Database.LSMTree.Internal.Run (Run, RunDataCaching (..)) +import Database.LSMTree.Internal.Run (Run) import qualified Database.LSMTree.Internal.Run as Run -import Database.LSMTree.Internal.RunAcc (RunBloomFilterAlloc (..)) import Database.LSMTree.Internal.RunNumber import Database.LSMTree.Internal.Serialise (SerialisedBlob, SerialisedKey, SerialisedValue) @@ -107,8 +106,7 @@ data MergeTrace = TraceFlushWriteBuffer NumEntries -- ^ Size of the write buffer RunNumber - RunDataCaching - RunBloomFilterAlloc + RunParams | TraceAddLevel | TraceAddRun RunNumber -- ^ newly added run @@ -116,8 +114,7 @@ data MergeTrace = | TraceNewMerge (V.Vector NumEntries) -- ^ Sizes of input runs RunNumber - RunDataCaching - RunBloomFilterAlloc + RunParams MergePolicyForLevel MR.LevelMergeType | TraceNewMergeSingleRun @@ -834,30 +831,27 @@ flushWriteBuffer :: -> ActionRegistry m -> TableContent m h -> m (TableContent m h) -flushWriteBuffer tr conf@TableConfig{confFencePointerIndex, confDiskCachePolicy} - resolve hfs hbio root uc reg tc +flushWriteBuffer tr conf resolve hfs hbio root uc reg tc | WB.null (tableWriteBuffer tc) = pure tc | otherwise = do - !n <- incrUniqCounter uc + !uniq <- incrUniqCounter uc let !size = WB.numEntries (tableWriteBuffer tc) !ln = LevelNo 1 - !cache = diskCachePolicyForLevel confDiskCachePolicy ln - !alloc = bloomFilterAllocForLevel conf ln - !indexType = indexTypeForRun confFencePointerIndex - !path = Paths.runPath (Paths.activeDir root) (uniqueToRunNumber n) + (!runParams, + runPaths) = mergingRunParamsForLevel + (Paths.activeDir root) conf uniq ln + traceWith tr $ AtLevel ln $ - TraceFlushWriteBuffer size (runNumber path) cache alloc + TraceFlushWriteBuffer size (runNumber runPaths) runParams r <- withRollback reg - (Run.fromWriteBuffer hfs hbio - cache - alloc - indexType - path + (Run.fromWriteBuffer + hfs hbio + runParams runPaths (tableWriteBuffer tc) (tableWriteBufferBlobs tc)) releaseRef delayedCommit reg (releaseRef (tableWriteBufferBlobs tc)) - wbblobs' <- withRollback reg (WBB.new hfs (Paths.tableBlobPath root n)) + wbblobs' <- withRollback reg (WBB.new hfs (Paths.tableBlobPath root uniq)) releaseRef levels' <- addRunToLevels tr conf resolve hfs hbio root uc r reg (tableLevels tc) @@ -1037,16 +1031,14 @@ newIncomingRunAtLevel tr hfs hbio | otherwise = do uniq <- incrUniqCounter uc - let (caching, alloc, indexType, runPaths) = + let (!runParams, !runPaths) = mergingRunParamsForLevel (Paths.activeDir root) conf uniq ln traceWith tr $ AtLevel ln $ TraceNewMerge (V.map Run.size rs) (runNumber runPaths) - caching alloc mergePolicy mergeType + runParams mergePolicy mergeType - mr <- MR.new hfs hbio resolve caching - alloc indexType mergeType - runPaths rs + mr <- MR.new hfs hbio resolve runParams mergeType runPaths rs assert (MR.totalMergeDebt mr <= maxMergeDebt conf mergePolicy ln) $ pure () @@ -1058,20 +1050,19 @@ mergingRunParamsForLevel :: -> TableConfig -> Unique -> LevelNo - -> (RunDataCaching, RunBloomFilterAlloc, IndexType, RunFsPaths) + -> (RunParams, RunFsPaths) mergingRunParamsForLevel dir conf@TableConfig { confDiskCachePolicy, confFencePointerIndex } unique ln = - (caching, alloc, indexType, runPaths) + (RunParams {..}, runPaths) where - !caching = diskCachePolicyForLevel confDiskCachePolicy ln - !alloc = bloomFilterAllocForLevel conf ln - !indexType = indexTypeForRun confFencePointerIndex - !runNum = uniqueToRunNumber unique - !runPaths = Paths.runPath dir runNum + !runParamCaching = diskCachePolicyForLevel confDiskCachePolicy ln + !runParamAlloc = bloomFilterAllocForLevel conf ln + !runParamIndex = indexTypeForRun confFencePointerIndex + !runPaths = Paths.runPath dir (uniqueToRunNumber unique) -- | We use levelling on the last level, unless that is also the first level. mergePolicyForLevel :: diff --git a/src/Database/LSMTree/Internal/MergingRun.hs b/src/Database/LSMTree/Internal/MergingRun.hs index 46ecd1b25..e1e674ff7 100644 --- a/src/Database/LSMTree/Internal/MergingRun.hs +++ b/src/Database/LSMTree/Internal/MergingRun.hs @@ -6,6 +6,7 @@ module Database.LSMTree.Internal.MergingRun ( -- * Merging run MergingRun , NumRuns (..) + , RunParams (..) , new , newCompleted , duplicateRuns @@ -61,16 +62,14 @@ import Data.Primitive.PrimVar import qualified Data.Vector as V import Database.LSMTree.Internal.Assertions (assert) import Database.LSMTree.Internal.Entry (NumEntries (..)) -import Database.LSMTree.Internal.Index (IndexType) import Database.LSMTree.Internal.Lookup (ResolveSerialisedValue) import Database.LSMTree.Internal.Merge (IsMergeType (..), - LevelMergeType (..), Merge, StepResult (..), - TreeMergeType (..)) + LevelMergeType (..), Merge, RunParams (..), + StepResult (..), TreeMergeType (..)) import qualified Database.LSMTree.Internal.Merge as Merge import Database.LSMTree.Internal.Paths (RunFsPaths (..)) import Database.LSMTree.Internal.Run (Run) import qualified Database.LSMTree.Internal.Run as Run -import Database.LSMTree.Internal.RunAcc (RunBloomFilterAlloc) import System.FS.API (HasFS) import System.FS.BlockIO.API (HasBlockIO) @@ -132,9 +131,7 @@ instance NFData MergeKnownCompleted where => HasFS IO h -> HasBlockIO IO h -> ResolveSerialisedValue - -> Run.RunDataCaching - -> RunBloomFilterAlloc - -> IndexType + -> RunParams -> t -> RunFsPaths -> V.Vector (Ref (Run IO h)) @@ -151,19 +148,17 @@ new :: => HasFS m h -> HasBlockIO m h -> ResolveSerialisedValue - -> Run.RunDataCaching - -> RunBloomFilterAlloc - -> IndexType + -> RunParams -> t -> RunFsPaths -> V.Vector (Ref (Run m h)) -> m (Ref (MergingRun t m h)) -new hfs hbio resolve caching alloc indexType ty runPaths inputRuns = +new hfs hbio resolve runParams ty runPaths inputRuns = -- If creating the Merge fails, we must release the references again. withActionRegistry $ \reg -> do runs <- V.mapM (\r -> withRollback reg (dupRef r) releaseRef) inputRuns merge <- fromMaybe (error "newMerge: merges can not be empty") - <$> Merge.new hfs hbio caching alloc indexType ty resolve runPaths runs + <$> Merge.new hfs hbio runParams ty resolve runPaths runs let numInputRuns = NumRuns $ V.length runs let mergeDebt = numEntriesToMergeDebt (V.foldMap' Run.size runs) unsafeNew diff --git a/src/Database/LSMTree/Internal/Run.hs b/src/Database/LSMTree/Internal/Run.hs index decd8a0fa..982a76f64 100644 --- a/src/Database/LSMTree/Internal/Run.hs +++ b/src/Database/LSMTree/Internal/Run.hs @@ -19,6 +19,7 @@ module Database.LSMTree.Internal.Run ( -- ** Run creation , fromMutable , fromWriteBuffer + , RunParams (..) , RunDataCaching (..) -- * Snapshot , openFromDisk @@ -44,8 +45,8 @@ import Database.LSMTree.Internal.Index (Index, IndexType) import qualified Database.LSMTree.Internal.Index as Index (fromSBS, sizeInPages) import Database.LSMTree.Internal.Page (NumPages) import Database.LSMTree.Internal.Paths as Paths -import Database.LSMTree.Internal.RunAcc (RunBloomFilterAlloc) -import Database.LSMTree.Internal.RunBuilder (RunBuilder) +import Database.LSMTree.Internal.RunBuilder (RunBuilder, + RunDataCaching (..), RunParams (..)) import qualified Database.LSMTree.Internal.RunBuilder as Builder import Database.LSMTree.Internal.RunNumber import Database.LSMTree.Internal.Serialise @@ -147,14 +148,6 @@ finaliser hfs kopsFile blobFile fsPaths = do FS.removeFile hfs (runIndexPath fsPaths) FS.removeFile hfs (runChecksumsPath fsPaths) --- | Should this run cache key\/ops data in memory? -data RunDataCaching = CacheRunData | NoCacheRunData - deriving stock (Show, Eq) - -instance NFData RunDataCaching where - rnf CacheRunData = () - rnf NoCacheRunData = () - {-# SPECIALISE setRunDataCaching :: HasBlockIO IO h -> FS.Handle h @@ -176,18 +169,18 @@ setRunDataCaching hbio runKOpsFile NoCacheRunData = do FS.hSetNoCache hbio runKOpsFile True {-# SPECIALISE fromMutable :: - RunDataCaching - -> RunBuilder IO h + RunBuilder IO h -> IO (Ref (Run IO h)) #-} -- TODO: make exception safe fromMutable :: (MonadST m, MonadSTM m, MonadMask m) - => RunDataCaching - -> RunBuilder m h + => RunBuilder m h -> m (Ref (Run m h)) -fromMutable runRunDataCaching builder = do - (runHasFS, runHasBlockIO, runRunFsPaths, runFilter, runIndex, runNumEntries) <- - Builder.unsafeFinalise (runRunDataCaching == NoCacheRunData) builder +fromMutable builder = do + (runHasFS, runHasBlockIO, + runRunFsPaths, runFilter, runIndex, + RunParams {runParamCaching = runRunDataCaching}, runNumEntries) <- + Builder.unsafeFinalise builder runKOpsFile <- FS.hOpen runHasFS (runKOpsPath runRunFsPaths) FS.ReadMode -- TODO: openBlobFile should be called with exceptions masked runBlobFile <- openBlobFile runHasFS (runBlobPath runRunFsPaths) FS.ReadMode @@ -198,9 +191,7 @@ fromMutable runRunDataCaching builder = do {-# SPECIALISE fromWriteBuffer :: HasFS IO h -> HasBlockIO IO h - -> RunDataCaching - -> RunBloomFilterAlloc - -> IndexType + -> RunParams -> RunFsPaths -> WriteBuffer -> Ref (WriteBufferBlobs IO h) @@ -216,20 +207,17 @@ fromWriteBuffer :: (MonadST m, MonadSTM m, MonadMask m) => HasFS m h -> HasBlockIO m h - -> RunDataCaching - -> RunBloomFilterAlloc - -> IndexType + -> RunParams -> RunFsPaths -> WriteBuffer -> Ref (WriteBufferBlobs m h) -> m (Ref (Run m h)) -fromWriteBuffer fs hbio caching alloc indexType fsPaths buffer blobs = do - builder <- Builder.new fs hbio fsPaths (WB.numEntries buffer) - alloc indexType +fromWriteBuffer fs hbio params fsPaths buffer blobs = do + builder <- Builder.new fs hbio params fsPaths (WB.numEntries buffer) for_ (WB.toList buffer) $ \(k, e) -> Builder.addKeyOp builder k (fmap (WBB.mkRawBlobRef blobs) e) --TODO: the fmap entry here reallocates even when there are no blobs - fromMutable caching builder + fromMutable builder {------------------------------------------------------------------------------- Snapshot diff --git a/src/Database/LSMTree/Internal/RunBuilder.hs b/src/Database/LSMTree/Internal/RunBuilder.hs index 6c8e05ce3..d62b9bdb1 100644 --- a/src/Database/LSMTree/Internal/RunBuilder.hs +++ b/src/Database/LSMTree/Internal/RunBuilder.hs @@ -2,6 +2,10 @@ -- module Database.LSMTree.Internal.RunBuilder ( RunBuilder (..) + , RunParams (..) + , RunDataCaching (..) + , RunBloomFilterAlloc (..) + , IndexType (..) , new , addKeyOp , addLargeSerialisedKeyOp @@ -9,6 +13,7 @@ module Database.LSMTree.Internal.RunBuilder ( , close ) where +import Control.DeepSeq (NFData (..)) import Control.Monad (when) import Control.Monad.Class.MonadST (MonadST (..)) import qualified Control.Monad.Class.MonadST as ST @@ -23,11 +28,12 @@ import Database.LSMTree.Internal.BlobRef (RawBlobRef) import Database.LSMTree.Internal.ChecksumHandle import qualified Database.LSMTree.Internal.CRC32C as CRC import Database.LSMTree.Internal.Entry -import Database.LSMTree.Internal.Index (Index, IndexType) +import Database.LSMTree.Internal.Index (Index, IndexType (..)) import Database.LSMTree.Internal.Paths import Database.LSMTree.Internal.RawOverflowPage (RawOverflowPage) import Database.LSMTree.Internal.RawPage (RawPage) -import Database.LSMTree.Internal.RunAcc (RunAcc, RunBloomFilterAlloc) +import Database.LSMTree.Internal.RunAcc (RunAcc, + RunBloomFilterAlloc (..)) import qualified Database.LSMTree.Internal.RunAcc as RunAcc import Database.LSMTree.Internal.Serialise import qualified System.FS.API as FS @@ -47,8 +53,10 @@ import System.FS.BlockIO.API (HasBlockIO) -- __Not suitable for concurrent construction from multiple threads!__ -- data RunBuilder m h = RunBuilder { + runBuilderParams :: !RunParams + -- | The file system paths for all the files used by the run. - runBuilderFsPaths :: !RunFsPaths + , runBuilderFsPaths :: !RunFsPaths -- | The run accumulator. This is the representation used for the -- morally pure subset of the run cnstruction functionality. In @@ -65,13 +73,27 @@ data RunBuilder m h = RunBuilder { , runBuilderHasBlockIO :: !(HasBlockIO m h) } +data RunParams = RunParams { + runParamCaching :: !RunDataCaching, + runParamAlloc :: !RunBloomFilterAlloc, + runParamIndex :: !IndexType + } + deriving stock Show + +-- | Should this run cache key\/ops data in memory? +data RunDataCaching = CacheRunData | NoCacheRunData + deriving stock (Show, Eq) + +instance NFData RunDataCaching where + rnf CacheRunData = () + rnf NoCacheRunData = () + {-# SPECIALISE new :: HasFS IO h -> HasBlockIO IO h + -> RunParams -> RunFsPaths -> NumEntries - -> RunBloomFilterAlloc - -> IndexType -> IO (RunBuilder IO h) #-} -- | Create an 'RunBuilder' to start building a run. -- @@ -80,19 +102,19 @@ new :: (MonadST m, MonadSTM m) => HasFS m h -> HasBlockIO m h + -> RunParams -> RunFsPaths -> NumEntries -- ^ an upper bound of the number of entries to be added - -> RunBloomFilterAlloc - -> IndexType -> m (RunBuilder m h) -new hfs hbio runBuilderFsPaths numEntries alloc indexType = do - runBuilderAcc <- ST.stToIO $ RunAcc.new numEntries alloc indexType +new hfs hbio runBuilderParams@RunParams{..} runBuilderFsPaths numEntries = do + runBuilderAcc <- ST.stToIO $ + RunAcc.new numEntries runParamAlloc runParamIndex runBuilderBlobOffset <- newPrimVar 0 runBuilderHandles <- traverse (makeHandle hfs) (pathsForRunFiles runBuilderFsPaths) let builder = RunBuilder { runBuilderHasFS = hfs, runBuilderHasBlockIO = hbio, .. } - writeIndexHeader hfs (forRunIndex runBuilderHandles) indexType + writeIndexHeader hfs (forRunIndex runBuilderHandles) runParamIndex return builder {-# SPECIALISE addKeyOp :: @@ -165,9 +187,10 @@ addLargeSerialisedKeyOp RunBuilder{..} key page overflowPages = do for_ chunks $ writeIndexChunk runBuilderHasFS (forRunIndex runBuilderHandles) {-# SPECIALISE unsafeFinalise :: - Bool - -> RunBuilder IO h - -> IO (HasFS IO h, HasBlockIO IO h, RunFsPaths, Bloom SerialisedKey, Index, NumEntries) #-} + RunBuilder IO h + -> IO (HasFS IO h, HasBlockIO IO h, + RunFsPaths, Bloom SerialisedKey, Index, + RunParams, NumEntries) #-} -- | Finish construction of the run. -- Writes the filter and index to file and leaves all written files on disk. -- @@ -176,10 +199,12 @@ addLargeSerialisedKeyOp RunBuilder{..} key page overflowPages = do -- TODO: Ensure proper cleanup even in presence of exceptions. unsafeFinalise :: (MonadST m, MonadSTM m, MonadThrow m) - => Bool -- ^ drop caches - -> RunBuilder m h - -> m (HasFS m h, HasBlockIO m h, RunFsPaths, Bloom SerialisedKey, Index, NumEntries) -unsafeFinalise dropCaches RunBuilder {..} = do + => RunBuilder m h + -> m (HasFS m h, HasBlockIO m h, + RunFsPaths, Bloom SerialisedKey, Index, + RunParams, NumEntries) +-- TODO: consider introducing a type for this big tuple +unsafeFinalise RunBuilder {..} = do -- write final bits (mPage, mChunk, runFilter, runIndex, numEntries) <- ST.stToIO (RunAcc.unsafeFinalise runBuilderAcc) @@ -197,11 +222,13 @@ unsafeFinalise dropCaches RunBuilder {..} = do dropCache runBuilderHasBlockIO (forRunFilterRaw runBuilderHandles) dropCache runBuilderHasBlockIO (forRunIndexRaw runBuilderHandles) -- drop the KOps and blobs files from the cache if asked for - when dropCaches $ do + when (runParamCaching runBuilderParams == NoCacheRunData) $ do dropCache runBuilderHasBlockIO (forRunKOpsRaw runBuilderHandles) dropCache runBuilderHasBlockIO (forRunBlobRaw runBuilderHandles) mapM_ (closeHandle runBuilderHasFS) runBuilderHandles - return (runBuilderHasFS, runBuilderHasBlockIO, runBuilderFsPaths, runFilter, runIndex, numEntries) + return (runBuilderHasFS, runBuilderHasBlockIO, + runBuilderFsPaths, runFilter, runIndex, + runBuilderParams, numEntries) {-# SPECIALISE close :: RunBuilder IO h -> IO () #-} -- | Close a run that is being constructed (has not been finalised yet), diff --git a/src/Database/LSMTree/Internal/Snapshot.hs b/src/Database/LSMTree/Internal/Snapshot.hs index 170afc164..0ab332f98 100644 --- a/src/Database/LSMTree/Internal/Snapshot.hs +++ b/src/Database/LSMTree/Internal/Snapshot.hs @@ -500,11 +500,9 @@ fromSnapLevels reg hfs hbio conf uc resolve dir (SnapLevels levels) = (SnapOngoingMerge rs mergeType)) = bracketOnError (do uniq <- incrUniqCounter uc - let (caching, alloc, indexType, runPaths) = + let (runParams, runPaths) = mergingRunParamsForLevel dir conf uniq ln - MR.new hfs hbio resolve caching - alloc indexType mergeType - runPaths rs) + MR.new hfs hbio resolve runParams mergeType runPaths rs) releaseRef $ \mr -> do let nominalDebt = nominalDebtForLevel conf ln diff --git a/test/Test/Database/LSMTree/Internal/Merge.hs b/test/Test/Database/LSMTree/Internal/Merge.hs index 0363ce24a..e4aa5894b 100644 --- a/test/Test/Database/LSMTree/Internal/Merge.hs +++ b/test/Test/Database/LSMTree/Internal/Merge.hs @@ -21,7 +21,6 @@ import Database.LSMTree.Internal.PageAcc (entryWouldFitInPage) import Database.LSMTree.Internal.Paths (RunFsPaths (..), pathsForRunFiles) import qualified Database.LSMTree.Internal.Run as Run -import Database.LSMTree.Internal.RunAcc (RunBloomFilterAlloc (..)) import Database.LSMTree.Internal.RunNumber import Database.LSMTree.Internal.Serialise import Database.LSMTree.Internal.UniqCounter @@ -163,8 +162,8 @@ prop_AbortMerge fs hbio mergeType (Positive stepSize) (SmallList wbs) = do wbs' = fmap serialiseRunData wbs makeInProgressMerge path runs = - Merge.new fs hbio Run.CacheRunData (RunAllocFixed 10) Index.Compact - mergeType mappendValues path (V.fromList runs) >>= \case + Merge.new fs hbio defaultRunParams mergeType mappendValues + path (V.fromList runs) >>= \case Nothing -> return Nothing -- not in progress Just merge -> do -- just do a few steps once, ideally not completing the merge @@ -190,8 +189,8 @@ mergeRuns :: [Ref (Run.Run IO h)] -> IO (Int, Ref (Run.Run IO h)) mergeRuns fs hbio mergeType (Positive stepSize) fsPath runs = do - Merge.new fs hbio Run.CacheRunData (RunAllocFixed 10) Index.Compact - mergeType mappendValues fsPath (V.fromList runs) + Merge.new fs hbio defaultRunParams mergeType mappendValues + fsPath (V.fromList runs) >>= \case Just m -> Merge.stepsToCompletionCounted m stepSize Nothing -> (,) 0 <$> unsafeCreateRunAt fs hbio Index.Compact fsPath diff --git a/test/Test/Database/LSMTree/Internal/RunBuilder.hs b/test/Test/Database/LSMTree/Internal/RunBuilder.hs index ed3fac40d..618531948 100644 --- a/test/Test/Database/LSMTree/Internal/RunBuilder.hs +++ b/test/Test/Database/LSMTree/Internal/RunBuilder.hs @@ -4,10 +4,9 @@ module Test.Database.LSMTree.Internal.RunBuilder (tests) where import Control.Monad.Class.MonadThrow import Data.Foldable (traverse_) +import Database.LSMTree.Extras.RunData (defaultRunParams) import Database.LSMTree.Internal.Entry (NumEntries (..)) -import qualified Database.LSMTree.Internal.Index as Index (IndexType (Compact)) import Database.LSMTree.Internal.Paths (RunFsPaths (..)) -import Database.LSMTree.Internal.RunAcc (RunBloomFilterAlloc (..)) import qualified Database.LSMTree.Internal.RunBuilder as RunBuilder import Database.LSMTree.Internal.RunNumber import qualified System.FS.API as FS @@ -48,7 +47,7 @@ prop_newInExistingDir hfs hbio = do let runDir = FS.mkFsPath ["a", "b", "c"] FS.createDirectoryIfMissing hfs True runDir bracket - (try (RunBuilder.new hfs hbio (RunFsPaths runDir (RunNumber 17)) (NumEntries 0) (RunAllocFixed 10) Index.Compact)) + (try (RunBuilder.new hfs hbio defaultRunParams (RunFsPaths runDir (RunNumber 17)) (NumEntries 0))) (traverse_ RunBuilder.close) $ pure . \case Left e@FS.FsError{} -> counterexample ("expected a success, but got: " <> show e) $ property False @@ -59,7 +58,7 @@ prop_newInNonExistingDir :: HasFS IO h -> FS.HasBlockIO IO h -> IO Property prop_newInNonExistingDir hfs hbio = do let runDir = FS.mkFsPath ["a", "b", "c"] bracket - (try (RunBuilder.new hfs hbio (RunFsPaths runDir (RunNumber 17)) (NumEntries 0) (RunAllocFixed 10) Index.Compact)) + (try (RunBuilder.new hfs hbio defaultRunParams (RunFsPaths runDir (RunNumber 17)) (NumEntries 0))) (traverse_ RunBuilder.close) $ pure . \case Left FS.FsError{} -> property True Right _ -> @@ -73,10 +72,10 @@ prop_newTwice :: HasFS IO h -> FS.HasBlockIO IO h -> IO Property prop_newTwice hfs hbio = do let runDir = FS.mkFsPath [] bracket - (RunBuilder.new hfs hbio (RunFsPaths runDir (RunNumber 17)) (NumEntries 0) (RunAllocFixed 10) Index.Compact) + (RunBuilder.new hfs hbio defaultRunParams (RunFsPaths runDir (RunNumber 17)) (NumEntries 0)) RunBuilder.close $ \_ -> bracket - (try (RunBuilder.new hfs hbio (RunFsPaths runDir (RunNumber 17)) (NumEntries 0) (RunAllocFixed 10) Index.Compact)) + (try (RunBuilder.new hfs hbio defaultRunParams (RunFsPaths runDir (RunNumber 17)) (NumEntries 0))) (traverse_ RunBuilder.close) $ pure . \case Left FS.FsError{} -> property True Right _ -> From c1825152f952efeed9f4d26d83a0d1762d2dc1e4 Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Tue, 4 Mar 2025 16:24:00 +0000 Subject: [PATCH 03/15] Change representation for snapshot of merging run The goal is to make the representation of the snapshot of the merging run closer to being sufficient information to reconstruct the merging run upon snapshot restore. Previously we only embed the merging run within an incoming run and we use information from that context to reconstruct the merging run. In future we want to reuse the representation of snapshots of merging runs within merging trees, and those do not contain the same context information as an incoming run does. So to enable reuse, it makes sense to make the merging run snapshot representation more standalone. In particular: * SnapIncomingRun no longer contains NumRuns or MergeDebt, and these get moved down to the SnapMergingRunState. * SnapIncomingRun now stores the NominalDebt * SnapMergingRunState also gains the RunParams and MergeCredits --- src/Database/LSMTree/Internal/Config.hs | 2 +- src/Database/LSMTree/Internal/Index.hs | 5 +- src/Database/LSMTree/Internal/Merge.hs | 4 + .../LSMTree/Internal/MergeSchedule.hs | 21 +-- src/Database/LSMTree/Internal/RunAcc.hs | 12 +- src/Database/LSMTree/Internal/RunBuilder.hs | 5 +- src/Database/LSMTree/Internal/Snapshot.hs | 132 ++++++++++-------- .../LSMTree/Internal/Snapshot/Codec.hs | 119 ++++++++++++++-- .../LSMTree/Internal/Snapshot/Codec.hs | 60 ++++++-- .../LSMTree/Internal/Snapshot/Codec/Golden.hs | 38 +++-- 10 files changed, 290 insertions(+), 108 deletions(-) diff --git a/src/Database/LSMTree/Internal/Config.hs b/src/Database/LSMTree/Internal/Config.hs index 3c61f90c1..ccf30c826 100644 --- a/src/Database/LSMTree/Internal/Config.hs +++ b/src/Database/LSMTree/Internal/Config.hs @@ -46,7 +46,7 @@ import qualified Monkey newtype LevelNo = LevelNo Int deriving stock (Show, Eq) - deriving newtype Enum + deriving newtype (Enum, NFData) {------------------------------------------------------------------------------- Table configuration diff --git a/src/Database/LSMTree/Internal/Index.hs b/src/Database/LSMTree/Internal/Index.hs index 4fe1e1436..25f80773f 100644 --- a/src/Database/LSMTree/Internal/Index.hs +++ b/src/Database/LSMTree/Internal/Index.hs @@ -72,6 +72,10 @@ import Database.LSMTree.Internal.Serialise (SerialisedKey) data IndexType = Compact | Ordinary deriving stock (Eq, Show) +instance NFData IndexType where + rnf Compact = () + rnf Ordinary = () + -- * Indexes -- | The type of supported indexes. @@ -81,7 +85,6 @@ data Index deriving stock (Eq, Show) instance NFData Index where - rnf (CompactIndex index) = rnf index rnf (OrdinaryIndex index) = rnf index diff --git a/src/Database/LSMTree/Internal/Merge.hs b/src/Database/LSMTree/Internal/Merge.hs index 9efb7b82d..21352b9b0 100644 --- a/src/Database/LSMTree/Internal/Merge.hs +++ b/src/Database/LSMTree/Internal/Merge.hs @@ -17,6 +17,7 @@ module Database.LSMTree.Internal.Merge ( , stepsToCompletionCounted , StepResult (..) , steps + , mergeRunParams ) where import Control.DeepSeq (NFData (..)) @@ -66,6 +67,9 @@ data Merge t m h = Merge { , mergeHasBlockIO :: !(HasBlockIO m h) } +mergeRunParams :: Merge t m h -> RunParams +mergeRunParams = Builder.runBuilderParams . mergeBuilder + -- | The current state of the merge. data MergeState = -- | There is still merging work to be done diff --git a/src/Database/LSMTree/Internal/MergeSchedule.hs b/src/Database/LSMTree/Internal/MergeSchedule.hs index 9b66596a4..8ff9ad930 100644 --- a/src/Database/LSMTree/Internal/MergeSchedule.hs +++ b/src/Database/LSMTree/Internal/MergeSchedule.hs @@ -72,7 +72,7 @@ import Database.LSMTree.Internal.Entry (Entry, NumEntries (..), import Database.LSMTree.Internal.Index (Index) import Database.LSMTree.Internal.Lookup (ResolveSerialisedValue) import Database.LSMTree.Internal.MergingRun (MergeCredits (..), - MergeDebt (..), MergingRun, NumRuns (..), RunParams (..)) + MergeDebt (..), MergingRun, RunParams (..)) import qualified Database.LSMTree.Internal.MergingRun as MR import Database.LSMTree.Internal.MergingTree (MergingTree) import Database.LSMTree.Internal.Paths (ActiveDir, RunFsPaths (..), @@ -379,6 +379,7 @@ instance NFData MergePolicyForLevel where -- complete. newtype NominalDebt = NominalDebt Int deriving stock Eq + deriving newtype (NFData) -- | Merge credits that get supplied to a table's levels. -- @@ -611,31 +612,21 @@ immediatelyCompleteIncomingRun tr conf ln ir = IncomingRun IO h -> IO (Either (Ref (Run IO h)) (MergePolicyForLevel, - NumRuns, NominalDebt, NominalCredits, - MergeDebt, - MergeCredits, - MR.MergingRunState MR.LevelMergeType IO h)) #-} + Ref (MergingRun MR.LevelMergeType IO h))) #-} snapshotIncomingRun :: - (PrimMonad m, MonadMVar m) + PrimMonad m => IncomingRun m h -> m (Either (Ref (Run m h)) (MergePolicyForLevel, - NumRuns, NominalDebt, NominalCredits, - MergeDebt, - MergeCredits, - MR.MergingRunState MR.LevelMergeType m h)) + Ref (MergingRun MR.LevelMergeType m h))) snapshotIncomingRun (Single r) = pure (Left r) snapshotIncomingRun (Merging mergePolicy nominalDebt nominalCreditsVar mr) = do - (numRuns, mergeDebt, mergeCredit, state) <- MR.snapshot mr nominalCredits <- readPrimVar nominalCreditsVar - pure (Right (mergePolicy, numRuns, - nominalDebt, nominalCredits, - mergeDebt, mergeCredit, - state)) + pure (Right (mergePolicy, nominalDebt, nominalCredits, mr)) {------------------------------------------------------------------------------- Union level diff --git a/src/Database/LSMTree/Internal/RunAcc.hs b/src/Database/LSMTree/Internal/RunAcc.hs index 0ffeaff57..18e43e4c3 100644 --- a/src/Database/LSMTree/Internal/RunAcc.hs +++ b/src/Database/LSMTree/Internal/RunAcc.hs @@ -31,6 +31,7 @@ module Database.LSMTree.Internal.RunAcc ( , PageAcc.entryWouldFitInPage ) where +import Control.DeepSeq (NFData (..)) import Control.Exception (assert) import Control.Monad.ST.Strict import Data.BloomFilter (Bloom, MBloom) @@ -78,12 +79,17 @@ data RunAcc s = RunAcc { -- | See 'Database.LSMTree.Internal.BloomFilterAlloc' data RunBloomFilterAlloc = -- | Bits per element in a filter - RunAllocFixed Word64 - | RunAllocRequestFPR Double + RunAllocFixed !Word64 + | RunAllocRequestFPR !Double -- | Total number of bits for a filter - | RunAllocMonkey Word64 + | RunAllocMonkey !Word64 deriving stock (Show, Eq) +instance NFData RunBloomFilterAlloc where + rnf (RunAllocFixed a) = rnf a + rnf (RunAllocRequestFPR a) = rnf a + rnf (RunAllocMonkey a) = rnf a + -- | @'new' nentries@ starts an incremental run construction. -- -- @nentries@ should be an upper bound on the expected number of entries in the diff --git a/src/Database/LSMTree/Internal/RunBuilder.hs b/src/Database/LSMTree/Internal/RunBuilder.hs index d62b9bdb1..e23ec9559 100644 --- a/src/Database/LSMTree/Internal/RunBuilder.hs +++ b/src/Database/LSMTree/Internal/RunBuilder.hs @@ -78,7 +78,10 @@ data RunParams = RunParams { runParamAlloc :: !RunBloomFilterAlloc, runParamIndex :: !IndexType } - deriving stock Show + deriving stock (Eq, Show) + +instance NFData RunParams where + rnf (RunParams a b c) = rnf a `seq` rnf b `seq` rnf c -- | Should this run cache key\/ops data in memory? data RunDataCaching = CacheRunData | NoCacheRunData diff --git a/src/Database/LSMTree/Internal/Snapshot.hs b/src/Database/LSMTree/Internal/Snapshot.hs index 0ab332f98..524765602 100644 --- a/src/Database/LSMTree/Internal/Snapshot.hs +++ b/src/Database/LSMTree/Internal/Snapshot.hs @@ -48,9 +48,9 @@ import Database.LSMTree.Internal.Paths (ActiveDir (..), ForBlob (..), ForKOps (..), NamedSnapshotDir (..), RunFsPaths (..), WriteBufferFsPaths (..), fromChecksumsFileForWriteBufferFiles, pathsForRunFiles, - runChecksumsPath, writeBufferBlobPath, + runChecksumsPath, runPath, writeBufferBlobPath, writeBufferChecksumsPath, writeBufferKOpsPath) -import Database.LSMTree.Internal.Run (Run) +import Database.LSMTree.Internal.Run (Run, RunParams) import qualified Database.LSMTree.Internal.Run as Run import Database.LSMTree.Internal.RunNumber import Database.LSMTree.Internal.UniqCounter (UniqCounter, @@ -156,8 +156,7 @@ instance NFData r => NFData (SnapLevel r) where -- data SnapIncomingRun r = SnapMergingRun !MergePolicyForLevel - !NumRuns - !MergeDebt -- ^ The total merge debt. + !NominalDebt !NominalCredits -- ^ The nominal credits supplied, and that -- need to be supplied on snapshot open. !(SnapMergingRunState MR.LevelMergeType r) @@ -165,8 +164,8 @@ data SnapIncomingRun r = deriving stock (Eq, Functor, Foldable, Traversable) instance NFData r => NFData (SnapIncomingRun r) where - rnf (SnapMergingRun a b c d e) = - rnf a `seq` rnf b `seq` rnf c `seq` rnf d `seq` rnf e + rnf (SnapMergingRun a b c d) = + rnf a `seq` rnf b `seq` rnf c `seq` rnf d rnf (SnapSingleRun a) = rnf a -- | The total number of supplied credits. This total is used on snapshot load @@ -176,13 +175,13 @@ newtype SuppliedCredits = SuppliedCredits { getSuppliedCredits :: Int } deriving newtype NFData data SnapMergingRunState t r = - SnapCompletedMerge !r - | SnapOngoingMerge !(V.Vector r) !t + SnapCompletedMerge !NumRuns !MergeDebt !r + | SnapOngoingMerge !RunParams !MergeCredits !(V.Vector r) !t deriving stock (Eq, Functor, Foldable, Traversable) instance (NFData t, NFData r) => NFData (SnapMergingRunState t r) where - rnf (SnapCompletedMerge a) = rnf a - rnf (SnapOngoingMerge a b) = rnf a `seq` rnf b + rnf (SnapCompletedMerge a b c) = rnf a `seq` rnf b `seq` rnf c + rnf (SnapOngoingMerge a b c d) = rnf a `seq` rnf b `seq` rnf c `seq` rnf d {------------------------------------------------------------------------------- Conversion to levels snapshot format @@ -220,31 +219,34 @@ toSnapIncomingRun ir = do case s of Left r -> pure $! SnapSingleRun r Right (mergePolicy, - numRuns, - _nominalDebt, -- not stored + nominalDebt, nominalCredits, - mergeDebt, - _mergeCredits, -- not stored - mergingRunState) -> do + mergingRun) -> do -- We need to know how many credits were supplied so we can restore merge -- work on snapshot load. - -- TODO: MR.snapshot needs to return duplicated run references, and we - -- need to arrange to release them when the snapshoting is done. - let smrs = toSnapMergingRunState mergingRunState - pure $! - SnapMergingRun - mergePolicy - numRuns - mergeDebt - nominalCredits - smrs + smrs <- toSnapMergingRunState mergingRun + pure $! SnapMergingRun mergePolicy nominalDebt nominalCredits smrs +{-# SPECIALISE toSnapMergingRunState :: + Ref (MR.MergingRun t IO h) + -> IO (SnapMergingRunState t (Ref (Run IO h))) #-} toSnapMergingRunState :: - MR.MergingRunState t m h - -> SnapMergingRunState t (Ref (Run m h)) -toSnapMergingRunState = \case - MR.CompletedMerge r -> SnapCompletedMerge r - MR.OngoingMerge rs m -> SnapOngoingMerge rs (Merge.mergeType m) + (PrimMonad m, MonadMVar m) + => Ref (MR.MergingRun t m h) + -> m (SnapMergingRunState t (Ref (Run m h))) +toSnapMergingRunState !mr = do + -- TODO: MR.snapshot needs to return duplicated run references, and we + -- need to arrange to release them when the snapshoting is done. + (numRuns, mergeDebt, mergeCredits, state) <- MR.snapshot mr + case state of + MR.CompletedMerge r -> + pure $! SnapCompletedMerge numRuns mergeDebt r + + MR.OngoingMerge rs m -> + pure $! SnapOngoingMerge runParams mergeCredits rs mergeType + where + runParams = Merge.mergeRunParams m + mergeType = Merge.mergeType m {------------------------------------------------------------------------------- Write Buffer @@ -485,35 +487,55 @@ fromSnapLevels reg hfs hbio conf uc resolve dir (SnapLevels levels) = fromSnapIncomingRun _ln (SnapSingleRun run) = newIncomingSingleRun run - fromSnapIncomingRun ln (SnapMergingRun mergePolicy nr mergeDebt _nc - (SnapCompletedMerge r)) = do - mr <- MR.newCompleted nr mergeDebt r - let nominalDebt = nominalDebtForLevel conf ln - nominalCredits = nominalDebtAsCredits nominalDebt + fromSnapIncomingRun ln (SnapMergingRun mergePolicy nominalDebt + nominalCredits smrs) = do + mr <- fromSnapMergingRunState hfs hbio uc resolve dir smrs ir <- newIncomingMergingRun mergePolicy nominalDebt mr - -- This will do no real work, since the mr is completed, it'll just - -- set the final nominal credits + -- This will set the correct nominal credits, but it will not do any more + -- merging work because fromSnapMergingRunState already supplies all the + -- merging credits already. supplyCreditsIncomingRun conf ln ir nominalCredits return ir - fromSnapIncomingRun ln (SnapMergingRun mergePolicy _nr _md nc - (SnapOngoingMerge rs mergeType)) = - bracketOnError - (do uniq <- incrUniqCounter uc - let (runParams, runPaths) = - mergingRunParamsForLevel dir conf uniq ln - MR.new hfs hbio resolve runParams mergeType runPaths rs) - releaseRef $ \mr -> do - - let nominalDebt = nominalDebtForLevel conf ln - ir <- newIncomingMergingRun mergePolicy nominalDebt mr - - -- When a snapshot is created, merge progress is lost, so we have to - -- redo merging work here. The MergeCredits in SnapMergingRun tracks - -- how many credits were supplied before the snapshot was taken. - --TODO: bracketOnError the MR.new for this: - supplyCreditsIncomingRun conf ln ir nc - return ir +{-# SPECIALISE fromSnapMergingRunState :: + MR.IsMergeType t + => HasFS IO h + -> HasBlockIO IO h + -> UniqCounter IO + -> ResolveSerialisedValue + -> ActiveDir + -> SnapMergingRunState t (Ref (Run IO h)) + -> IO (Ref (MR.MergingRun t IO h)) #-} +fromSnapMergingRunState :: + (MonadMask m, MonadMVar m, MonadSTM m, MonadST m, MR.IsMergeType t) + => HasFS m h + -> HasBlockIO m h + -> UniqCounter m + -> ResolveSerialisedValue + -> ActiveDir + -> SnapMergingRunState t (Ref (Run m h)) + -> m (Ref (MR.MergingRun t m h)) +fromSnapMergingRunState _hfs _hbio _uc _resolve _dir + (SnapCompletedMerge numRuns mergeDebt r) = + MR.newCompleted numRuns mergeDebt r + +fromSnapMergingRunState hfs hbio uc resolve dir + (SnapOngoingMerge runParams mergeCredits + rs mergeType) = do + bracketOnError + (do uniq <- incrUniqCounter uc + let runPaths = runPath dir (uniqueToRunNumber uniq) + MR.new hfs hbio resolve runParams mergeType runPaths rs) + releaseRef $ \mr -> do + -- When a snapshot is created, merge progress is lost, so we have to + -- redo merging work here. The MergeCredits in SnapMergingRun tracks + -- how many credits were supplied before the snapshot was taken. + + --TODO: the threshold should be stored with the MergingRun + -- here we want to supply the credits now, so we can use a threshold of 1 + let thresh = MR.CreditThreshold (MR.UnspentCredits 1) + _ <- MR.supplyCreditsAbsolute mr thresh mergeCredits + return mr {------------------------------------------------------------------------------- Hard links diff --git a/src/Database/LSMTree/Internal/Snapshot/Codec.hs b/src/Database/LSMTree/Internal/Snapshot/Codec.hs index 421905259..994079fdd 100644 --- a/src/Database/LSMTree/Internal/Snapshot/Codec.hs +++ b/src/Database/LSMTree/Internal/Snapshot/Codec.hs @@ -34,6 +34,9 @@ import Database.LSMTree.Internal.Entry import Database.LSMTree.Internal.MergeSchedule import Database.LSMTree.Internal.MergingRun (NumRuns (..)) import qualified Database.LSMTree.Internal.MergingRun as MR +import Database.LSMTree.Internal.RunBuilder (IndexType (..), + RunBloomFilterAlloc (..), RunDataCaching (..), + RunParams (..)) import Database.LSMTree.Internal.RunNumber import Database.LSMTree.Internal.Snapshot import qualified System.FS.API as FS @@ -342,6 +345,75 @@ instance Encode NumEntries where instance DecodeVersioned NumEntries where decodeVersioned V0 = NumEntries <$> decodeInt +-- RunParams and friends + +instance Encode RunParams where + encode RunParams { runParamCaching, runParamAlloc, runParamIndex } = + encodeListLen 4 + <> encodeWord 0 + <> encode runParamCaching + <> encode runParamAlloc + <> encode runParamIndex + +instance DecodeVersioned RunParams where + decodeVersioned v@V0 = do + n <- decodeListLen + tag <- decodeWord + case (n, tag) of + (4, 0) -> do runParamCaching <- decodeVersioned v + runParamAlloc <- decodeVersioned v + runParamIndex <- decodeVersioned v + pure RunParams{..} + _ -> fail ("[RunParams] Unexpected combination of list length and tag: " <> show (n, tag)) + +instance Encode RunDataCaching where + encode CacheRunData = encodeWord 0 + encode NoCacheRunData = encodeWord 1 + +instance DecodeVersioned RunDataCaching where + decodeVersioned V0 = do + tag <- decodeWord + case tag of + 0 -> pure CacheRunData + 1 -> pure NoCacheRunData + _ -> fail ("[RunDataCaching] Unexpected tag: " <> show tag) + +instance Encode IndexType where + encode Ordinary = encodeWord 0 + encode Compact = encodeWord 1 + +instance DecodeVersioned IndexType where + decodeVersioned V0 = do + tag <- decodeWord + case tag of + 0 -> pure Ordinary + 1 -> pure Compact + _ -> fail ("[IndexType] Unexpected tag: " <> show tag) + +instance Encode RunBloomFilterAlloc where + encode (RunAllocFixed bits) = + encodeListLen 2 + <> encodeWord 0 + <> encodeWord64 bits + encode (RunAllocRequestFPR fpr) = + encodeListLen 2 + <> encodeWord 1 + <> encodeDouble fpr + encode (RunAllocMonkey bits) = + encodeListLen 2 + <> encodeWord 2 + <> encodeWord64 bits + +instance DecodeVersioned RunBloomFilterAlloc where + decodeVersioned V0 = do + n <- decodeListLen + tag <- decodeWord + case (n, tag) of + (2, 0) -> RunAllocFixed <$> decodeWord64 + (2, 1) -> RunAllocRequestFPR <$> decodeDouble + (2, 2) -> RunAllocMonkey <$> decodeWord64 + _ -> fail ("[RunBloomFilterAlloc] Unexpected combination of list length and tag: " <> show (n, tag)) + -- BloomFilterAlloc instance Encode BloomFilterAlloc where @@ -474,12 +546,11 @@ instance DecodeVersioned RunNumber where -- SnapIncomingRun instance Encode (SnapIncomingRun RunNumber) where - encode (SnapMergingRun mpfl nr md nc smrs) = - encodeListLen 6 + encode (SnapMergingRun mpfl nd nc smrs) = + encodeListLen 5 <> encodeWord 0 <> encode mpfl - <> encode nr - <> encode md + <> encode nd <> encode nc <> encode smrs encode (SnapSingleRun x) = @@ -492,9 +563,8 @@ instance DecodeVersioned (SnapIncomingRun RunNumber) where n <- decodeListLen tag <- decodeWord case (n, tag) of - (6, 0) -> SnapMergingRun <$> - decodeVersioned v <*> decodeVersioned v <*> decodeVersioned v <*> - decodeVersioned v <*> decodeVersioned v + (5, 0) -> SnapMergingRun <$> decodeVersioned v <*> decodeVersioned v + <*> decodeVersioned v <*> decodeVersioned v (2, 1) -> SnapSingleRun <$> decodeVersioned v _ -> fail ("[SnapMergingRun] Unexpected combination of list length and tag: " <> show (n, tag)) @@ -523,13 +593,17 @@ instance DecodeVersioned MergePolicyForLevel where -- SnapMergingRunState instance Encode t => Encode (SnapMergingRunState t RunNumber) where - encode (SnapCompletedMerge x) = - encodeListLen 2 + encode (SnapCompletedMerge nr md r) = + encodeListLen 4 <> encodeWord 0 - <> encode x - encode (SnapOngoingMerge rs mt) = - encodeListLen 3 + <> encode nr + <> encode md + <> encode r + encode (SnapOngoingMerge ln mc rs mt) = + encodeListLen 5 <> encodeWord 1 + <> encode ln + <> encode mc <> encode rs <> encode mt @@ -538,11 +612,20 @@ instance DecodeVersioned t => DecodeVersioned (SnapMergingRunState t RunNumber) n <- decodeListLen tag <- decodeWord case (n, tag) of - (2, 0) -> SnapCompletedMerge <$> decodeVersioned v - (3, 1) -> SnapOngoingMerge <$> decodeVersioned v <*> decodeVersioned v + (4, 0) -> SnapCompletedMerge <$> decodeVersioned v + <*> decodeVersioned v + <*> decodeVersioned v + (5, 1) -> SnapOngoingMerge <$> decodeVersioned v <*> decodeVersioned v + <*> decodeVersioned v <*> decodeVersioned v _ -> fail ("[SnapMergingRunState] Unexpected combination of list length and tag: " <> show (n, tag)) --- NominalCredits and MergeDebt +-- NominalDebt, NominalCredits, MergeDebt and MergeCredits + +instance Encode NominalDebt where + encode (NominalDebt x) = encodeInt x + +instance DecodeVersioned NominalDebt where + decodeVersioned V0 = NominalDebt <$> decodeInt instance Encode NominalCredits where encode (NominalCredits x) = encodeInt x @@ -556,6 +639,12 @@ instance Encode MergeDebt where instance DecodeVersioned MergeDebt where decodeVersioned V0 = (MergeDebt . MergeCredits) <$> decodeInt +instance Encode MergeCredits where + encode (MergeCredits x) = encodeInt x + +instance DecodeVersioned MergeCredits where + decodeVersioned V0 = MergeCredits <$> decodeInt + -- MergeType instance Encode MR.LevelMergeType where diff --git a/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs b/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs index 4568d900e..cde8f4fc4 100644 --- a/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs +++ b/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs @@ -17,6 +17,8 @@ import Database.LSMTree.Internal.Config import Database.LSMTree.Internal.Entry import Database.LSMTree.Internal.MergeSchedule import Database.LSMTree.Internal.MergingRun +import Database.LSMTree.Internal.RunBuilder (IndexType (..), + RunBloomFilterAlloc (..), RunDataCaching (..)) import Database.LSMTree.Internal.RunNumber import Database.LSMTree.Internal.Snapshot import Database.LSMTree.Internal.Snapshot.Codec @@ -170,8 +172,14 @@ testAll test = [ , test (Proxy @(SnapIncomingRun RunNumber)) , test (Proxy @NumRuns) , test (Proxy @MergePolicyForLevel) + , test (Proxy @RunDataCaching) + , test (Proxy @RunBloomFilterAlloc) + , test (Proxy @IndexType) + , test (Proxy @RunParams) , test (Proxy @(SnapMergingRunState LevelMergeType RunNumber)) , test (Proxy @MergeDebt) + , test (Proxy @MergeCredits) + , test (Proxy @NominalDebt) , test (Proxy @NominalCredits) , test (Proxy @LevelMergeType) , test (Proxy @TreeMergeType) @@ -277,13 +285,13 @@ deriving newtype instance Arbitrary RunNumber instance Arbitrary (SnapIncomingRun RunNumber) where arbitrary = oneof [ - SnapMergingRun <$> arbitrary <*> arbitrary <*> arbitrary + SnapMergingRun <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary , SnapSingleRun <$> arbitrary ] - shrink (SnapMergingRun a b c d e) = - [ SnapMergingRun a' b' c' d' e' - | (a', b', c', d', e') <- shrink (a, b, c, d, e) ] + shrink (SnapMergingRun a b c d) = + [ SnapMergingRun a' b' c' d' + | (a', b', c', d') <- shrink (a, b, c, d) ] shrink (SnapSingleRun a) = SnapSingleRun <$> shrink a deriving newtype instance Arbitrary NumRuns @@ -294,17 +302,52 @@ instance Arbitrary MergePolicyForLevel where instance Arbitrary t => Arbitrary (SnapMergingRunState t RunNumber) where arbitrary = oneof [ - SnapCompletedMerge <$> arbitrary + SnapCompletedMerge <$> arbitrary <*> arbitrary <*> arbitrary , SnapOngoingMerge <$> arbitrary <*> arbitrary + <*> arbitrary <*> arbitrary ] - shrink (SnapCompletedMerge x) = SnapCompletedMerge <$> shrink x - shrink (SnapOngoingMerge x y) = - [ SnapOngoingMerge x' y' | (x', y') <- shrink (x, y) ] + shrink (SnapCompletedMerge a b c) = + [ SnapCompletedMerge a' b' c' + | (a', b', c') <- shrink (a, b, c) ] + shrink (SnapOngoingMerge a b c d) = + [ SnapOngoingMerge a' b' c' d' + | (a', b', c', d') <- shrink (a, b, c, d) ] deriving newtype instance Arbitrary MergeDebt deriving newtype instance Arbitrary MergeCredits +deriving newtype instance Arbitrary NominalDebt deriving newtype instance Arbitrary NominalCredits +{------------------------------------------------------------------------------- + RunParams +-------------------------------------------------------------------------------} + +instance Arbitrary RunParams where + arbitrary = RunParams <$> arbitrary <*> arbitrary <*> arbitrary + shrink (RunParams a b c) = + [ RunParams a' b' c' + | (a', b', c') <- shrink (a, b, c) ] + +instance Arbitrary RunDataCaching where + arbitrary = elements [CacheRunData, NoCacheRunData] + shrink NoCacheRunData = [CacheRunData] + shrink _ = [] + +instance Arbitrary IndexType where + arbitrary = elements [Ordinary, Compact] + shrink Compact = [Ordinary] + shrink _ = [] + +instance Arbitrary RunBloomFilterAlloc where + arbitrary = oneof [ + RunAllocFixed <$> arbitrary + , RunAllocRequestFPR <$> arbitrary + , RunAllocMonkey <$> arbitrary + ] + shrink (RunAllocFixed x) = RunAllocFixed <$> shrink x + shrink (RunAllocRequestFPR x) = RunAllocRequestFPR <$> shrink x + shrink (RunAllocMonkey x) = RunAllocMonkey <$> shrink x + {------------------------------------------------------------------------------- Show -------------------------------------------------------------------------------} @@ -316,4 +359,5 @@ deriving stock instance Show r => Show (SnapIncomingRun r) deriving stock instance (Show t, Show r) => Show (SnapMergingRunState t r) deriving stock instance Show MergeDebt deriving stock instance Show MergeCredits +deriving stock instance Show NominalDebt deriving stock instance Show NominalCredits diff --git a/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs b/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs index bed9a431f..148c48219 100644 --- a/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs +++ b/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs @@ -15,9 +15,12 @@ import Database.LSMTree.Common (BloomFilterAlloc (..), import Database.LSMTree.Internal.Config (FencePointerIndex (..), MergePolicy (..), MergeSchedule (..), SizeRatio (..)) import Database.LSMTree.Internal.MergeSchedule - (MergePolicyForLevel (..), NominalCredits (..)) + (MergePolicyForLevel (..), NominalCredits (..), + NominalDebt (..)) import Database.LSMTree.Internal.MergingRun (NumRuns (..)) import qualified Database.LSMTree.Internal.MergingRun as MR +import Database.LSMTree.Internal.RunBuilder (IndexType (..), + RunBloomFilterAlloc (..), RunDataCaching (..)) import Database.LSMTree.Internal.RunNumber (RunNumber (..)) import Database.LSMTree.Internal.Snapshot import Database.LSMTree.Internal.Snapshot.Codec @@ -213,11 +216,10 @@ enumerateSnapIncomingRun = let inSnaps = [ (fuseAnnotations ["R1", a, b], - SnapMergingRun policy numRuns mergeDebt nominalCredits sState) + SnapMergingRun policy nominalDebt nominalCredits sState) | (a, policy ) <- [("P0", LevelTiering), ("P1", LevelLevelling)] - , numRuns <- NumRuns <$> [ magicNumber1 ] - , mergeDebt <- MR.MergeDebt <$> [ magicNumber2 ] - , nominalCredits <- NominalCredits <$> [ magicNumber1 ] + , nominalDebt <- NominalDebt <$> [ magicNumber2 ] + , nominalCredits <- NominalCredits <$> [ magicNumber1 ] , (b, sState ) <- enumerateSnapMergingRunState enumerateLevelMergeType ] in fold @@ -226,11 +228,19 @@ enumerateSnapIncomingRun = ] enumerateSnapMergingRunState :: - [(ComponentAnnotation, t)] -> [(ComponentAnnotation, SnapMergingRunState t RunNumber)] + [(ComponentAnnotation, t)] + -> [(ComponentAnnotation, SnapMergingRunState t RunNumber)] enumerateSnapMergingRunState mTypes = - (fuseAnnotations ["C0", blank, blank], SnapCompletedMerge enumerateRunNumbers) : - [ (fuseAnnotations ["C1", a, b], SnapOngoingMerge runVec mType) - | (a, runVec ) <- enumerateVectorRunNumber + [ (fuseAnnotations ["C0", blank, blank], + SnapCompletedMerge numRuns mergeDebt enumerateRunNumbers) + | numRuns <- NumRuns <$> [ magicNumber1 ] + , mergeDebt <- (MR.MergeDebt. MR.MergeCredits) <$> [ magicNumber2 ] + ] + ++ [ (fuseAnnotations ["C1", a, b], + SnapOngoingMerge runParams mergeCredits runVec mType) + | let runParams = enumerateRunParams + , mergeCredits <- MR.MergeCredits <$> [ magicNumber2 ] + , (a, runVec ) <- enumerateVectorRunNumber , (b, mType ) <- mTypes ] @@ -266,6 +276,16 @@ enumerateDiskCachePolicy = enumerateRunNumbers :: RunNumber enumerateRunNumbers = RunNumber magicNumber2 +--TODO: use a proper enumeration, but don't cause a combinatorial explosion. +enumerateRunParams :: MR.RunParams +enumerateRunParams = + MR.RunParams { + MR.runParamCaching = NoCacheRunData, + MR.runParamAlloc = RunAllocFixed 10, + MR.runParamIndex = Compact + } + + -- Randomly chosen numbers magicNumber1, magicNumber2, magicNumber3 :: Enum e => e magicNumber1 = toEnum 42 From 23c41e3d51f47f4b5995e1e5eb5bab92494d40f7 Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Fri, 7 Mar 2025 17:42:15 +0000 Subject: [PATCH 04/15] Change representation for snapshot of a run Again, the goal is to make the representation more self-contained so that less information needs to be supplied from the context during snapshot restore. And again the intnded use case is merging trees, and runs within them. In the case of a run it is the disk caching policy, and the index type. So overall for a run we store the run number (to find the disk files), the index type (so we know how to restore the index) and the caching policy. Arguably the index type should not need to be known in advance and the index file format should simply say which kind of index it is. --- src/Database/LSMTree/Internal.hs | 2 +- src/Database/LSMTree/Internal/Index.hs | 5 ++ src/Database/LSMTree/Internal/Run.hs | 27 ++++++++- src/Database/LSMTree/Internal/Snapshot.hs | 55 ++++++++++++------- .../LSMTree/Internal/Snapshot/Codec.hs | 45 ++++++++++----- .../LSMTree/Internal/Snapshot/Codec.hs | 26 ++++++--- .../LSMTree/Internal/Snapshot/Codec/Golden.hs | 39 ++++++++----- 7 files changed, 140 insertions(+), 59 deletions(-) diff --git a/src/Database/LSMTree/Internal.hs b/src/Database/LSMTree/Internal.hs index c80ee128b..7e5d34d1e 100644 --- a/src/Database/LSMTree/Internal.hs +++ b/src/Database/LSMTree/Internal.hs @@ -1308,7 +1308,7 @@ openSnapshot sesh label tableType override snap resolve = do (tableWriteBuffer, tableWriteBufferBlobs) <- openWriteBuffer reg resolve hfs hbio uc activeDir snapWriteBufferPaths -- Hard link runs into the active directory, - snapLevels' <- openRuns reg hfs hbio conf (sessionUniqCounter seshEnv) snapDir activeDir snapLevels + snapLevels' <- openRuns reg hfs hbio (sessionUniqCounter seshEnv) snapDir activeDir snapLevels -- Convert from the snapshot format, restoring merge progress in the process tableLevels <- fromSnapLevels reg hfs hbio conf (sessionUniqCounter seshEnv) resolve activeDir snapLevels' diff --git a/src/Database/LSMTree/Internal/Index.hs b/src/Database/LSMTree/Internal/Index.hs index 25f80773f..9fc68a5e7 100644 --- a/src/Database/LSMTree/Internal/Index.hs +++ b/src/Database/LSMTree/Internal/Index.hs @@ -25,6 +25,7 @@ module Database.LSMTree.Internal.Index ( -- * Index types IndexType (Compact, Ordinary), + indexToIndexType, -- * Indexes Index (CompactIndex, OrdinaryIndex), @@ -88,6 +89,10 @@ instance NFData Index where rnf (CompactIndex index) = rnf index rnf (OrdinaryIndex index) = rnf index +indexToIndexType :: Index -> IndexType +indexToIndexType CompactIndex{} = Compact +indexToIndexType OrdinaryIndex{} = Ordinary + {-| Searches for a page span that contains a key–value pair with the given key. If there is indeed such a pair, the result is the corresponding page span; diff --git a/src/Database/LSMTree/Internal/Run.hs b/src/Database/LSMTree/Internal/Run.hs index 982a76f64..c80775706 100644 --- a/src/Database/LSMTree/Internal/Run.hs +++ b/src/Database/LSMTree/Internal/Run.hs @@ -14,15 +14,18 @@ module Database.LSMTree.Internal.Run ( , sizeInPages , runFsPaths , runFsPathsNumber + , runDataCaching + , runIndexType , mkRawBlobRef , mkWeakBlobRef -- ** Run creation , fromMutable , fromWriteBuffer , RunParams (..) - , RunDataCaching (..) -- * Snapshot , openFromDisk + , RunDataCaching (..) + , IndexType (..) ) where import Control.DeepSeq (NFData (..), rwhnf) @@ -41,8 +44,8 @@ import qualified Database.LSMTree.Internal.BlobRef as BlobRef import Database.LSMTree.Internal.BloomFilter (bloomFilterFromSBS) import qualified Database.LSMTree.Internal.CRC32C as CRC import Database.LSMTree.Internal.Entry (NumEntries (..)) -import Database.LSMTree.Internal.Index (Index, IndexType) -import qualified Database.LSMTree.Internal.Index as Index (fromSBS, sizeInPages) +import Database.LSMTree.Internal.Index (Index, IndexType (..)) +import qualified Database.LSMTree.Internal.Index as Index import Database.LSMTree.Internal.Page (NumPages) import Database.LSMTree.Internal.Paths as Paths import Database.LSMTree.Internal.RunBuilder (RunBuilder, @@ -113,6 +116,15 @@ runFsPaths (DeRef r) = runRunFsPaths r runFsPathsNumber :: Ref (Run m h) -> RunNumber runFsPathsNumber = Paths.runNumber . runFsPaths +-- | See 'openFromDisk' +runIndexType :: Ref (Run m h) -> IndexType +runIndexType (DeRef r) = Index.indexToIndexType (runIndex r) + +-- | See 'openFromDisk' +runDataCaching :: Ref (Run m h) -> RunDataCaching +runDataCaching (DeRef r) = runRunDataCaching r + + -- | Helper function to make a 'WeakBlobRef' that points into a 'Run'. mkRawBlobRef :: Run m h -> BlobSpan -> RawBlobRef m h mkRawBlobRef Run{runBlobFile} blobspan = @@ -237,6 +249,15 @@ fromWriteBuffer fs hbio params fsPaths buffer blobs = do -- -- Exceptions will be raised when any of the file's contents don't match their -- checksum ('ChecksumError') or can't be parsed ('FileFormatError'). +-- +-- The 'RunDataCaching' and 'IndexType' parameters need to be saved and +-- restored separately because these are not stored in the on-disk +-- representation. Use 'runDataCaching' and 'runIndexType' to obtain these +-- parameters from the open run before persisting to disk. +-- +-- TODO: it may make more sense to persist these parameters with the run's +-- on-disk representation. +-- openFromDisk :: forall m h. (MonadSTM m, MonadMask m, PrimMonad m) diff --git a/src/Database/LSMTree/Internal/Snapshot.hs b/src/Database/LSMTree/Internal/Snapshot.hs index 524765602..544f71acd 100644 --- a/src/Database/LSMTree/Internal/Snapshot.hs +++ b/src/Database/LSMTree/Internal/Snapshot.hs @@ -14,6 +14,7 @@ module Database.LSMTree.Internal.Snapshot ( , snapshotWriteBuffer , openWriteBuffer -- * Runs + , SnapshotRun (..) , snapshotRuns , openRuns , releaseRuns @@ -110,7 +111,7 @@ data SnapshotMetaData = SnapshotMetaData { -- | The write buffer. , snapWriteBuffer :: !RunNumber -- | The shape of the levels of the LSM tree. - , snapMetaLevels :: !(SnapLevels RunNumber) + , snapMetaLevels :: !(SnapLevels SnapshotRun) } deriving stock Eq @@ -353,12 +354,28 @@ openWriteBuffer reg resolve hfs hbio uc activeDir snapWriteBufferPaths = do Runs -------------------------------------------------------------------------------} +-- | Information needed to open a 'Run' from disk using 'snapshotRuns' and +-- 'openRuns'. +-- +-- TODO: one could imagine needing only the 'RunNumber' to identify the files +-- on disk, and the other parameters being stored with the run itself, rather +-- than needing to be supplied. +data SnapshotRun = SnapshotRun { + snapRunNumber :: !RunNumber, + snapRunCaching :: !Run.RunDataCaching, + snapRunIndex :: !Run.IndexType + } + deriving stock Eq + +instance NFData SnapshotRun where + rnf (SnapshotRun a b c) = rnf a `seq` rnf b `seq` rnf c + {-# SPECIALISE snapshotRuns :: ActionRegistry IO -> UniqCounter IO -> NamedSnapshotDir -> SnapLevels (Ref (Run IO h)) - -> IO (SnapLevels RunNumber) #-} + -> IO (SnapLevels SnapshotRun) #-} -- | @'snapshotRuns' _ _ snapUc targetDir levels@ creates hard links for all run -- files associated with the runs in @levels@, and puts the new directory -- entries in the @targetDir@ directory. The entries are renamed using @snapUc@. @@ -368,7 +385,7 @@ snapshotRuns :: -> UniqCounter m -> NamedSnapshotDir -> SnapLevels (Ref (Run m h)) - -> m (SnapLevels RunNumber) + -> m (SnapLevels SnapshotRun) snapshotRuns reg snapUc (NamedSnapshotDir targetDir) levels = do for levels $ \run@(DeRef Run.Run { Run.runHasFS = hfs, @@ -378,17 +395,20 @@ snapshotRuns reg snapUc (NamedSnapshotDir targetDir) levels = do let sourcePaths = Run.runFsPaths run let targetPaths = sourcePaths { runDir = targetDir , runNumber = rn} hardLinkRunFiles reg hfs hbio sourcePaths targetPaths - pure (runNumber targetPaths) + pure SnapshotRun { + snapRunNumber = runNumber targetPaths, + snapRunCaching = Run.runDataCaching run, + snapRunIndex = Run.runIndexType run + } {-# SPECIALISE openRuns :: ActionRegistry IO -> HasFS IO h -> HasBlockIO IO h - -> TableConfig -> UniqCounter IO -> NamedSnapshotDir -> ActiveDir - -> SnapLevels RunNumber + -> SnapLevels SnapshotRun -> IO (SnapLevels (Ref (Run IO h))) #-} -- | @'openRuns' _ _ _ _ uniqCounter sourceDir targetDir levels@ takes all run -- files that are referenced by @levels@, and hard links them from @sourceDir@ @@ -402,23 +422,19 @@ openRuns :: => ActionRegistry m -> HasFS m h -> HasBlockIO m h - -> TableConfig -> UniqCounter m -> NamedSnapshotDir -> ActiveDir - -> SnapLevels RunNumber + -> SnapLevels SnapshotRun -> m (SnapLevels (Ref (Run m h))) -openRuns - reg hfs hbio TableConfig{..} uc - (NamedSnapshotDir sourceDir) (ActiveDir targetDir) (SnapLevels levels) = do - levels' <- - V.iforM levels $ \i level -> - let ln = LevelNo (i+1) in - let - caching = diskCachePolicyForLevel confDiskCachePolicy ln - indexType = indexTypeForRun confFencePointerIndex - in - for level $ \runNum -> do +openRuns reg hfs hbio uc (NamedSnapshotDir sourceDir) (ActiveDir targetDir) + levels = + for levels $ + \SnapshotRun { + snapRunNumber = runNum, + snapRunCaching = caching, + snapRunIndex = indexType + } -> do let sourcePaths = RunFsPaths sourceDir runNum runNum' <- uniqueToRunNumber <$> incrUniqCounter uc let targetPaths = RunFsPaths targetDir runNum' @@ -427,7 +443,6 @@ openRuns withRollback reg (Run.openFromDisk hfs hbio caching indexType targetPaths) releaseRef - pure (SnapLevels levels') {-# SPECIALISE releaseRuns :: ActionRegistry IO -> SnapLevels (Ref (Run IO h)) -> IO () diff --git a/src/Database/LSMTree/Internal/Snapshot/Codec.hs b/src/Database/LSMTree/Internal/Snapshot/Codec.hs index 994079fdd..fd1a58413 100644 --- a/src/Database/LSMTree/Internal/Snapshot/Codec.hs +++ b/src/Database/LSMTree/Internal/Snapshot/Codec.hs @@ -262,6 +262,25 @@ instance DecodeVersioned SnapshotTableType where 2 -> pure SnapFullTable _ -> fail ("[SnapshotTableType] Unexpected tag: " <> show tag) +instance Encode SnapshotRun where + encode SnapshotRun { snapRunNumber, snapRunCaching, snapRunIndex } = + encodeListLen 4 + <> encodeWord 0 + <> encode snapRunNumber + <> encode snapRunCaching + <> encode snapRunIndex + +instance DecodeVersioned SnapshotRun where + decodeVersioned v@V0 = do + n <- decodeListLen + tag <- decodeWord + case (n, tag) of + (4, 0) -> do snapRunNumber <- decodeVersioned v + snapRunCaching <- decodeVersioned v + snapRunIndex <- decodeVersioned v + pure SnapshotRun{..} + _ -> fail ("[SnapshotRun] Unexpected combination of list length and tag: " <> show (n, tag)) + {------------------------------------------------------------------------------- Encoding and decoding: TableConfig -------------------------------------------------------------------------------} @@ -499,38 +518,38 @@ instance DecodeVersioned MergeSchedule where -- SnapLevels -instance Encode (SnapLevels RunNumber) where +instance Encode r => Encode (SnapLevels r) where encode (SnapLevels levels) = encodeListLen (fromIntegral (V.length levels)) <> V.foldMap encode levels -instance DecodeVersioned (SnapLevels RunNumber) where +instance DecodeVersioned r => DecodeVersioned (SnapLevels r) where decodeVersioned v@V0 = do n <- decodeListLen SnapLevels <$> V.replicateM n (decodeVersioned v) -- SnapLevel -instance Encode (SnapLevel RunNumber) where +instance Encode r => Encode (SnapLevel r) where encode (SnapLevel incomingRuns residentRuns) = encodeListLen 2 <> encode incomingRuns <> encode residentRuns -instance DecodeVersioned (SnapLevel RunNumber) where +instance DecodeVersioned r => DecodeVersioned (SnapLevel r) where decodeVersioned v@V0 = do _ <- decodeListLenOf 2 SnapLevel <$> decodeVersioned v <*> decodeVersioned v --- Vector RunNumber +-- Vector -instance Encode (V.Vector RunNumber) where +instance Encode r => Encode (V.Vector r) where encode rns = encodeListLen (fromIntegral (V.length rns)) <> V.foldMap encode rns -instance DecodeVersioned (V.Vector RunNumber) where +instance DecodeVersioned r => DecodeVersioned (V.Vector r) where decodeVersioned v@V0 = do n <- decodeListLen V.replicateM n (decodeVersioned v) @@ -545,7 +564,7 @@ instance DecodeVersioned RunNumber where -- SnapIncomingRun -instance Encode (SnapIncomingRun RunNumber) where +instance Encode r => Encode (SnapIncomingRun r) where encode (SnapMergingRun mpfl nd nc smrs) = encodeListLen 5 <> encodeWord 0 @@ -558,7 +577,7 @@ instance Encode (SnapIncomingRun RunNumber) where <> encodeWord 1 <> encode x -instance DecodeVersioned (SnapIncomingRun RunNumber) where +instance DecodeVersioned r => DecodeVersioned (SnapIncomingRun r) where decodeVersioned v@V0 = do n <- decodeListLen tag <- decodeWord @@ -592,22 +611,22 @@ instance DecodeVersioned MergePolicyForLevel where -- SnapMergingRunState -instance Encode t => Encode (SnapMergingRunState t RunNumber) where +instance (Encode t, Encode r) => Encode (SnapMergingRunState t r) where encode (SnapCompletedMerge nr md r) = encodeListLen 4 <> encodeWord 0 <> encode nr <> encode md <> encode r - encode (SnapOngoingMerge ln mc rs mt) = + encode (SnapOngoingMerge rp mc rs mt) = encodeListLen 5 <> encodeWord 1 - <> encode ln + <> encode rp <> encode mc <> encode rs <> encode mt -instance DecodeVersioned t => DecodeVersioned (SnapMergingRunState t RunNumber) where +instance (DecodeVersioned t, DecodeVersioned r) => DecodeVersioned (SnapMergingRunState t r) where decodeVersioned v@V0 = do n <- decodeListLen tag <- decodeWord diff --git a/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs b/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs index cde8f4fc4..5bb055e10 100644 --- a/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs +++ b/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs @@ -154,6 +154,7 @@ testAll test = [ test (Proxy @SnapshotMetaData) , test (Proxy @SnapshotLabel) , test (Proxy @SnapshotTableType) + , test (Proxy @SnapshotRun) -- TableConfig , test (Proxy @TableConfig) , test (Proxy @MergePolicy) @@ -165,18 +166,18 @@ testAll test = [ , test (Proxy @DiskCachePolicy) , test (Proxy @MergeSchedule) -- SnapLevels - , test (Proxy @(SnapLevels RunNumber)) - , test (Proxy @(SnapLevel RunNumber)) - , test (Proxy @(V.Vector RunNumber)) + , test (Proxy @(SnapLevels SnapshotRun)) + , test (Proxy @(SnapLevel SnapshotRun)) + , test (Proxy @(V.Vector SnapshotRun)) , test (Proxy @RunNumber) - , test (Proxy @(SnapIncomingRun RunNumber)) + , test (Proxy @(SnapIncomingRun SnapshotRun)) , test (Proxy @NumRuns) , test (Proxy @MergePolicyForLevel) , test (Proxy @RunDataCaching) , test (Proxy @RunBloomFilterAlloc) , test (Proxy @IndexType) , test (Proxy @RunParams) - , test (Proxy @(SnapMergingRunState LevelMergeType RunNumber)) + , test (Proxy @(SnapMergingRunState LevelMergeType SnapshotRun)) , test (Proxy @MergeDebt) , test (Proxy @MergeCredits) , test (Proxy @NominalDebt) @@ -211,6 +212,12 @@ instance Arbitrary SnapshotTableType where arbitrary = elements [SnapNormalTable, SnapMonoidalTable] shrink _ = [] +instance Arbitrary SnapshotRun where + arbitrary = SnapshotRun <$> arbitrary <*> arbitrary <*> arbitrary + shrink (SnapshotRun a b c) = + [ SnapshotRun a' b' c' + | (a', b', c') <- shrink (a, b, c)] + {------------------------------------------------------------------------------- Arbitrary: TableConfig -------------------------------------------------------------------------------} @@ -266,13 +273,13 @@ instance Arbitrary MergeSchedule where Arbitrary: SnapLevels -------------------------------------------------------------------------------} -instance Arbitrary (SnapLevels RunNumber) where +instance Arbitrary r => Arbitrary (SnapLevels r) where arbitrary = do n <- chooseInt (0, 10) SnapLevels . V.fromList <$> vector n shrink (SnapLevels x) = SnapLevels . V.fromList <$> shrink (V.toList x) -instance Arbitrary (SnapLevel RunNumber) where +instance Arbitrary r => Arbitrary (SnapLevel r) where arbitrary = SnapLevel <$> arbitrary <*> arbitraryShortVector shrink (SnapLevel a b) = [SnapLevel a' b' | (a', b') <- shrink (a, b)] @@ -283,7 +290,7 @@ arbitraryShortVector = do deriving newtype instance Arbitrary RunNumber -instance Arbitrary (SnapIncomingRun RunNumber) where +instance Arbitrary r => Arbitrary (SnapIncomingRun r) where arbitrary = oneof [ SnapMergingRun <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary @@ -300,7 +307,7 @@ instance Arbitrary MergePolicyForLevel where arbitrary = elements [LevelTiering, LevelLevelling] shrink _ = [] -instance Arbitrary t => Arbitrary (SnapMergingRunState t RunNumber) where +instance (Arbitrary t, Arbitrary r) => Arbitrary (SnapMergingRunState t r) where arbitrary = oneof [ SnapCompletedMerge <$> arbitrary <*> arbitrary <*> arbitrary , SnapOngoingMerge <$> arbitrary <*> arbitrary @@ -353,6 +360,7 @@ instance Arbitrary RunBloomFilterAlloc where -------------------------------------------------------------------------------} deriving stock instance Show SnapshotMetaData +deriving stock instance Show SnapshotRun deriving stock instance Show r => Show (SnapLevels r) deriving stock instance Show r => Show (SnapLevel r) deriving stock instance Show r => Show (SnapIncomingRun r) diff --git a/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs b/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs index 148c48219..77c19644e 100644 --- a/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs +++ b/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs @@ -157,7 +157,7 @@ basicTableConfig = ( fuseAnnotations $ "T0" : replicate 4 blank, defaultTableCon basicRunNumber :: RunNumber basicRunNumber = enumerateRunNumbers -basicSnapLevels :: (ComponentAnnotation, SnapLevels RunNumber) +basicSnapLevels :: (ComponentAnnotation, SnapLevels SnapshotRun) basicSnapLevels = head enumerateSnapLevels {---------------- @@ -198,20 +198,20 @@ enumerateTableConfig = , (g, merge ) <- [("G0", OneShot), ("G1", Incremental)] ] -enumerateSnapLevels :: [(ComponentAnnotation, SnapLevels RunNumber)] +enumerateSnapLevels :: [(ComponentAnnotation, SnapLevels SnapshotRun)] enumerateSnapLevels = fmap (SnapLevels . V.singleton) <$> enumerateSnapLevel {---------------- Enumeration of SnapLevel sub-components ----------------} -enumerateSnapLevel :: [(ComponentAnnotation, SnapLevel RunNumber)] +enumerateSnapLevel :: [(ComponentAnnotation, SnapLevel SnapshotRun)] enumerateSnapLevel = do (a, run) <- enumerateSnapIncomingRun - (b, vec) <- enumerateVectorRunNumber + (b, vec) <- enumerateVectorRunInfo [( fuseAnnotations [ a, b ], SnapLevel run vec)] -enumerateSnapIncomingRun :: [(ComponentAnnotation, SnapIncomingRun RunNumber)] +enumerateSnapIncomingRun :: [(ComponentAnnotation, SnapIncomingRun SnapshotRun)] enumerateSnapIncomingRun = let inSnaps = @@ -223,16 +223,16 @@ enumerateSnapIncomingRun = , (b, sState ) <- enumerateSnapMergingRunState enumerateLevelMergeType ] in fold - [ [(fuseAnnotations $ "R0" : replicate 4 blank, SnapSingleRun enumerateRunNumbers)] + [ [(fuseAnnotations $ "R0" : replicate 4 blank, SnapSingleRun enumerateOpenRunInfo)] , inSnaps ] enumerateSnapMergingRunState :: [(ComponentAnnotation, t)] - -> [(ComponentAnnotation, SnapMergingRunState t RunNumber)] + -> [(ComponentAnnotation, SnapMergingRunState t SnapshotRun)] enumerateSnapMergingRunState mTypes = [ (fuseAnnotations ["C0", blank, blank], - SnapCompletedMerge numRuns mergeDebt enumerateRunNumbers) + SnapCompletedMerge numRuns mergeDebt enumerateOpenRunInfo) | numRuns <- NumRuns <$> [ magicNumber1 ] , mergeDebt <- (MR.MergeDebt. MR.MergeCredits) <$> [ magicNumber2 ] ] @@ -240,7 +240,7 @@ enumerateSnapMergingRunState mTypes = SnapOngoingMerge runParams mergeCredits runVec mType) | let runParams = enumerateRunParams , mergeCredits <- MR.MergeCredits <$> [ magicNumber2 ] - , (a, runVec ) <- enumerateVectorRunNumber + , (a, runVec ) <- enumerateVectorRunInfo , (b, mType ) <- mTypes ] @@ -248,11 +248,14 @@ enumerateLevelMergeType :: [(ComponentAnnotation, MR.LevelMergeType)] enumerateLevelMergeType = [("L0", MR.MergeMidLevel), ("L1", MR.MergeLastLevel)] -enumerateVectorRunNumber :: [(ComponentAnnotation, Vector RunNumber)] -enumerateVectorRunNumber = +enumerateVectorRunInfo :: [(ComponentAnnotation, Vector SnapshotRun)] +enumerateVectorRunInfo = [ ("V0", mempty) - , ("V1", V.fromList [RunNumber magicNumber1]) - , ("V2", V.fromList [RunNumber magicNumber1, RunNumber magicNumber2 ]) + , ("V1", V.fromList [enumerateOpenRunInfo]) + , ("V2", V.fromList [enumerateOpenRunInfo, + enumerateOpenRunInfo { + snapRunNumber = RunNumber magicNumber2 + } ]) ] {---------------- @@ -285,6 +288,16 @@ enumerateRunParams = MR.runParamIndex = Compact } +--TODO: use a proper enumeration, but don't cause a combinatorial explosion of +-- golden tests. Perhaps do all combos as a direct golden test, but then where +-- it is embedded, just use one combo. +enumerateOpenRunInfo :: SnapshotRun +enumerateOpenRunInfo = + SnapshotRun { + snapRunNumber = enumerateRunNumbers, + snapRunCaching = CacheRunData, + snapRunIndex = Compact + } -- Randomly chosen numbers magicNumber1, magicNumber2, magicNumber3 :: Enum e => e From e61a430c71b665715a183ec8c69623fdff13b3e0 Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Fri, 7 Mar 2025 18:02:33 +0000 Subject: [PATCH 05/15] Make snapshotRun/openRun singluar on Run rather than Levels It is just a simple `traverse` to lift it to Levels. This makes it simpler to reuse with merging trees of runs, which can also just use `traverse`. --- src/Database/LSMTree/Internal.hs | 8 +- src/Database/LSMTree/Internal/Snapshot.hs | 133 ++++++++++------------ 2 files changed, 65 insertions(+), 76 deletions(-) diff --git a/src/Database/LSMTree/Internal.hs b/src/Database/LSMTree/Internal.hs index 7e5d34d1e..8f69c98aa 100644 --- a/src/Database/LSMTree/Internal.hs +++ b/src/Database/LSMTree/Internal.hs @@ -1240,7 +1240,7 @@ createSnapshot snap label tableType t = do snapLevels <- toSnapLevels (tableLevels content) -- Hard link runs into the named snapshot directory - snapLevels' <- snapshotRuns reg snapUc snapDir snapLevels + snapLevels' <- traverse (snapshotRun hfs hbio snapUc reg snapDir) snapLevels -- Release the table content releaseTableContent reg content @@ -1308,11 +1308,11 @@ openSnapshot sesh label tableType override snap resolve = do (tableWriteBuffer, tableWriteBufferBlobs) <- openWriteBuffer reg resolve hfs hbio uc activeDir snapWriteBufferPaths -- Hard link runs into the active directory, - snapLevels' <- openRuns reg hfs hbio (sessionUniqCounter seshEnv) snapDir activeDir snapLevels + snapLevels' <- traverse (openRun hfs hbio uc reg snapDir activeDir) snapLevels -- Convert from the snapshot format, restoring merge progress in the process - tableLevels <- fromSnapLevels reg hfs hbio conf (sessionUniqCounter seshEnv) resolve activeDir snapLevels' - releaseRuns reg snapLevels' + tableLevels <- fromSnapLevels reg hfs hbio conf uc resolve activeDir snapLevels' + traverse_ (delayedCommit reg . releaseRef) snapLevels' tableCache <- mkLevelsCache reg tableLevels newWith reg sesh seshEnv conf' am $! TableContent { diff --git a/src/Database/LSMTree/Internal/Snapshot.hs b/src/Database/LSMTree/Internal/Snapshot.hs index 544f71acd..9879bf81e 100644 --- a/src/Database/LSMTree/Internal/Snapshot.hs +++ b/src/Database/LSMTree/Internal/Snapshot.hs @@ -13,11 +13,10 @@ module Database.LSMTree.Internal.Snapshot ( -- * Write buffer , snapshotWriteBuffer , openWriteBuffer - -- * Runs + -- * Run , SnapshotRun (..) - , snapshotRuns - , openRuns - , releaseRuns + , snapshotRun + , openRun -- * Opening from levels snapshot format , fromSnapLevels -- * Hard links @@ -33,9 +32,8 @@ import Control.Monad.Class.MonadST (MonadST) import Control.Monad.Class.MonadThrow (MonadMask, bracketOnError) import Control.Monad.Primitive (PrimMonad) import Control.RefCount -import Data.Foldable (sequenceA_, traverse_) +import Data.Foldable (sequenceA_) import Data.Text (Text) -import Data.Traversable (for) import qualified Data.Vector as V import Database.LSMTree.Internal.Config import Database.LSMTree.Internal.CRC32C (checkCRC) @@ -354,8 +352,8 @@ openWriteBuffer reg resolve hfs hbio uc activeDir snapWriteBufferPaths = do Runs -------------------------------------------------------------------------------} --- | Information needed to open a 'Run' from disk using 'snapshotRuns' and --- 'openRuns'. +-- | Information needed to open a 'Run' from disk using 'snapshotRun' and +-- 'openRun'. -- -- TODO: one could imagine needing only the 'RunNumber' to identify the files -- on disk, and the other parameters being stored with the run itself, rather @@ -370,87 +368,78 @@ data SnapshotRun = SnapshotRun { instance NFData SnapshotRun where rnf (SnapshotRun a b c) = rnf a `seq` rnf b `seq` rnf c -{-# SPECIALISE snapshotRuns :: - ActionRegistry IO +{-# SPECIALISE snapshotRun :: + HasFS IO h + -> HasBlockIO IO h -> UniqCounter IO + -> ActionRegistry IO -> NamedSnapshotDir - -> SnapLevels (Ref (Run IO h)) - -> IO (SnapLevels SnapshotRun) #-} --- | @'snapshotRuns' _ _ snapUc targetDir levels@ creates hard links for all run --- files associated with the runs in @levels@, and puts the new directory --- entries in the @targetDir@ directory. The entries are renamed using @snapUc@. -snapshotRuns :: + -> Ref (Run IO h) + -> IO SnapshotRun #-} +-- | @'snapshotRun' _ _ snapUc targetDir run@ creates hard links for all files +-- associated with the run, and puts the new directory entries in the +-- @targetDir@ directory. The entries are renamed using @snapUc@. +snapshotRun :: (MonadMask m, PrimMonad m) - => ActionRegistry m + => HasFS m h + -> HasBlockIO m h -> UniqCounter m + -> ActionRegistry m -> NamedSnapshotDir - -> SnapLevels (Ref (Run m h)) - -> m (SnapLevels SnapshotRun) -snapshotRuns reg snapUc (NamedSnapshotDir targetDir) levels = do - for levels $ \run@(DeRef Run.Run { - Run.runHasFS = hfs, - Run.runHasBlockIO = hbio - }) -> do - rn <- uniqueToRunNumber <$> incrUniqCounter snapUc - let sourcePaths = Run.runFsPaths run - let targetPaths = sourcePaths { runDir = targetDir , runNumber = rn} - hardLinkRunFiles reg hfs hbio sourcePaths targetPaths - pure SnapshotRun { - snapRunNumber = runNumber targetPaths, - snapRunCaching = Run.runDataCaching run, - snapRunIndex = Run.runIndexType run - } - -{-# SPECIALISE openRuns :: - ActionRegistry IO - -> HasFS IO h + -> Ref (Run m h) + -> m SnapshotRun +snapshotRun hfs hbio snapUc reg (NamedSnapshotDir targetDir) run = do + rn <- uniqueToRunNumber <$> incrUniqCounter snapUc + let sourcePaths = Run.runFsPaths run + let targetPaths = sourcePaths { runDir = targetDir , runNumber = rn} + hardLinkRunFiles reg hfs hbio sourcePaths targetPaths + pure SnapshotRun { + snapRunNumber = runNumber targetPaths, + snapRunCaching = Run.runDataCaching run, + snapRunIndex = Run.runIndexType run + } + +{-# SPECIALISE openRun :: + HasFS IO h -> HasBlockIO IO h -> UniqCounter IO + -> ActionRegistry IO -> NamedSnapshotDir -> ActiveDir - -> SnapLevels SnapshotRun - -> IO (SnapLevels (Ref (Run IO h))) #-} --- | @'openRuns' _ _ _ _ uniqCounter sourceDir targetDir levels@ takes all run --- files that are referenced by @levels@, and hard links them from @sourceDir@ + -> SnapshotRun + -> IO (Ref (Run IO h)) #-} +-- | @'openRun' _ _ _ _ uniqCounter sourceDir targetDir snaprun@ takes all run +-- files that are referenced by @snaprun@, and hard links them from @sourceDir@ -- into @targetDir@ with new, unique names (using @uniqCounter@). Each set of -- (hard linked) files that represents a run is opened and verified, returning --- 'Run's as a result. +-- 'Run' as a result. -- --- The result must ultimately be released using 'releaseRuns'. -openRuns :: +-- The result must ultimately be released using 'releaseRef'. +openRun :: (MonadMask m, MonadSTM m, MonadST m) - => ActionRegistry m - -> HasFS m h + => HasFS m h -> HasBlockIO m h -> UniqCounter m + -> ActionRegistry m -> NamedSnapshotDir -> ActiveDir - -> SnapLevels SnapshotRun - -> m (SnapLevels (Ref (Run m h))) -openRuns reg hfs hbio uc (NamedSnapshotDir sourceDir) (ActiveDir targetDir) - levels = - for levels $ - \SnapshotRun { - snapRunNumber = runNum, - snapRunCaching = caching, - snapRunIndex = indexType - } -> do - let sourcePaths = RunFsPaths sourceDir runNum - runNum' <- uniqueToRunNumber <$> incrUniqCounter uc - let targetPaths = RunFsPaths targetDir runNum' - hardLinkRunFiles reg hfs hbio sourcePaths targetPaths - - withRollback reg - (Run.openFromDisk hfs hbio caching indexType targetPaths) - releaseRef - -{-# SPECIALISE releaseRuns :: - ActionRegistry IO -> SnapLevels (Ref (Run IO h)) -> IO () - #-} -releaseRuns :: - (MonadMask m, MonadST m) - => ActionRegistry m -> SnapLevels (Ref (Run m h)) -> m () -releaseRuns reg = traverse_ $ \r -> delayedCommit reg (releaseRef r) + -> SnapshotRun + -> m (Ref (Run m h)) +openRun hfs hbio uc reg + (NamedSnapshotDir sourceDir) (ActiveDir targetDir) + SnapshotRun { + snapRunNumber = runNum, + snapRunCaching = caching, + snapRunIndex = indexType + } = do + let sourcePaths = RunFsPaths sourceDir runNum + runNum' <- uniqueToRunNumber <$> incrUniqCounter uc + let targetPaths = RunFsPaths targetDir runNum' + hardLinkRunFiles reg hfs hbio sourcePaths targetPaths + + withRollback reg + (Run.openFromDisk hfs hbio caching indexType targetPaths) + releaseRef {------------------------------------------------------------------------------- Opening from levels snapshot format From 8a39751fabd45c8f89f9cdc51fe75e86b21bd7c1 Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Fri, 7 Mar 2025 18:11:02 +0000 Subject: [PATCH 06/15] Make function argument order more consistent in snapshot functions Put hfs hbio first since that's more global and what we use elsewhere. Roughly in order of lest frequently varying to most frequently varying, or equivalently most global to most local. --- src/Database/LSMTree/Internal.hs | 4 +- src/Database/LSMTree/Internal/Snapshot.hs | 82 +++++++++++----------- test/Test/Database/LSMTree/Internal/Run.hs | 2 +- 3 files changed, 43 insertions(+), 45 deletions(-) diff --git a/src/Database/LSMTree/Internal.hs b/src/Database/LSMTree/Internal.hs index 8f69c98aa..2082b3c41 100644 --- a/src/Database/LSMTree/Internal.hs +++ b/src/Database/LSMTree/Internal.hs @@ -1234,7 +1234,7 @@ createSnapshot snap label tableType t = do let wb = tableWriteBuffer content let wbb = tableWriteBufferBlobs content snapWriteBufferNumber <- Paths.writeBufferNumber <$> - snapshotWriteBuffer reg hfs hbio activeUc snapUc activeDir snapDir wb wbb + snapshotWriteBuffer hfs hbio activeUc snapUc reg activeDir snapDir wb wbb -- Convert to snapshot format snapLevels <- toSnapLevels (tableLevels content) @@ -1311,7 +1311,7 @@ openSnapshot sesh label tableType override snap resolve = do snapLevels' <- traverse (openRun hfs hbio uc reg snapDir activeDir) snapLevels -- Convert from the snapshot format, restoring merge progress in the process - tableLevels <- fromSnapLevels reg hfs hbio conf uc resolve activeDir snapLevels' + tableLevels <- fromSnapLevels hfs hbio uc conf resolve reg activeDir snapLevels' traverse_ (delayedCommit reg . releaseRef) snapLevels' tableCache <- mkLevelsCache reg tableLevels diff --git a/src/Database/LSMTree/Internal/Snapshot.hs b/src/Database/LSMTree/Internal/Snapshot.hs index 9879bf81e..6eae12c98 100644 --- a/src/Database/LSMTree/Internal/Snapshot.hs +++ b/src/Database/LSMTree/Internal/Snapshot.hs @@ -253,11 +253,11 @@ toSnapMergingRunState !mr = do {-# SPECIALISE snapshotWriteBuffer :: - ActionRegistry IO - -> HasFS IO h + HasFS IO h -> HasBlockIO IO h -> UniqCounter IO -> UniqCounter IO + -> ActionRegistry IO -> ActiveDir -> NamedSnapshotDir -> WriteBuffer @@ -266,17 +266,17 @@ toSnapMergingRunState !mr = do #-} snapshotWriteBuffer :: (MonadMVar m, MonadSTM m, MonadST m, MonadMask m) - => ActionRegistry m - -> HasFS m h + => HasFS m h -> HasBlockIO m h -> UniqCounter m -> UniqCounter m + -> ActionRegistry m -> ActiveDir -> NamedSnapshotDir -> WriteBuffer -> Ref (WriteBufferBlobs m h) -> m WriteBufferFsPaths -snapshotWriteBuffer reg hfs hbio activeUc snapUc activeDir snapDir wb wbb = do +snapshotWriteBuffer hfs hbio activeUc snapUc reg activeDir snapDir wb wbb = do -- Write the write buffer and write buffer blobs to the active directory. activeWriteBufferNumber <- uniqueToRunNumber <$> incrUniqCounter activeUc let activeWriteBufferPaths = WriteBufferFsPaths (getActiveDir activeDir) activeWriteBufferNumber @@ -291,13 +291,13 @@ snapshotWriteBuffer reg hfs hbio activeUc snapUc activeDir snapDir wb wbb = do -- Hard link the write buffer and write buffer blobs to the snapshot directory. snapWriteBufferNumber <- uniqueToRunNumber <$> incrUniqCounter snapUc let snapWriteBufferPaths = WriteBufferFsPaths (getNamedSnapshotDir snapDir) snapWriteBufferNumber - hardLink reg hfs hbio + hardLink hfs hbio reg (writeBufferKOpsPath activeWriteBufferPaths) (writeBufferKOpsPath snapWriteBufferPaths) - hardLink reg hfs hbio + hardLink hfs hbio reg (writeBufferBlobPath activeWriteBufferPaths) (writeBufferBlobPath snapWriteBufferPaths) - hardLink reg hfs hbio + hardLink hfs hbio reg (writeBufferChecksumsPath activeWriteBufferPaths) (writeBufferChecksumsPath snapWriteBufferPaths) pure snapWriteBufferPaths @@ -336,7 +336,7 @@ openWriteBuffer reg resolve hfs hbio uc activeDir snapWriteBufferPaths = do activeWriteBufferNumber <- uniqueToInt <$> incrUniqCounter uc let activeWriteBufferBlobPath = getActiveDir activeDir FS.mkFsPath [show activeWriteBufferNumber] <.> "wbblobs" - copyFile reg hfs hbio (writeBufferBlobPath snapWriteBufferPaths) activeWriteBufferBlobPath + copyFile hfs reg (writeBufferBlobPath snapWriteBufferPaths) activeWriteBufferBlobPath writeBufferBlobs <- withRollback reg (WBB.open hfs activeWriteBufferBlobPath FS.AllowExisting) @@ -376,8 +376,8 @@ instance NFData SnapshotRun where -> NamedSnapshotDir -> Ref (Run IO h) -> IO SnapshotRun #-} --- | @'snapshotRun' _ _ snapUc targetDir run@ creates hard links for all files --- associated with the run, and puts the new directory entries in the +-- | @'snapshotRun' _ _ snapUc _ targetDir run@ creates hard links for all files +-- associated with the @run@, and puts the new directory entries in the -- @targetDir@ directory. The entries are renamed using @snapUc@. snapshotRun :: (MonadMask m, PrimMonad m) @@ -392,7 +392,7 @@ snapshotRun hfs hbio snapUc reg (NamedSnapshotDir targetDir) run = do rn <- uniqueToRunNumber <$> incrUniqCounter snapUc let sourcePaths = Run.runFsPaths run let targetPaths = sourcePaths { runDir = targetDir , runNumber = rn} - hardLinkRunFiles reg hfs hbio sourcePaths targetPaths + hardLinkRunFiles hfs hbio reg sourcePaths targetPaths pure SnapshotRun { snapRunNumber = runNumber targetPaths, snapRunCaching = Run.runDataCaching run, @@ -408,7 +408,7 @@ snapshotRun hfs hbio snapUc reg (NamedSnapshotDir targetDir) run = do -> ActiveDir -> SnapshotRun -> IO (Ref (Run IO h)) #-} --- | @'openRun' _ _ _ _ uniqCounter sourceDir targetDir snaprun@ takes all run +-- | @'openRun' _ _ uniqCounter _ sourceDir targetDir snaprun@ takes all run -- files that are referenced by @snaprun@, and hard links them from @sourceDir@ -- into @targetDir@ with new, unique names (using @uniqCounter@). Each set of -- (hard linked) files that represents a run is opened and verified, returning @@ -435,7 +435,7 @@ openRun hfs hbio uc reg let sourcePaths = RunFsPaths sourceDir runNum runNum' <- uniqueToRunNumber <$> incrUniqCounter uc let targetPaths = RunFsPaths targetDir runNum' - hardLinkRunFiles reg hfs hbio sourcePaths targetPaths + hardLinkRunFiles hfs hbio reg sourcePaths targetPaths withRollback reg (Run.openFromDisk hfs hbio caching indexType targetPaths) @@ -446,12 +446,12 @@ openRun hfs hbio uc reg -------------------------------------------------------------------------------} {-# SPECIALISE fromSnapLevels :: - ActionRegistry IO - -> HasFS IO h + HasFS IO h -> HasBlockIO IO h - -> TableConfig -> UniqCounter IO + -> TableConfig -> ResolveSerialisedValue + -> ActionRegistry IO -> ActiveDir -> SnapLevels (Ref (Run IO h)) -> IO (Levels IO h) @@ -459,16 +459,16 @@ openRun hfs hbio uc reg -- | Duplicates runs and re-creates merging runs. fromSnapLevels :: forall m h. (MonadMask m, MonadMVar m, MonadSTM m, MonadST m) - => ActionRegistry m - -> HasFS m h + => HasFS m h -> HasBlockIO m h - -> TableConfig -> UniqCounter m + -> TableConfig -> ResolveSerialisedValue + -> ActionRegistry m -> ActiveDir -> SnapLevels (Ref (Run m h)) -> m (Levels m h) -fromSnapLevels reg hfs hbio conf uc resolve dir (SnapLevels levels) = +fromSnapLevels hfs hbio uc conf resolve reg dir (SnapLevels levels) = V.iforM levels $ \i -> fromSnapLevel (LevelNo (i+1)) where -- TODO: we may wish to trace the merges created during snapshot restore: @@ -546,9 +546,9 @@ fromSnapMergingRunState hfs hbio uc resolve dir -------------------------------------------------------------------------------} {-# SPECIALISE hardLinkRunFiles :: - ActionRegistry IO - -> HasFS IO h + HasFS IO h -> HasBlockIO IO h + -> ActionRegistry IO -> RunFsPaths -> RunFsPaths -> IO () #-} @@ -557,38 +557,38 @@ fromSnapMergingRunState hfs hbio uc resolve dir -- name for the new directory entry. hardLinkRunFiles :: (MonadMask m, PrimMonad m) - => ActionRegistry m - -> HasFS m h + => HasFS m h -> HasBlockIO m h + -> ActionRegistry m -> RunFsPaths -> RunFsPaths -> m () -hardLinkRunFiles reg hfs hbio sourceRunFsPaths targetRunFsPaths = do +hardLinkRunFiles hfs hbio reg sourceRunFsPaths targetRunFsPaths = do let sourcePaths = pathsForRunFiles sourceRunFsPaths targetPaths = pathsForRunFiles targetRunFsPaths - sequenceA_ (hardLink reg hfs hbio <$> sourcePaths <*> targetPaths) - hardLink reg hfs hbio (runChecksumsPath sourceRunFsPaths) (runChecksumsPath targetRunFsPaths) + sequenceA_ (hardLink hfs hbio reg <$> sourcePaths <*> targetPaths) + hardLink hfs hbio reg (runChecksumsPath sourceRunFsPaths) (runChecksumsPath targetRunFsPaths) {-# SPECIALISE hardLink :: - ActionRegistry IO - -> HasFS IO h + HasFS IO h -> HasBlockIO IO h + -> ActionRegistry IO -> FS.FsPath -> FS.FsPath -> IO () #-} --- | @'hardLink' reg hfs hbio sourcePath targetPath@ creates a hard link from +-- | @'hardLink' hfs hbio reg sourcePath targetPath@ creates a hard link from -- @sourcePath@ to @targetPath@. hardLink :: (MonadMask m, PrimMonad m) - => ActionRegistry m - -> HasFS m h + => HasFS m h -> HasBlockIO m h + -> ActionRegistry m -> FS.FsPath -> FS.FsPath -> m () -hardLink reg hfs hbio sourcePath targetPath = do +hardLink hfs hbio reg sourcePath targetPath = do withRollback_ reg (FS.createHardLink hbio sourcePath targetPath) (FS.removeFile hfs targetPath) @@ -599,23 +599,21 @@ hardLink reg hfs hbio sourcePath targetPath = do {-# SPECIALISE copyFile :: - ActionRegistry IO - -> HasFS IO h - -> HasBlockIO IO h + HasFS IO h + -> ActionRegistry IO -> FS.FsPath -> FS.FsPath -> IO () #-} --- | @'copyFile' reg hfs hbio source target@ copies the @source@ path to the @target@ path. +-- | @'copyFile' hfs reg source target@ copies the @source@ path to the @target@ path. copyFile :: (MonadMask m, PrimMonad m) - => ActionRegistry m - -> HasFS m h - -> HasBlockIO m h + => HasFS m h + -> ActionRegistry m -> FS.FsPath -> FS.FsPath -> m () -copyFile reg hfs _hbio sourcePath targetPath = +copyFile hfs reg sourcePath targetPath = flip (withRollback_ reg) (FS.removeFile hfs targetPath) $ FS.withFile hfs sourcePath FS.ReadMode $ \sourceHandle -> FS.withFile hfs targetPath (FS.WriteMode FS.MustBeNew) $ \targetHandle -> do diff --git a/test/Test/Database/LSMTree/Internal/Run.hs b/test/Test/Database/LSMTree/Internal/Run.hs index a42360b49..db4a670ab 100644 --- a/test/Test/Database/LSMTree/Internal/Run.hs +++ b/test/Test/Database/LSMTree/Internal/Run.hs @@ -201,7 +201,7 @@ prop_WriteAndOpen fs hbio wb = withActionRegistry $ \reg -> do let paths = Run.runFsPaths written paths' = paths { runNumber = RunNumber 17} - hardLinkRunFiles reg fs hbio paths paths' + hardLinkRunFiles fs hbio reg paths paths' loaded <- openFromDisk fs hbio CacheRunData Index.Compact (simplePath 17) Run.size written @=? Run.size loaded From 2db52746f66e2b2a039fb2417990cf94ea3b493e Mon Sep 17 00:00:00 2001 From: Recursion Ninja Date: Thu, 27 Feb 2025 17:07:34 -0500 Subject: [PATCH 07/15] Add MergingTree to the snapshot representation and functions The union level contains an optional MergingTree. This is now included in the snapshot representation. Update codecs and tests. This changes the snapshot file format. The golden test files are updated in the next patch. Co-authored-by: Duncan Coutts Co-authored-by: Joris Dral --- src/Database/LSMTree/Internal.hs | 32 +++- src/Database/LSMTree/Internal/MergingTree.hs | 12 +- src/Database/LSMTree/Internal/Snapshot.hs | 168 +++++++++++++++++- .../LSMTree/Internal/Snapshot/Codec.hs | 120 ++++++++++++- .../LSMTree/Internal/Snapshot/Codec.hs | 86 ++++++++- .../LSMTree/Internal/Snapshot/Codec/Golden.hs | 89 +++++++++- 6 files changed, 480 insertions(+), 27 deletions(-) diff --git a/src/Database/LSMTree/Internal.hs b/src/Database/LSMTree/Internal.hs index 2082b3c41..1f0b2efc4 100644 --- a/src/Database/LSMTree/Internal.hs +++ b/src/Database/LSMTree/Internal.hs @@ -64,7 +64,7 @@ module Database.LSMTree.Internal ( , openSnapshot , deleteSnapshot , listSnapshots - -- * Mutiple writable tables + -- * Multiple writable tables , duplicate -- * Table union , unions @@ -323,7 +323,7 @@ data SessionState m h = | SessionClosed data SessionEnv m h = SessionEnv { - -- | The path to the directory in which this sesion is live. This is a path + -- | The path to the directory in which this session is live. This is a path -- relative to root of the 'HasFS' instance. -- -- INVARIANT: the session root is never changed during the lifetime of a @@ -1242,10 +1242,22 @@ createSnapshot snap label tableType t = do -- Hard link runs into the named snapshot directory snapLevels' <- traverse (snapshotRun hfs hbio snapUc reg snapDir) snapLevels - -- Release the table content + -- If a merging tree exists, do the same hard-linking for the runs within + mTreeOpt <- case tableUnionLevel content of + NoUnion -> pure Nothing + Union mTreeRef -> do + mTree <- toSnapMergingTree mTreeRef + Just <$> traverse (snapshotRun hfs hbio snapUc reg snapDir) mTree + releaseTableContent reg content - let snapMetaData = SnapshotMetaData label tableType (tableConfig t) snapWriteBufferNumber snapLevels' + let snapMetaData = SnapshotMetaData + label + tableType + (tableConfig t) + snapWriteBufferNumber + snapLevels' + mTreeOpt SnapshotMetaDataFile contentPath = Paths.snapshotMetaDataFile snapDir SnapshotMetaDataChecksumFile checksumPath = Paths.snapshotMetaDataChecksumFile snapDir writeFileSnapshotMetaData hfs contentPath checksumPath snapMetaData @@ -1290,7 +1302,7 @@ openSnapshot sesh label tableType override snap resolve = do Left e -> throwIO (ErrSnapshotDeserialiseFailure e snap) Right x -> pure x - let SnapshotMetaData label' tableType' conf snapWriteBuffer snapLevels = snapMetaData + let SnapshotMetaData label' tableType' conf snapWriteBuffer snapLevels mTreeOpt = snapMetaData unless (tableType == tableType') $ throwIO (ErrSnapshotWrongTableType snap tableType tableType') @@ -1309,10 +1321,16 @@ openSnapshot sesh label tableType override snap resolve = do -- Hard link runs into the active directory, snapLevels' <- traverse (openRun hfs hbio uc reg snapDir activeDir) snapLevels + unionLevel <- case mTreeOpt of + Nothing -> pure NoUnion + Just mTree -> do + snapTree <- traverse (openRun hfs hbio uc reg snapDir activeDir) mTree + Union <$> fromSnapMergingTree reg hfs hbio conf uc resolve activeDir snapTree -- Convert from the snapshot format, restoring merge progress in the process tableLevels <- fromSnapLevels hfs hbio uc conf resolve reg activeDir snapLevels' traverse_ (delayedCommit reg . releaseRef) snapLevels' + --TODO: also delayedCommit unionLevel tableCache <- mkLevelsCache reg tableLevels newWith reg sesh seshEnv conf' am $! TableContent { @@ -1320,7 +1338,7 @@ openSnapshot sesh label tableType override snap resolve = do , tableWriteBufferBlobs , tableLevels , tableCache - , tableUnionLevel = NoUnion -- TODO: at some point also load union level from snapshot + , tableUnionLevel = unionLevel } {-# SPECIALISE deleteSnapshot :: @@ -1370,7 +1388,7 @@ listSnapshots sesh = do else pure $ Nothing {------------------------------------------------------------------------------- - Mutiple writable tables + Multiple writable tables -------------------------------------------------------------------------------} {-# SPECIALISE duplicate :: Table IO h -> IO (Table IO h) #-} diff --git a/src/Database/LSMTree/Internal/MergingTree.hs b/src/Database/LSMTree/Internal/MergingTree.hs index fbcc7012e..e37ac87c2 100644 --- a/src/Database/LSMTree/Internal/MergingTree.hs +++ b/src/Database/LSMTree/Internal/MergingTree.hs @@ -2,6 +2,7 @@ module Database.LSMTree.Internal.MergingTree ( -- $mergingtrees MergingTree (..) + , newOngoingMerge , PreExistingRun (..) , newPendingLevelMerge , newPendingUnionMerge @@ -102,6 +103,15 @@ data PreExistingRun m h = PreExistingRun !(Ref (Run m h)) | PreExistingMergingRun !(Ref (MergingRun MR.LevelMergeType m h)) +-- | Create a new 'MergingTree' representing the merge of an ongoing run. +-- The usage of this function is primarily to facilitate the reloading of an +-- ongoing merge from a persistent snapshot. +newOngoingMerge :: + (MonadMVar m, PrimMonad m, MonadMask m) + => Ref (MergingRun MR.TreeMergeType m h) + -> m (Ref (MergingTree m h)) +newOngoingMerge = mkMergingTree . OngoingTreeMerge + -- | Create a new 'MergingTree' representing the merge of a sequence of -- pre-existing runs (completed or ongoing, plus a optional final tree). -- This is for merging the entire contents of a table down to a single run @@ -183,7 +193,7 @@ newPendingUnionMerge :: -> m (Ref (MergingTree m h)) newPendingUnionMerge mts = do mts' <- V.filterM (fmap not . isStructurallyEmpty) (V.fromList mts) - -- isStructurallyEmpty is interruptable even with async exceptions masked, + -- isStructurallyEmpty is interruptible even with async exceptions masked, -- but we use it before allocating new references. mts'' <- V.mapM dupRef mts' case V.uncons mts'' of diff --git a/src/Database/LSMTree/Internal/Snapshot.hs b/src/Database/LSMTree/Internal/Snapshot.hs index 6eae12c98..123c9cdfc 100644 --- a/src/Database/LSMTree/Internal/Snapshot.hs +++ b/src/Database/LSMTree/Internal/Snapshot.hs @@ -8,8 +8,15 @@ module Database.LSMTree.Internal.Snapshot ( , SnapLevel (..) , SnapIncomingRun (..) , SnapMergingRunState (..) + -- * MergeTree snapshot format + , SnapMergingTree(..) + , SnapMergingTreeState(..) + , SnapPendingMerge(..) + , SnapPreExistingRun(..) -- * Conversion to levels snapshot format , toSnapLevels + -- * Conversion to merging tree snapshot format + , toSnapMergingTree -- * Write buffer , snapshotWriteBuffer , openWriteBuffer @@ -17,8 +24,11 @@ module Database.LSMTree.Internal.Snapshot ( , SnapshotRun (..) , snapshotRun , openRun - -- * Opening from levels snapshot format + -- * Opening snapshot formats + -- ** Levels format , fromSnapLevels + -- ** Merging Tree format + , fromSnapMergingTree -- * Hard links , hardLinkRunFiles ) where @@ -43,6 +53,7 @@ import qualified Database.LSMTree.Internal.Merge as Merge import Database.LSMTree.Internal.MergeSchedule import Database.LSMTree.Internal.MergingRun (NumRuns (..)) import qualified Database.LSMTree.Internal.MergingRun as MR +import qualified Database.LSMTree.Internal.MergingTree as MT import Database.LSMTree.Internal.Paths (ActiveDir (..), ForBlob (..), ForKOps (..), NamedSnapshotDir (..), RunFsPaths (..), WriteBufferFsPaths (..), @@ -93,7 +104,7 @@ data SnapshotMetaData = SnapshotMetaData { -- -- One could argue that the 'SnapshotName' could be used to to hold this -- type information, but the file name of snapshot metadata is not guarded - -- by a checksum, wherease the contents of the file are. Therefore using the + -- by a checksum, whereas the contents of the file are. Therefore using the -- 'SnapshotLabel' is safer. snapMetaLabel :: !SnapshotLabel -- | Whether a table is normal or monoidal. @@ -110,11 +121,15 @@ data SnapshotMetaData = SnapshotMetaData { , snapWriteBuffer :: !RunNumber -- | The shape of the levels of the LSM tree. , snapMetaLevels :: !(SnapLevels SnapshotRun) + -- | The state of tree merging of the LSM tree. + , snapMergingTree :: !(Maybe (SnapMergingTree SnapshotRun)) } deriving stock Eq instance NFData SnapshotMetaData where - rnf (SnapshotMetaData a b c d e) = rnf a `seq` rnf b `seq` rnf c `seq` rnf d `seq` rnf e + rnf (SnapshotMetaData a b c d e f) = + rnf a `seq` rnf b `seq` rnf c `seq` + rnf d `seq` rnf e `seq` rnf f {------------------------------------------------------------------------------- Levels snapshot format @@ -182,6 +197,151 @@ instance (NFData t, NFData r) => NFData (SnapMergingRunState t r) where rnf (SnapCompletedMerge a b c) = rnf a `seq` rnf b `seq` rnf c rnf (SnapOngoingMerge a b c d) = rnf a `seq` rnf b `seq` rnf c `seq` rnf d +{------------------------------------------------------------------------------- + Snapshot MergingTree +-------------------------------------------------------------------------------} + +newtype SnapMergingTree r = SnapMergingTree (SnapMergingTreeState r) + deriving stock (Eq, Functor, Foldable, Traversable) + deriving newtype NFData + +data SnapMergingTreeState r = + SnapCompletedTreeMerge !r + | SnapPendingTreeMerge !(SnapPendingMerge r) + | SnapOngoingTreeMerge + !(SnapMergingRunState MR.TreeMergeType r) + deriving stock (Eq, Functor, Foldable, Traversable) + +instance NFData r => NFData (SnapMergingTreeState r) where + rnf (SnapCompletedTreeMerge a) = rnf a + rnf (SnapPendingTreeMerge a) = rnf a + rnf (SnapOngoingTreeMerge a) = rnf a + +data SnapPendingMerge r = + SnapPendingLevelMerge + ![SnapPreExistingRun r] + !(Maybe (SnapMergingTree r)) + | SnapPendingUnionMerge + ![SnapMergingTree r] + deriving stock (Eq, Functor, Foldable, Traversable) + +instance NFData r => NFData (SnapPendingMerge r) where + rnf (SnapPendingLevelMerge a b) = rnf a `seq` rnf b + rnf (SnapPendingUnionMerge a) = rnf a + +data SnapPreExistingRun r = + SnapPreExistingRun !r + | SnapPreExistingMergingRun + !(SnapMergingRunState MR.LevelMergeType r) + deriving stock (Eq, Functor, Foldable, Traversable) + +instance NFData r => NFData (SnapPreExistingRun r) where + rnf (SnapPreExistingRun a) = rnf a + rnf (SnapPreExistingMergingRun a) = rnf a + +{------------------------------------------------------------------------------- + Opening from merging tree snapshot format +-------------------------------------------------------------------------------} + +{-# SPECIALISE fromSnapMergingTree :: + ActionRegistry IO + -> HasFS IO h + -> HasBlockIO IO h + -> TableConfig + -> UniqCounter IO + -> ResolveSerialisedValue + -> ActiveDir + -> SnapMergingTree (Ref (Run IO h)) + -> IO (Ref (MT.MergingTree IO h)) + #-} +-- | Duplicates runs and re-creates merging runs. +fromSnapMergingTree :: + forall m h. (MonadMask m, MonadMVar m, MonadSTM m, MonadST m) + => ActionRegistry m + -> HasFS m h + -> HasBlockIO m h + -> TableConfig + -> UniqCounter m + -> ResolveSerialisedValue + -> ActiveDir + -> SnapMergingTree (Ref (Run m h)) + -> m (Ref (MT.MergingTree m h)) +fromSnapMergingTree reg hfs hbio conf uc resolve dir (SnapMergingTree snapTreeState) = + fromSnapTreeState snapTreeState + where + -- Partially applied functions for convenience + recurrence :: SnapMergingTree (Ref (Run m h)) -> m (Ref (MT.MergingTree m h)) + recurrence = fromSnapMergingTree reg hfs hbio conf uc resolve dir + + getSnapMergingRunState + :: forall t. + MR.IsMergeType t + => SnapMergingRunState t (Ref (Run m h)) + -> m (Ref (MR.MergingRun t m h)) + getSnapMergingRunState = fromSnapMergingRunState hfs hbio uc resolve dir + + -- Conversion definitions + fromSnapTreeState :: SnapMergingTreeState (Ref (Run m h)) -> m (Ref (MT.MergingTree m h)) + fromSnapTreeState (SnapCompletedTreeMerge run) = + MT.newPendingLevelMerge [MT.PreExistingRun run] Nothing + fromSnapTreeState (SnapPendingTreeMerge pMerge) = case pMerge of + SnapPendingLevelMerge peRuns maybeMergeTree -> do + peRuns' <- traverse fromSnapPreExistingRun peRuns + maybeMergeTree' <- traverse recurrence maybeMergeTree + MT.newPendingLevelMerge peRuns' maybeMergeTree' + SnapPendingUnionMerge mergeTrees -> + MT.newPendingUnionMerge =<< traverse recurrence mergeTrees + fromSnapTreeState (SnapOngoingTreeMerge smrs) = + MT.newOngoingMerge =<< getSnapMergingRunState smrs + + fromSnapPreExistingRun :: SnapPreExistingRun (Ref (Run m h)) -> m (MT.PreExistingRun m h) + fromSnapPreExistingRun (SnapPreExistingRun run) = pure $ MT.PreExistingRun run + fromSnapPreExistingRun (SnapPreExistingMergingRun smrs) = + MT.PreExistingMergingRun <$> getSnapMergingRunState smrs + +{------------------------------------------------------------------------------- + Conversion to merge tree snapshot format +-------------------------------------------------------------------------------} + +{-# SPECIALISE toSnapMergingTree :: Ref (MT.MergingTree IO h) -> IO (SnapMergingTree (Ref (Run IO h))) #-} +toSnapMergingTree :: + (PrimMonad m, MonadMVar m) + => Ref (MT.MergingTree m h) + -> m (SnapMergingTree (Ref (Run m h))) +toSnapMergingTree (DeRef (MT.MergingTree mStateVar _mCounter)) = + withMVar mStateVar $ \mState -> SnapMergingTree <$> toSnapMergingTreeState mState + +{-# SPECIALISE toSnapMergingTreeState :: MT.MergingTreeState IO h -> IO (SnapMergingTreeState (Ref (Run IO h))) #-} +toSnapMergingTreeState :: + (PrimMonad m, MonadMVar m) + => MT.MergingTreeState m h + -> m (SnapMergingTreeState (Ref (Run m h))) +toSnapMergingTreeState (MT.CompletedTreeMerge r) = pure $ SnapCompletedTreeMerge r +toSnapMergingTreeState (MT.PendingTreeMerge p) = SnapPendingTreeMerge <$> toSnapPendingMerge p +toSnapMergingTreeState (MT.OngoingTreeMerge mergingRun) = + SnapOngoingTreeMerge <$> toSnapMergingRunState mergingRun + +{-# SPECIALISE toSnapPendingMerge :: MT.PendingMerge IO h -> IO (SnapPendingMerge (Ref (Run IO h))) #-} +toSnapPendingMerge :: + (PrimMonad m, MonadMVar m) + => MT.PendingMerge m h + -> m (SnapPendingMerge (Ref (Run m h))) +toSnapPendingMerge (MT.PendingUnionMerge mts) = + SnapPendingUnionMerge <$> traverse toSnapMergingTree (V.toList mts) +toSnapPendingMerge (MT.PendingLevelMerge pes mmt) = do + pes' <- traverse toSnapPreExistingRun pes + mmt' <- traverse toSnapMergingTree mmt + pure $ SnapPendingLevelMerge (V.toList pes') mmt' + +{-# SPECIALISE toSnapPreExistingRun :: MT.PreExistingRun IO h -> IO (SnapPreExistingRun (Ref (Run IO h))) #-} +toSnapPreExistingRun :: + (PrimMonad m, MonadMVar m) + => MT.PreExistingRun m h + -> m (SnapPreExistingRun (Ref (Run m h))) +toSnapPreExistingRun (MT.PreExistingRun run) = pure $ SnapPreExistingRun run +toSnapPreExistingRun (MT.PreExistingMergingRun peMergingRun) = + SnapPreExistingMergingRun <$> toSnapMergingRunState peMergingRun + {------------------------------------------------------------------------------- Conversion to levels snapshot format -------------------------------------------------------------------------------} @@ -235,7 +395,7 @@ toSnapMergingRunState :: -> m (SnapMergingRunState t (Ref (Run m h))) toSnapMergingRunState !mr = do -- TODO: MR.snapshot needs to return duplicated run references, and we - -- need to arrange to release them when the snapshoting is done. + -- need to arrange to release them when the snapshotting is done. (numRuns, mergeDebt, mergeCredits, state) <- MR.snapshot mr case state of MR.CompletedMerge r -> diff --git a/src/Database/LSMTree/Internal/Snapshot/Codec.hs b/src/Database/LSMTree/Internal/Snapshot/Codec.hs index fd1a58413..0f0cb8090 100644 --- a/src/Database/LSMTree/Internal/Snapshot/Codec.hs +++ b/src/Database/LSMTree/Internal/Snapshot/Codec.hs @@ -20,11 +20,12 @@ import Codec.CBOR.Decoding import Codec.CBOR.Encoding import Codec.CBOR.Read import Codec.CBOR.Write -import Control.Monad (when) +import Control.Monad (replicateM, when) import Control.Monad.Class.MonadThrow (MonadThrow (..)) import Data.Bifunctor import qualified Data.ByteString.Char8 as BSC import Data.ByteString.Lazy (ByteString) +import Data.Foldable (fold) import qualified Data.Map.Strict as Map import qualified Data.Vector as V import Database.LSMTree.Internal.Config @@ -149,6 +150,7 @@ encodeSnapshotMetaData = toLazyByteString . encode . Versioned decodeSnapshotMetaData :: ByteString -> Either DeserialiseFailure SnapshotMetaData decodeSnapshotMetaData bs = second (getVersioned . snd) (deserialiseFromBytes decode bs) + {------------------------------------------------------------------------------- Encoding and decoding -------------------------------------------------------------------------------} @@ -159,7 +161,7 @@ class Encode a where -- | Decoder that is not parameterised by a 'SnapshotVersion'. -- -- Used only for 'SnapshotVersion' and 'Versioned', which live outside the --- 'SnapshotMetaData' type hierachy. +-- 'SnapshotMetaData' type hierarchy. class Decode a where decode :: Decoder s a @@ -220,23 +222,25 @@ instance Decode SnapshotVersion where -- SnapshotMetaData instance Encode SnapshotMetaData where - encode (SnapshotMetaData label tableType config writeBuffer levels) = - encodeListLen 5 + encode (SnapshotMetaData label tableType config writeBuffer levels mergingTree) = + encodeListLen 7 <> encode label <> encode tableType <> encode config <> encode writeBuffer <> encode levels + <> encodeMaybe mergingTree instance DecodeVersioned SnapshotMetaData where decodeVersioned ver@V0 = do - _ <- decodeListLenOf 5 + _ <- decodeListLenOf 7 SnapshotMetaData <$> decodeVersioned ver <*> decodeVersioned ver <*> decodeVersioned ver <*> decodeVersioned ver <*> decodeVersioned ver + <*> decodeMaybe ver -- SnapshotLabel @@ -699,3 +703,109 @@ instance DecodeVersioned MR.TreeMergeType where 1 -> pure MR.MergeLevel 2 -> pure MR.MergeUnion _ -> fail ("[TreeMergeType] Unexpected tag: " <> show tag) + +{------------------------------------------------------------------------------- + Encoding and decoding: SnapMergingTree +-------------------------------------------------------------------------------} + +-- SnapMergingTree + +instance Encode r => Encode (SnapMergingTree r) where + encode (SnapMergingTree tState) = encode tState + +instance DecodeVersioned r => DecodeVersioned (SnapMergingTree r) where + decodeVersioned ver@V0 = SnapMergingTree <$> decodeVersioned ver + +-- SnapMergingTreeState + +instance Encode r => Encode (SnapMergingTreeState r) where + encode (SnapCompletedTreeMerge x) = + encodeListLen 2 + <> encodeWord 0 + <> encode x + encode (SnapPendingTreeMerge x) = + encodeListLen 2 + <> encodeWord 1 + <> encode x + encode (SnapOngoingTreeMerge smrs) = + encodeListLen 2 + <> encodeWord 2 + <> encode smrs + +instance DecodeVersioned r => DecodeVersioned (SnapMergingTreeState r) where + decodeVersioned v@V0 = do + n <- decodeListLen + tag <- decodeWord + case (n, tag) of + (2, 0) -> SnapCompletedTreeMerge <$> decodeVersioned v + (2, 1) -> SnapPendingTreeMerge <$> decodeVersioned v + (2, 2) -> SnapOngoingTreeMerge <$> decodeVersioned v + _ -> fail ("[SnapMergingTreeState] Unexpected combination of list length and tag: " <> show (n, tag)) + +-- SnapPendingMerge + +instance Encode r => Encode (SnapPendingMerge r) where + encode (SnapPendingLevelMerge pe mt) = fold + [ encodeListLen 4 + , encodeWord 0 + , encodeMaybe mt + , encodeListLen . toEnum $ length pe + , foldMap encode pe + ] + encode (SnapPendingUnionMerge mts) = + encodeListLen 2 + <> encodeWord 1 + <> encodeListLen (toEnum $ length mts) + <> foldMap encode mts + +instance DecodeVersioned r => DecodeVersioned (SnapPendingMerge r) where + decodeVersioned v@V0 = do + n <- decodeListLen + tag <- decodeWord + case (n, tag) of + (4, 0) -> do + -- Get the whether or not the levels merge exists + peLvls <- decodeMaybe v + peLen <- decodeListLen + peRuns <- replicateM peLen (decodeVersioned v) + pure $ SnapPendingLevelMerge peRuns peLvls + (2, 1) -> do + -- Get the number of pre-existsing unions to read + peLen <- decodeListLen + SnapPendingUnionMerge <$> replicateM peLen (decodeVersioned v) + _ -> fail ("[SnapPendingMerge] Unexpected combination of list length and tag: " <> show (n, tag)) + +-- SnapPreExistingRun + +instance Encode r => Encode (SnapPreExistingRun r) where + encode (SnapPreExistingRun x) = + encodeListLen 2 + <> encodeWord 0 + <> encode x + encode (SnapPreExistingMergingRun smrs) = + encodeListLen 2 + <> encodeWord 1 + <> encode smrs + +instance DecodeVersioned r => DecodeVersioned (SnapPreExistingRun r) where + decodeVersioned v@V0 = do + n <- decodeListLen + tag <- decodeWord + case (n, tag) of + (2, 0) -> SnapPreExistingRun <$> decodeVersioned v + (2, 1) -> SnapPreExistingMergingRun <$> decodeVersioned v + _ -> fail ("[SnapPreExistingRun] Unexpected combination of list length and tag: " <> show (n, tag)) + +-- Utilities for encoding/decoding Maybe values + +encodeMaybe :: Encode a => Maybe a -> Encoding +encodeMaybe = \case + Nothing -> encodeBool False <> encodeNull + Just en -> encodeBool True <> encode en + + +decodeMaybe :: DecodeVersioned a => SnapshotVersion -> Decoder s (Maybe a) +decodeMaybe v@V0 = decodeBool >>= \exist -> + if exist + then Just <$> decodeVersioned v + else Nothing <$ decodeNull diff --git a/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs b/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs index 5bb055e10..97a77a67f 100644 --- a/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs +++ b/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs @@ -184,6 +184,10 @@ testAll test = [ , test (Proxy @NominalCredits) , test (Proxy @LevelMergeType) , test (Proxy @TreeMergeType) + , test (Proxy @(SnapMergingTree SnapshotRun)) + , test (Proxy @(SnapMergingTreeState SnapshotRun)) + , test (Proxy @(SnapPendingMerge SnapshotRun)) + , test (Proxy @(SnapPreExistingRun SnapshotRun)) ] {------------------------------------------------------------------------------- @@ -201,10 +205,12 @@ deriving newtype instance Arbitrary a => Arbitrary (Versioned a) -------------------------------------------------------------------------------} instance Arbitrary SnapshotMetaData where - arbitrary = SnapshotMetaData <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary - shrink (SnapshotMetaData a b c d e) = - [ SnapshotMetaData a' b' c' d' e' - | (a', b', c', d', e') <- shrink (a, b, c, d, e)] + arbitrary = SnapshotMetaData <$> + arbitrary <*> arbitrary <*> arbitrary <*> + arbitrary <*> arbitrary <*> arbitrary + shrink (SnapshotMetaData a b c d e f) = + [ SnapshotMetaData a' b' c' d' e' f' + | (a', b', c', d', e', f') <- shrink (a, b, c, d, e, f)] deriving newtype instance Arbitrary SnapshotLabel @@ -365,7 +371,79 @@ deriving stock instance Show r => Show (SnapLevels r) deriving stock instance Show r => Show (SnapLevel r) deriving stock instance Show r => Show (SnapIncomingRun r) deriving stock instance (Show t, Show r) => Show (SnapMergingRunState t r) + +deriving stock instance Show r => Show (SnapMergingTree r) +deriving stock instance Show r => Show (SnapMergingTreeState r) +deriving stock instance Show r => Show (SnapPendingMerge r) +deriving stock instance Show r => Show (SnapPreExistingRun r) + deriving stock instance Show MergeDebt deriving stock instance Show MergeCredits deriving stock instance Show NominalDebt deriving stock instance Show NominalCredits + +{------------------------------------------------------------------------------- + Arbitrary: SnapshotMetaData +-------------------------------------------------------------------------------} + +deriving newtype instance Arbitrary r => Arbitrary (SnapMergingTree r) + +instance Arbitrary r => Arbitrary (SnapMergingTreeState r) where + arbitrary = inductiveMergingTreeState inductiveLimit + shrink (SnapCompletedTreeMerge a) = SnapCompletedTreeMerge <$> shrink a + shrink (SnapPendingTreeMerge a) = SnapPendingTreeMerge <$> shrink a + shrink (SnapOngoingTreeMerge a) = SnapOngoingTreeMerge <$> shrink a + +instance Arbitrary r => Arbitrary (SnapPendingMerge r) where + arbitrary = inductivePendingTreeMerge inductiveLimit + shrink (SnapPendingUnionMerge a) = SnapPendingUnionMerge <$> shrinkList shrink a + shrink (SnapPendingLevelMerge a b) = + [ SnapPendingLevelMerge a' b' | a' <- shrinkList shrink a, b' <- shrink b ] + + +instance Arbitrary r => Arbitrary (SnapPreExistingRun r) where + arbitrary = oneof [ + SnapPreExistingRun <$> arbitrary + , SnapPreExistingMergingRun <$> arbitrary + ] + shrink (SnapPreExistingRun a) = SnapPreExistingRun <$> shrink a + shrink (SnapPreExistingMergingRun a) = SnapPreExistingMergingRun <$> shrink a + +-- | The 'SnapMergingTree' is an inductive data-type and therefore we must limit +-- the recursive depth at which new 'Arbitrary' sub-trees are generated. Hence +-- the need for this limit. This limit is the "gas" for the inductive functions. +-- At reach recursive call, the "gas" value decremented until it reaches zero. +-- Each inductive function ensures it never create a forest of sub-trees greater +-- than the /monotonically decreasing/ gas parameter it received. +inductiveLimit :: Int +inductiveLimit = 4 + +-- | +-- Generate an 'Arbitrary', "gas-limited" 'SnapMergingTree'. +inductiveSized :: Arbitrary r => Int -> Gen (SnapMergingTree r) +inductiveSized = fmap SnapMergingTree . inductiveMergingTreeState + +-- | +-- Generate an 'Arbitrary', "gas-limited" 'SnapMergingTreeState'. +inductiveMergingTreeState :: Arbitrary a => Int -> Gen (SnapMergingTreeState a) +inductiveMergingTreeState gas = oneof [ + SnapCompletedTreeMerge <$> arbitrary + , SnapPendingTreeMerge <$> inductivePendingTreeMerge gas + , SnapOngoingTreeMerge <$> arbitrary + ] + +-- | +-- Generate an 'Arbitrary', "gas-limited" 'SnapPendingMerge'. +inductivePendingTreeMerge :: Arbitrary a => Int -> Gen (SnapPendingMerge a) +inductivePendingTreeMerge gas = oneof [ + SnapPendingLevelMerge <$> genPreExistings <*> genMaybeSubTree + , SnapPendingUnionMerge <$> genListSubtrees + ] + where + subGen = inductiveSized . max 0 $ gas - 1 + -- Define custom generators to ensure that the sub-trees are less than + -- or equal to the "gas" parameter. + genPreExistings = genVectorsUpToBound gas arbitrary + genListSubtrees = genVectorsUpToBound gas subGen + genMaybeSubTree = oneof [ pure Nothing, Just <$> subGen ] + genVectorsUpToBound x gen = oneof $ flip vectorOf gen <$> [ 0 .. x ] diff --git a/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs b/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs index 77c19644e..dcb0aca75 100644 --- a/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs +++ b/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs @@ -4,6 +4,7 @@ module Test.Database.LSMTree.Internal.Snapshot.Codec.Golden import Codec.CBOR.Write (toLazyByteString) import Control.Monad (when) +import Data.Bifunctor (second) import qualified Data.ByteString.Lazy as BSL (writeFile) import Data.Foldable (fold) import qualified Data.List as List @@ -40,6 +41,7 @@ tests = handleOutputFiles . testGroup , testCodecSnapshotTableType , testCodecTableConfig , testCodecSnapLevels + , testCodecMergingTree ] -- | The mount point is defined as the location of the golden file data directory @@ -48,7 +50,7 @@ goldenDataMountPoint :: MountPoint goldenDataMountPoint = MountPoint "test/golden-file-data/snapshot-codec" -- | Delete output files on test-case success. --- Change the option here if this is undesireable. +-- Change the option here if this is undesirable. handleOutputFiles :: TestTree -> TestTree handleOutputFiles = Tasty.localOption Au.OnPass @@ -83,7 +85,7 @@ snapshotCodecTest name datum = outputAction = do -- Ensure that if the output file already exists, we remove it and -- re-write out the serialized data. This ensures that there are no - -- false-positives, false-negatives, or irrelavent I/O exceptions. + -- false-positives, false-negatives, or irrelevant I/O exceptions. removeIfExists snapshotFsPath BSL.writeFile snapshotHsPath . toLazyByteString $ encode datum @@ -96,7 +98,8 @@ testCodecSnapshotLabel = (tagC, valC) = basicTableConfig valD = basicRunNumber (tagE, valE) = basicSnapLevels - in (fuseAnnotations [tagA, tagB, tagC, tagE ], SnapshotMetaData valA valB valC valD valE) + (tagF, valF) = basicSnapMergingTree + in (fuseAnnotations [tagA, tagB, tagC, tagE, tagF ], SnapshotMetaData valA valB valC valD valE valF) in testCodecBuilder "SnapshotLabels" $ assembler <$> enumerateSnapshotLabel testCodecSnapshotTableType :: TestTree @@ -106,7 +109,8 @@ testCodecSnapshotTableType = (tagC, valC) = basicTableConfig valD = basicRunNumber (tagE, valE) = basicSnapLevels - in (fuseAnnotations [tagA, tagB, tagC, tagE ], SnapshotMetaData valA valB valC valD valE) + (tagF, valF) = basicSnapMergingTree + in (fuseAnnotations [tagA, tagB, tagC, tagE, tagF ], SnapshotMetaData valA valB valC valD valE valF) in testCodecBuilder "SnapshotTables" $ assembler <$> enumerateSnapshotTableType testCodecTableConfig :: TestTree @@ -116,7 +120,8 @@ testCodecTableConfig = (tagB, valB) = basicSnapshotTableType valD = basicRunNumber (tagE, valE) = basicSnapLevels - in (fuseAnnotations [tagA, tagB, tagC, tagE ], SnapshotMetaData valA valB valC valD valE) + (tagF, valF) = basicSnapMergingTree + in (fuseAnnotations [tagA, tagB, tagC, tagE, tagF ], SnapshotMetaData valA valB valC valD valE valF) in testCodecBuilder "SnapshotConfig" $ assembler <$> enumerateTableConfig testCodecSnapLevels :: TestTree @@ -126,9 +131,21 @@ testCodecSnapLevels = (tagB, valB) = basicSnapshotTableType (tagC, valC) = basicTableConfig valD = basicRunNumber - in (fuseAnnotations [tagA, tagB, tagC, tagE ], SnapshotMetaData valA valB valC valD valE) + (tagF, valF) = basicSnapMergingTree + in (fuseAnnotations [tagA, tagB, tagC, tagE, tagF ], SnapshotMetaData valA valB valC valD valE valF) in testCodecBuilder "SnapshotLevels" $ assembler <$> enumerateSnapLevels +testCodecMergingTree :: TestTree +testCodecMergingTree = + let assembler (tagF, valF) = + let (tagA, valA) = basicSnapshotLabel + (tagB, valB) = basicSnapshotTableType + (tagC, valC) = basicTableConfig + valD = basicRunNumber + (tagE, valE) = basicSnapLevels + in (fuseAnnotations [tagA, tagB, tagC, tagE, tagF ], SnapshotMetaData valA valB valC valD valE valF) + in testCodecBuilder "SnapshotMergingTree" $ assembler <$> enumerateSnapMergingTree + testCodecBuilder :: TestName -> [(ComponentAnnotation, SnapshotMetaData)] -> TestTree testCodecBuilder tName metadata = testGroup tName $ uncurry snapshotCodecTest <$> metadata @@ -160,6 +177,9 @@ basicRunNumber = enumerateRunNumbers basicSnapLevels :: (ComponentAnnotation, SnapLevels SnapshotRun) basicSnapLevels = head enumerateSnapLevels +basicSnapMergingTree :: (ComponentAnnotation, Maybe (SnapMergingTree SnapshotRun)) +basicSnapMergingTree = head enumerateSnapMergingTree + {---------------- Enumeration of SnapshotMetaData sub-components ----------------} @@ -258,6 +278,63 @@ enumerateVectorRunInfo = } ]) ] +{---------------- +Enumeration of SnapMergingTree sub-components +----------------} + +enumerateSnapMergingTree :: [(ComponentAnnotation, Maybe (SnapMergingTree SnapshotRun))] +enumerateSnapMergingTree = + let noneTrees = (fuseAnnotations $ "M0" : replicate 11 blank, Nothing) + someTrees = reannotate <$> enumerateSnapMergingTreeState True + reannotate (tag, val) = (fuseAnnotations ["M1", tag], Just val) + in noneTrees : someTrees + +enumerateSnapMergingTreeState :: Bool -> [(ComponentAnnotation, SnapMergingTree SnapshotRun)] +enumerateSnapMergingTreeState expandable = + let s0 = [ (fuseAnnotations $ "S0" : replicate 10 blank, SnapCompletedTreeMerge enumerateOpenRunInfo) ] + s1 = do + (tagX, valX) <- enumerateSnapPendingMerge expandable + [ (fuseAnnotations ["S1", tagX], SnapPendingTreeMerge valX) ] + s2 = do + (tagX, valX) <- enumerateSnapOngoingTreeMerge + [ (fuseAnnotations ["S2", tagX], valX) ] + in second SnapMergingTree <$> fold [ s0, s1, s2 ] + +enumerateSnapOngoingTreeMerge :: [(ComponentAnnotation, SnapMergingTreeState SnapshotRun)] +enumerateSnapOngoingTreeMerge = do + (tagX, valX) <- enumerateSnapMergingRunState enumerateTreeMergeType + let value = SnapOngoingTreeMerge valX + pure ( fuseAnnotations $ ["G0", blank, tagX] <> replicate 5 blank, value ) + +enumerateSnapPendingMerge :: Bool -> [(ComponentAnnotation, SnapPendingMerge SnapshotRun)] +enumerateSnapPendingMerge expandable = + let (tagTrees, subTrees) + | not expandable = ("M0", []) + | otherwise = ("M1", snd <$> enumerateSnapMergingTreeState False) + headMay [] = Nothing + headMay (x:_) = Just x + prefix = do + extra <- [False, True ] + (tagPre, valPre) <- enumerateSnapPreExistingRun + (tagExt, valExt) <- + if extra + then second pure <$> enumerateSnapPreExistingRun + else [(fuseAnnotations $ replicate 4 blank, [])] + let preValues = [ valPre ] <> valExt + pure (fuseAnnotations [ "P0", tagPre, tagExt, tagTrees], SnapPendingLevelMerge preValues $ headMay subTrees) + in prefix <> [(fuseAnnotations $ fold [["P1"], replicate 8 blank, [tagTrees]], SnapPendingUnionMerge subTrees)] + +enumerateSnapPreExistingRun :: [(ComponentAnnotation, SnapPreExistingRun SnapshotRun)] +enumerateSnapPreExistingRun = + ( fuseAnnotations ("E0" : replicate 3 blank), SnapPreExistingRun enumerateOpenRunInfo) + : [ (fuseAnnotations ["E1", tagX], SnapPreExistingMergingRun valX) + | (tagX, valX) <- enumerateSnapMergingRunState enumerateLevelMergeType + ] + +enumerateTreeMergeType :: [(ComponentAnnotation, MR.TreeMergeType)] +enumerateTreeMergeType = + [("T0", MR.MergeLevel), ("T1", MR.MergeUnion)] + {---------------- Enumeration of SnapshotMetaData sub-sub-components and so on... ----------------} From 53aa7e3b76a9a5676555d3b6b198a29444741ac5 Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Fri, 7 Mar 2025 23:30:46 +0000 Subject: [PATCH 08/15] Update golden files for snapshot disk representations following the change of snapshot representation types. --- ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 49 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 56 bytes ...P0-E0-__-__-__-E0-__-__-__-M1.snapshot.golden | Bin 0 -> 77 bytes ...P0-E0-__-__-__-E1-C0-__-__-M1.snapshot.golden | Bin 0 -> 83 bytes ...P0-E0-__-__-__-E1-C1-V0-L0-M1.snapshot.golden | Bin 0 -> 84 bytes ...P0-E0-__-__-__-E1-C1-V0-L1-M1.snapshot.golden | Bin 0 -> 84 bytes ...P0-E0-__-__-__-E1-C1-V1-L0-M1.snapshot.golden | Bin 0 -> 90 bytes ...P0-E0-__-__-__-E1-C1-V1-L1-M1.snapshot.golden | Bin 0 -> 90 bytes ...P0-E0-__-__-__-E1-C1-V2-L0-M1.snapshot.golden | Bin 0 -> 96 bytes ...P0-E0-__-__-__-E1-C1-V2-L1-M1.snapshot.golden | Bin 0 -> 96 bytes ...P0-E0-__-__-__-__-__-__-__-M1.snapshot.golden | Bin 0 -> 69 bytes ...P0-E1-C0-__-__-E0-__-__-__-M1.snapshot.golden | Bin 0 -> 83 bytes ...P0-E1-C0-__-__-E1-C0-__-__-M1.snapshot.golden | Bin 0 -> 89 bytes ...P0-E1-C0-__-__-E1-C1-V0-L0-M1.snapshot.golden | Bin 0 -> 90 bytes ...P0-E1-C0-__-__-E1-C1-V0-L1-M1.snapshot.golden | Bin 0 -> 90 bytes ...P0-E1-C0-__-__-E1-C1-V1-L0-M1.snapshot.golden | Bin 0 -> 96 bytes ...P0-E1-C0-__-__-E1-C1-V1-L1-M1.snapshot.golden | Bin 0 -> 96 bytes ...P0-E1-C0-__-__-E1-C1-V2-L0-M1.snapshot.golden | Bin 0 -> 102 bytes ...P0-E1-C0-__-__-E1-C1-V2-L1-M1.snapshot.golden | Bin 0 -> 102 bytes ...P0-E1-C0-__-__-__-__-__-__-M1.snapshot.golden | Bin 0 -> 75 bytes ...P0-E1-C1-V0-L0-E0-__-__-__-M1.snapshot.golden | Bin 0 -> 84 bytes ...P0-E1-C1-V0-L0-E1-C0-__-__-M1.snapshot.golden | Bin 0 -> 90 bytes ...P0-E1-C1-V0-L0-E1-C1-V0-L0-M1.snapshot.golden | Bin 0 -> 91 bytes ...P0-E1-C1-V0-L0-E1-C1-V0-L1-M1.snapshot.golden | Bin 0 -> 91 bytes ...P0-E1-C1-V0-L0-E1-C1-V1-L0-M1.snapshot.golden | Bin 0 -> 97 bytes ...P0-E1-C1-V0-L0-E1-C1-V1-L1-M1.snapshot.golden | Bin 0 -> 97 bytes ...P0-E1-C1-V0-L0-E1-C1-V2-L0-M1.snapshot.golden | Bin 0 -> 103 bytes ...P0-E1-C1-V0-L0-E1-C1-V2-L1-M1.snapshot.golden | Bin 0 -> 103 bytes ...P0-E1-C1-V0-L0-__-__-__-__-M1.snapshot.golden | Bin 0 -> 76 bytes ...P0-E1-C1-V0-L1-E0-__-__-__-M1.snapshot.golden | Bin 0 -> 84 bytes ...P0-E1-C1-V0-L1-E1-C0-__-__-M1.snapshot.golden | Bin 0 -> 90 bytes ...P0-E1-C1-V0-L1-E1-C1-V0-L0-M1.snapshot.golden | Bin 0 -> 91 bytes ...P0-E1-C1-V0-L1-E1-C1-V0-L1-M1.snapshot.golden | Bin 0 -> 91 bytes ...P0-E1-C1-V0-L1-E1-C1-V1-L0-M1.snapshot.golden | Bin 0 -> 97 bytes ...P0-E1-C1-V0-L1-E1-C1-V1-L1-M1.snapshot.golden | Bin 0 -> 97 bytes ...P0-E1-C1-V0-L1-E1-C1-V2-L0-M1.snapshot.golden | Bin 0 -> 103 bytes ...P0-E1-C1-V0-L1-E1-C1-V2-L1-M1.snapshot.golden | Bin 0 -> 103 bytes ...P0-E1-C1-V0-L1-__-__-__-__-M1.snapshot.golden | Bin 0 -> 76 bytes ...P0-E1-C1-V1-L0-E0-__-__-__-M1.snapshot.golden | Bin 0 -> 90 bytes ...P0-E1-C1-V1-L0-E1-C0-__-__-M1.snapshot.golden | Bin 0 -> 96 bytes ...P0-E1-C1-V1-L0-E1-C1-V0-L0-M1.snapshot.golden | Bin 0 -> 97 bytes ...P0-E1-C1-V1-L0-E1-C1-V0-L1-M1.snapshot.golden | Bin 0 -> 97 bytes ...P0-E1-C1-V1-L0-E1-C1-V1-L0-M1.snapshot.golden | Bin 0 -> 103 bytes ...P0-E1-C1-V1-L0-E1-C1-V1-L1-M1.snapshot.golden | Bin 0 -> 103 bytes ...P0-E1-C1-V1-L0-E1-C1-V2-L0-M1.snapshot.golden | Bin 0 -> 109 bytes ...P0-E1-C1-V1-L0-E1-C1-V2-L1-M1.snapshot.golden | Bin 0 -> 109 bytes ...P0-E1-C1-V1-L0-__-__-__-__-M1.snapshot.golden | Bin 0 -> 82 bytes ...P0-E1-C1-V1-L1-E0-__-__-__-M1.snapshot.golden | Bin 0 -> 90 bytes ...P0-E1-C1-V1-L1-E1-C0-__-__-M1.snapshot.golden | Bin 0 -> 96 bytes ...P0-E1-C1-V1-L1-E1-C1-V0-L0-M1.snapshot.golden | Bin 0 -> 97 bytes ...P0-E1-C1-V1-L1-E1-C1-V0-L1-M1.snapshot.golden | Bin 0 -> 97 bytes ...P0-E1-C1-V1-L1-E1-C1-V1-L0-M1.snapshot.golden | Bin 0 -> 103 bytes ...P0-E1-C1-V1-L1-E1-C1-V1-L1-M1.snapshot.golden | Bin 0 -> 103 bytes ...P0-E1-C1-V1-L1-E1-C1-V2-L0-M1.snapshot.golden | Bin 0 -> 109 bytes ...P0-E1-C1-V1-L1-E1-C1-V2-L1-M1.snapshot.golden | Bin 0 -> 109 bytes ...P0-E1-C1-V1-L1-__-__-__-__-M1.snapshot.golden | Bin 0 -> 82 bytes ...P0-E1-C1-V2-L0-E0-__-__-__-M1.snapshot.golden | Bin 0 -> 96 bytes ...P0-E1-C1-V2-L0-E1-C0-__-__-M1.snapshot.golden | Bin 0 -> 102 bytes ...P0-E1-C1-V2-L0-E1-C1-V0-L0-M1.snapshot.golden | Bin 0 -> 103 bytes ...P0-E1-C1-V2-L0-E1-C1-V0-L1-M1.snapshot.golden | Bin 0 -> 103 bytes ...P0-E1-C1-V2-L0-E1-C1-V1-L0-M1.snapshot.golden | Bin 0 -> 109 bytes ...P0-E1-C1-V2-L0-E1-C1-V1-L1-M1.snapshot.golden | Bin 0 -> 109 bytes ...P0-E1-C1-V2-L0-E1-C1-V2-L0-M1.snapshot.golden | Bin 0 -> 115 bytes ...P0-E1-C1-V2-L0-E1-C1-V2-L1-M1.snapshot.golden | Bin 0 -> 115 bytes ...P0-E1-C1-V2-L0-__-__-__-__-M1.snapshot.golden | Bin 0 -> 88 bytes ...P0-E1-C1-V2-L1-E0-__-__-__-M1.snapshot.golden | Bin 0 -> 96 bytes ...P0-E1-C1-V2-L1-E1-C0-__-__-M1.snapshot.golden | Bin 0 -> 102 bytes ...P0-E1-C1-V2-L1-E1-C1-V0-L0-M1.snapshot.golden | Bin 0 -> 103 bytes ...P0-E1-C1-V2-L1-E1-C1-V0-L1-M1.snapshot.golden | Bin 0 -> 103 bytes ...P0-E1-C1-V2-L1-E1-C1-V1-L0-M1.snapshot.golden | Bin 0 -> 109 bytes ...P0-E1-C1-V2-L1-E1-C1-V1-L1-M1.snapshot.golden | Bin 0 -> 109 bytes ...P0-E1-C1-V2-L1-E1-C1-V2-L0-M1.snapshot.golden | Bin 0 -> 115 bytes ...P0-E1-C1-V2-L1-E1-C1-V2-L1-M1.snapshot.golden | Bin 0 -> 115 bytes ...P0-E1-C1-V2-L1-__-__-__-__-M1.snapshot.golden | Bin 0 -> 88 bytes ...P1-__-__-__-__-__-__-__-__-M1.snapshot.golden | Bin 0 -> 3155 bytes ...G0-__-C0-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 62 bytes ...G0-__-C1-V0-T0-__-__-__-__-__.snapshot.golden | Bin 0 -> 63 bytes ...G0-__-C1-V0-T1-__-__-__-__-__.snapshot.golden | Bin 0 -> 63 bytes ...G0-__-C1-V1-T0-__-__-__-__-__.snapshot.golden | Bin 0 -> 69 bytes ...G0-__-C1-V1-T1-__-__-__-__-__.snapshot.golden | Bin 0 -> 69 bytes ...G0-__-C1-V2-T0-__-__-__-__-__.snapshot.golden | Bin 0 -> 75 bytes ...G0-__-C1-V2-T1-__-__-__-__-__.snapshot.golden | Bin 0 -> 75 bytes ...__-__-__-__-R0-__-__-__-__-V0.snapshot.golden | Bin 44 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 55 bytes ...__-__-__-__-R0-__-__-__-__-V1.snapshot.golden | Bin 46 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 61 bytes ...__-__-__-__-R0-__-__-__-__-V2.snapshot.golden | Bin 48 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 60 bytes ...__-__-__-__-R1-P0-C0-__-__-V0.snapshot.golden | Bin 53 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 66 bytes ...__-__-__-__-R1-P0-C0-__-__-V1.snapshot.golden | Bin 55 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 72 bytes ...__-__-__-__-R1-P0-C0-__-__-V2.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 61 bytes ...__-__-__-__-R1-P0-C1-V0-L0-V0.snapshot.golden | Bin 53 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 67 bytes ...__-__-__-__-R1-P0-C1-V0-L0-V1.snapshot.golden | Bin 55 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 73 bytes ...__-__-__-__-R1-P0-C1-V0-L0-V2.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 61 bytes ...__-__-__-__-R1-P0-C1-V0-L1-V0.snapshot.golden | Bin 53 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 67 bytes ...__-__-__-__-R1-P0-C1-V0-L1-V1.snapshot.golden | Bin 55 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 73 bytes ...__-__-__-__-R1-P0-C1-V0-L1-V2.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 67 bytes ...__-__-__-__-R1-P0-C1-V1-L0-V0.snapshot.golden | Bin 55 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 73 bytes ...__-__-__-__-R1-P0-C1-V1-L0-V1.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 79 bytes ...__-__-__-__-R1-P0-C1-V1-L0-V2.snapshot.golden | Bin 59 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 67 bytes ...__-__-__-__-R1-P0-C1-V1-L1-V0.snapshot.golden | Bin 55 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 73 bytes ...__-__-__-__-R1-P0-C1-V1-L1-V1.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 79 bytes ...__-__-__-__-R1-P0-C1-V1-L1-V2.snapshot.golden | Bin 59 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 73 bytes ...__-__-__-__-R1-P0-C1-V2-L0-V0.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 79 bytes ...__-__-__-__-R1-P0-C1-V2-L0-V1.snapshot.golden | Bin 59 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 85 bytes ...__-__-__-__-R1-P0-C1-V2-L0-V2.snapshot.golden | Bin 61 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 73 bytes ...__-__-__-__-R1-P0-C1-V2-L1-V0.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 79 bytes ...__-__-__-__-R1-P0-C1-V2-L1-V1.snapshot.golden | Bin 59 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 85 bytes ...__-__-__-__-R1-P0-C1-V2-L1-V2.snapshot.golden | Bin 61 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 60 bytes ...__-__-__-__-R1-P1-C0-__-__-V0.snapshot.golden | Bin 53 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 66 bytes ...__-__-__-__-R1-P1-C0-__-__-V1.snapshot.golden | Bin 55 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 72 bytes ...__-__-__-__-R1-P1-C0-__-__-V2.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 61 bytes ...__-__-__-__-R1-P1-C1-V0-L0-V0.snapshot.golden | Bin 53 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 67 bytes ...__-__-__-__-R1-P1-C1-V0-L0-V1.snapshot.golden | Bin 55 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 73 bytes ...__-__-__-__-R1-P1-C1-V0-L0-V2.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 61 bytes ...__-__-__-__-R1-P1-C1-V0-L1-V0.snapshot.golden | Bin 53 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 67 bytes ...__-__-__-__-R1-P1-C1-V0-L1-V1.snapshot.golden | Bin 55 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 73 bytes ...__-__-__-__-R1-P1-C1-V0-L1-V2.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 67 bytes ...__-__-__-__-R1-P1-C1-V1-L0-V0.snapshot.golden | Bin 55 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 73 bytes ...__-__-__-__-R1-P1-C1-V1-L0-V1.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 79 bytes ...__-__-__-__-R1-P1-C1-V1-L0-V2.snapshot.golden | Bin 59 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 67 bytes ...__-__-__-__-R1-P1-C1-V1-L1-V0.snapshot.golden | Bin 55 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 73 bytes ...__-__-__-__-R1-P1-C1-V1-L1-V1.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 79 bytes ...__-__-__-__-R1-P1-C1-V1-L1-V2.snapshot.golden | Bin 59 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 73 bytes ...__-__-__-__-R1-P1-C1-V2-L0-V0.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 79 bytes ...__-__-__-__-R1-P1-C1-V2-L0-V1.snapshot.golden | Bin 59 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 85 bytes ...__-__-__-__-R1-P1-C1-V2-L0-V2.snapshot.golden | Bin 61 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 73 bytes ...__-__-__-__-R1-P1-C1-V2-L1-V0.snapshot.golden | Bin 57 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 79 bytes ...__-__-__-__-R1-P1-C1-V2-L1-V1.snapshot.golden | Bin 59 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 85 bytes ...__-__-__-__-R1-P1-C1-V2-L1-V2.snapshot.golden | Bin 61 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 50 bytes ...A0-I0-D0-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 45 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 50 bytes ...A0-I0-D0-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 45 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 50 bytes ...A0-I0-D1-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 45 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 50 bytes ...A0-I0-D1-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 45 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 51 bytes ...A0-I0-D2-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 46 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 51 bytes ...A0-I0-D2-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 46 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 50 bytes ...A0-I1-D0-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 45 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 50 bytes ...A0-I1-D0-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 45 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 50 bytes ...A0-I1-D1-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 45 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 50 bytes ...A0-I1-D1-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 45 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 51 bytes ...A0-I1-D2-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 46 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 51 bytes ...A0-I1-D2-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 46 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 56 bytes ...A1-I0-D0-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 51 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 56 bytes ...A1-I0-D0-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 51 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 56 bytes ...A1-I0-D1-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 51 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 56 bytes ...A1-I0-D1-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 51 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 57 bytes ...A1-I0-D2-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 52 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 57 bytes ...A1-I0-D2-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 52 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 56 bytes ...A1-I1-D0-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 51 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 56 bytes ...A1-I1-D0-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 51 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 56 bytes ...A1-I1-D1-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 51 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 56 bytes ...A1-I1-D1-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 51 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 57 bytes ...A1-I1-D2-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 52 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 57 bytes ...A1-I1-D2-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 52 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 53 bytes ...A2-I0-D0-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 48 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 53 bytes ...A2-I0-D0-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 48 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 53 bytes ...A2-I0-D1-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 48 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 53 bytes ...A2-I0-D1-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 48 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 54 bytes ...A2-I0-D2-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 49 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 54 bytes ...A2-I0-D2-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 49 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 53 bytes ...A2-I1-D0-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 48 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 53 bytes ...A2-I1-D0-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 48 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 53 bytes ...A2-I1-D1-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 48 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 53 bytes ...A2-I1-D1-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 48 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 54 bytes ...A2-I1-D2-G0-R0-__-__-__-__-V0.snapshot.golden | Bin 49 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 54 bytes ...A2-I1-D2-G1-R0-__-__-__-__-V0.snapshot.golden | Bin 49 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 49 bytes ...__-__-__-__-R0-__-__-__-__-V0.snapshot.golden | Bin 44 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 49 bytes ...__-__-__-__-R0-__-__-__-__-V0.snapshot.golden | Bin 44 -> 0 bytes ...__-__-__-__-__-__-__-__-__-__.snapshot.golden | Bin 0 -> 32 bytes ...__-__-__-__-R0-__-__-__-__-V0.snapshot.golden | Bin 27 -> 0 bytes 249 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S0-__-__-__-__-__-__-__-__-__-__.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E0-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C0-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V0-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V0-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V1-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V1-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V2-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V2-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-__-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E0-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C0-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V0-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V0-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V1-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V1-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V2-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V2-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-__-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E0-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E1-C0-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E1-C1-V0-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E1-C1-V0-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E1-C1-V1-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E1-C1-V1-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E1-C1-V2-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E1-C1-V2-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-__-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E0-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E1-C0-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E1-C1-V0-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E1-C1-V0-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E1-C1-V1-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E1-C1-V1-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E1-C1-V2-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E1-C1-V2-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-__-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L0-E0-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L0-E1-C0-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L0-E1-C1-V0-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L0-E1-C1-V0-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L0-E1-C1-V1-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L0-E1-C1-V1-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L0-E1-C1-V2-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L0-E1-C1-V2-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L0-__-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L1-E0-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L1-E1-C0-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L1-E1-C1-V0-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L1-E1-C1-V0-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L1-E1-C1-V1-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L1-E1-C1-V1-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L1-E1-C1-V2-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L1-E1-C1-V2-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L1-__-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L0-E0-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L0-E1-C0-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L0-E1-C1-V0-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L0-E1-C1-V0-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L0-E1-C1-V1-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L0-E1-C1-V1-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L0-E1-C1-V2-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L0-E1-C1-V2-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L0-__-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L1-E0-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L1-E1-C0-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L1-E1-C1-V0-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L1-E1-C1-V0-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L1-E1-C1-V1-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L1-E1-C1-V1-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L1-E1-C1-V2-L0-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L1-E1-C1-V2-L1-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L1-__-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P1-__-__-__-__-__-__-__-__-M1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S2-G0-__-C0-__-__-__-__-__-__-__.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S2-G0-__-C1-V0-T0-__-__-__-__-__.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S2-G0-__-C1-V0-T1-__-__-__-__-__.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S2-G0-__-C1-V1-T0-__-__-__-__-__.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S2-G0-__-C1-V1-T1-__-__-__-__-__.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S2-G0-__-C1-V2-T0-__-__-__-__-__.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S2-G0-__-C1-V2-T1-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C0-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C0-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C0-__-__-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C0-__-__-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C0-__-__-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C0-__-__-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L0-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L0-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L0-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L0-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L0-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L0-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L1-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L1-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L1-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L0-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L0-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L0-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L0-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L0-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L0-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L0-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L0-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L0-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L0-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L0-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L0-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V1.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V2.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D0-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D0-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D0-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D0-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D1-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D1-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D1-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D1-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D2-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D2-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D2-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D2-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D0-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D0-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D0-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D0-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D1-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D1-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D1-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D1-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D2-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D2-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D2-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D2-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D0-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D0-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D0-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D0-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D1-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D1-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D1-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D1-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D2-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D2-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D2-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D2-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D0-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D0-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D0-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D0-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D1-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D1-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D1-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D1-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D2-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D2-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D2-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D2-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I0-D0-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I0-D0-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I0-D0-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I0-D0-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I0-D1-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I0-D1-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I0-D1-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I0-D1-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I0-D2-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I0-D2-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I0-D2-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I0-D2-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D0-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D0-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D0-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D0-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D1-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D1-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D1-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D1-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D2-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D2-G0-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D2-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D2-G1-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N1-T0-__-__-__-__-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N1-T0-__-__-__-__-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B0-N2-T0-__-__-__-__-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B0-N2-T0-__-__-__-__-R0-__-__-__-__-V0.snapshot.golden create mode 100644 test/golden-file-data/snapshot-codec/B1-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden delete mode 100644 test/golden-file-data/snapshot-codec/B1-N0-T0-__-__-__-__-R0-__-__-__-__-V0.snapshot.golden diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..31ac1085a1eaac93e39f7f4f71da6900ce48b6d3 GIT binary patch literal 49 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~KpX E0FodMw*UYD literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S0-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S0-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..9b1f35f621ef0d4c9afd3f2adbba6bd4b1d02b56 GIT binary patch literal 56 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& GfG`010uML< literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E0-__-__-__-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E0-__-__-__-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..590d4326b742da9224732c90de89765e3aa9eac3 GIT binary patch literal 77 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& NY-VU`Vu0|_X#jYb6RrRN literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C0-__-__-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C0-__-__-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..5bd0547d9a02b7b5d0f834d43d0b560bd5d748a6 GIT binary patch literal 83 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& UY-VU`Vu0|#qFNFWP##ni0QIXB6+!?2 literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V0-L1-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V0-L1-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..4486120d3f5546a9d099b46b580f2962d112b22f GIT binary patch literal 84 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& aY-VU`Vu0|P7+Znjj6iijMGZh2A`Acw<`qK# literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V1-L0-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V1-L0-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..7667b0de6236d79b1febb92698976783d4d48714 GIT binary patch literal 90 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& aY-VU`Vu0|P7+Znjj6iiDMG!Ftm?Qv@%@#NS literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V1-L1-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V1-L1-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..69dc6ace96561edbbc2e3ca37ca5ea7e4a0798be GIT binary patch literal 90 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& bY-VU`Vu0|P7+Znjj6iiDMG!F{55fQdkk%GC literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V2-L0-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V2-L0-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..1fe79dfb1897d2c5da3d565d674e140970c58308 GIT binary patch literal 96 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& dY-VU`Vu0|P7+Znjj6iijMNJSf2+06b0stD87cKw* literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V2-L1-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-E1-C1-V2-L1-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..d8113504b47ce3bace08ca35ea42d17b16abaebe GIT binary patch literal 96 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& dY-VU`Vu0|P7+Znjj6iijMNJSf2niH}FaR2#7cT$+ literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-__-__-__-__-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E0-__-__-__-__-__-__-__-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..47f1a17b2b9912f8963fe08e40481ea958e2241b GIT binary patch literal 69 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& NY-VU|Vu0{qGyvW&5;gz; literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E0-__-__-__-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E0-__-__-__-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..649852ba132ceac8b90c195024430d1e4de9d814 GIT binary patch literal 83 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& RY-WJ)v?L-RVoeOFGywLQ6y*Q_ literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C0-__-__-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C0-__-__-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..5a026e93956e4129f343fadba714ec872ded5790 GIT binary patch literal 89 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& UY-WJ)v?L-RVqiH0j{zbI0DA=%8~^|S literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V0-L0-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V0-L0-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..ac04197c4e40eee0beb576605d217fe8cff87d73 GIT binary patch literal 90 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& dY-WJ)v?L-RVoi*#Kv_nhYM{~vh9(AxFaVQZ7I^>w literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V0-L1-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V0-L1-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..fb1b40ff3f31e75a931ddf244d1a1e138f7e5d77 GIT binary patch literal 90 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& cY-WJ)v?L-RVoi*#Kv_nhYM{~vAPo@)0F!7IdH?_b literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V1-L0-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V1-L0-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..410b4f2f41810b8791b1d879b48fa2354bfa53e0 GIT binary patch literal 96 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& eY-WJ)v?L-RVoi*#Kv_nhYLHTh7(){SgaH5^s26Vl literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V1-L1-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V1-L1-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..4d43bff91bc752305be7a4c02956ec9b114cbf39 GIT binary patch literal 96 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& dY-WJ)v?L-RVoi*#Kv_nhYLHTh7?1~H0017b7jOUo literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V2-L0-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V2-L0-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..e1de6317b8526291c9daef2cad206937031e0680 GIT binary patch literal 102 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& gY-WJ)v?L-RVoi*#Kv_nhYM|04h!})qXkvgc0I#eVWdHyG literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V2-L1-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-E1-C1-V2-L1-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..1004eac794285359d111b89658ccd152bbaa7721 GIT binary patch literal 102 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& fY-WJ)v?L-RVoi*#Kv_nhYM|04h!}(fia{6vueBIv literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-__-__-__-__-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C0-__-__-__-__-__-__-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..fb21e975cdcca8ac8f87cc58ba8d6c1a7e4d7bdd GIT binary patch literal 75 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& SY-VT#^Ry%)AYx4n5E=kdffH>2 literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E0-__-__-__-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E0-__-__-__-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..a70e4e0e0378043eb98ebb6eda9b7a92f387017c GIT binary patch literal 84 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& XY-RxRT7i5Cv0hPxVc>n+a literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E1-C1-V0-L0-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E1-C1-V0-L0-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..c7f80bb2c60c6b558f534264c341963ec6c1486e GIT binary patch literal 91 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& XY-RxRT7i5gfIX*{uj*v literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E1-C1-V2-L0-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L0-E1-C1-V2-L0-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..0bdc26cd6c2c21866cb1f1f5bf7fa1798c2b4df7 GIT binary patch literal 103 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& dY-RxRT7i5Cv0hQhsdH?_b literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E1-C1-V0-L0-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E1-C1-V0-L0-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..efe9fbd8b0f018905d1c773b6a8c61525f37ed96 GIT binary patch literal 91 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& ZY-RxRT7i5V!Z literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E1-C1-V1-L1-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E1-C1-V1-L1-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..427c004210f94d3668a9d7937a39f299a7fc3f6c GIT binary patch literal 97 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& aY-RxRT7i5gfIX+92d?2 literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E1-C1-V2-L0-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V0-L1-E1-C1-V2-L0-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..9fde7f87507c193d1d3fe68639b4931c6503b72e GIT binary patch literal 103 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& dY-RxRT7i57%&5C#Bbav8Y* literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L1-E1-C1-V2-L1-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V1-L1-E1-C1-V2-L1-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..7827791b6f4cac83b569ad8e6143e0bf63914796 GIT binary patch literal 109 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& cY-RxRT7i5Xan8o?JX literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L0-E1-C0-__-__-M1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S1-P0-E1-C1-V2-L0-E1-C0-__-__-M1.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..4bc4707d130e91172c1dfd8ec62998478dfd0082 GIT binary patch literal 102 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& gY-RxRT7i5OT}cm3e}M4tTD4Y_ENe{(Y% z-_hD5FZv+@*tZK_AL@l-lhd4Dan7-ZhEE|ai(*ze7XYyW@eKU?%VzZ+&Lm=ovlW7Q zFE27-Ry`kP)*&!j)U5%?ykN>q35`0b9tGDn_v#k$UICZ4SPti-8 zU#*;+&GkXG<}Q$B^s=s%nO1Lh1qxX$t?`Z~lizV#V`V0$yde(3?SpED*4PEIv{?XP zeOoi2uiSVJuc>e08+bQv!sx=HEBi3AF!Yby>WtQF02bi@j@b>~i~2J1@^Y6Ay#KNr qWYxMG%mUilivfUV5zM*uVt< literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S2-G0-__-C0-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M1-S2-G0-__-C0-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..bbfd888bf54a376fe9e8531f701287bb38dbcfed GIT binary patch literal 62 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~Fh& L0y4BDA|PAAC diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L0-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V0-L0-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..8b0f3e6411bb1a6baffc0dad41879a3c6dc7fc03 GIT binary patch literal 73 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(j-kcg1bYGrI; VU<3*S#TpozT7Y5$0|13n6W{;< literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L0-V2.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L0-V2.snapshot.golden deleted file mode 100644 index ac318f27a964b66d756e10ffc2bc95df49df327f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(d*kkFEdkkD#o LY?RPqXaaEnB61JC diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..fbe575dfb8e1a105ad7b851784aaaf1c5a68dfed GIT binary patch literal 67 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(j-kcg1bYGrI; RU<3+-#9DwN42+Bo-vFqY5%mB7 literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V0.snapshot.golden deleted file mode 100644 index 7e98afdd0ea5b309d58823366ec5eac741fce193..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(d*kkFEdkkD#o KY?RPqYybe{(GLm$ diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..32de5aa503a6bc4e4cc4e8eff907550b039768c5 GIT binary patch literal 73 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(j-kcg1bYGrI; SU<3+-#9DwN42+CW`WpZgAQI~U literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V1.snapshot.golden deleted file mode 100644 index c30beb6de21a3ed3befc49144a9faf609b125f47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(d*kkFEdkkD#o JY?ROfVE_YB4^037 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..aaecb898cc89039360c3931af816228d3e0feb25 GIT binary patch literal 79 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(j-kcg1bYGrI; VU<3+-#9DwN42+CT5E?>$0|13$6X5^= literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V2.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V1-L1-V2.snapshot.golden deleted file mode 100644 index 8e2e2425e5bfdeef80191c540c0f1e77e9950145..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(d*kkFEdkkD#o LY?RPqYyxorB6ttJ diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..12bdc571f135ad8235feff875ae31b6c43b98a0f GIT binary patch literal 73 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(j-kcg1bYGrI; VU<3*S#hO}xA`C!2h-7H^1^^QY66yc| literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V0.snapshot.golden deleted file mode 100644 index 3910821174a5119c09309928fb1f648249751030..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(d*kkFEdkkD#o JYyvSD8UO=d4|)Iq diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..bc9585187706533168c37a7444e35fbed49f73c2 GIT binary patch literal 79 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(j-kcg1bYGrI; WU<3*S#hO}xA`C!2h-7Gl(BA-rKNH{p literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L0-V1.snapshot.golden deleted file mode 100644 index 15f5309a8efdd4152a881494a346f5fe01d442eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(d*kkFEdkkD#o LYyvSD8YQ# diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..99045113372c12da8e0e2281d9e9ea990c4b7664 GIT binary patch literal 73 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(j-kcg1bYGrI; VU<3*S#hO}xA`C!2h-7T|1^^Qb66*i} literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V0.snapshot.golden deleted file mode 100644 index d36d4b26bc8d0f8931daaec8fb8585a2118e2799..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(d*kkFEdkkD#o JYyvSD8vp}g4|@Or diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..d590f35d5abf632d58f5521ac2603ea78e6a0c66 GIT binary patch literal 79 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(j-kcg1bYGrI; WU<3*S#hO}xA`C!2h-7Sp(BA-rNE6`z literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V1.snapshot.golden deleted file mode 100644 index 7c6db8f69e641494ce50e943c01b6410cc8f0264..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(d*kkFEdkkD#o LYyvSD8zr;=BD4>_ diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..a29e1dcf94d1080b44fb94d78fa7d00deecec87a GIT binary patch literal 85 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(j-kcg1bYGrI; WU<3*S#hO}xA`C!2h-5_JeFFgdP88Pw literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V2.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P0-C1-V2-L1-V2.snapshot.golden deleted file mode 100644 index 551acf1c51460519bbe4db3c6e88919be76110a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-(d*kkFEdkkD#o JYyvSLBmhFa5HA1# diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..d67a0f1c83567d602e870f6c3c61b889a7df8d84 GIT binary patch literal 60 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?U<`5?T@w LKr(`XvEds4GhGm( literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V0.snapshot.golden deleted file mode 100644 index 0c073099dfa03b464ee0f63a319674e536a2c562..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwT1^ZR G5e)#w+YYq= diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..357762c7b394cc05d2fbf22ff29a46d89427156c GIT binary patch literal 66 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?U<`5?T@w NKr(`Xu@ORj0|1ue5tsk~ literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C0-__-__-V1.snapshot.golden deleted file mode 100644 index af82b738fc40dbc8b2ef4a569050ebd550daf343..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwT1^Z< Hu7nl<i_@% literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L0-V2.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L0-V2.snapshot.golden deleted file mode 100644 index 64ff2d0b68925f48c68b97f4ad550a7eaa64bb39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs0N I3{4;=00ajQdjJ3c diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..9f7e76f2f0ef1f63979379d619dbca0aacc25396 GIT binary patch literal 61 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 LVIZf0vEds4L=zDJ literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V0.snapshot.golden deleted file mode 100644 index 1290e2388cd7aaf7f64b8760f95e5cc0fab91cbd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs0N Gj12(CZVt!* diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..cd8b1e54d648184ede86c15e8cf28bfc0112e15a GIT binary patch literal 67 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 RVIZf0v9X0gB7%YO8vv=z5%vH8 literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V1.snapshot.golden deleted file mode 100644 index 3aeea05a2ec2c1475a73965272927cec97cac732..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs0N IjExdn0OeZ`3jhEB diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..f248050c4f17980ea3113fdcb29352748797cefa GIT binary patch literal 73 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 UVIZf0v8jbYB7y-Z0wTWw02N>o>;M1& literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V2.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V0-L1-V2.snapshot.golden deleted file mode 100644 index 7fad35275deeba4b190777e46980584a94dd54b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs0N Ij7=aW00a#Wd;kCd diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..f2c436a16f274df7809c99e42471d0c2423eff3a GIT binary patch literal 67 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 QVUSo0gG2-aBSXVC0I0AL^#A|> literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V0.snapshot.golden deleted file mode 100644 index 648e240c5bacaf03a308b9dc941d7153311f45f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs1& I5?Txm0OZ{d3IG5A diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..da694d8d6263896baedd1423e27f0d5124e77adc GIT binary patch literal 73 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 RVUSo0gG2-aBLkHF1^^T{66*i} literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V1.snapshot.golden deleted file mode 100644 index bd58702ed4039d4e8a9a94f332906d9ca9c732e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs1& J5?TyE3;+XN4^037 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..95657829d1611817660a85ab3befc8986c204744 GIT binary patch literal 79 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 TVUSo0gG2-aBSRB}hLGO?g?SU< literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V2.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L0-V2.snapshot.golden deleted file mode 100644 index d5a385cf442ee6b721ad868cce1d67c2f65910b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs1& K5?TyRAPxW`gb%&| diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..36e8c65aafe72b056dbf50daf1119294b3b5922e GIT binary patch literal 67 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 QVUSo0gG2-aBV)ri0I0JO_5c6? literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V0.snapshot.golden deleted file mode 100644 index 61da53a4c84245e4e6bd8f301d2c7debcd9341ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs1& I5?YK60Oa2f3jhEB diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..2ea5bd4da2d4d9d1e06ec58d26b62631550f4189 GIT binary patch literal 73 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 RVUSo0gG2-aBO{dl1^^U566^o~ literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V1.snapshot.golden deleted file mode 100644 index 24d11390d503f215f81e222d5b14efdd7af6ee89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs1& I5?UY(00UtUP5=M^ diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..fd07e67887ee1475f5b1fd8e951dc7546c9dc07a GIT binary patch literal 79 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 TVUSo0gG2-aBV!YUhLGO?g@_a4 literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V2.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V1-L1-V2.snapshot.golden deleted file mode 100644 index 0c07f9bf4183c65995f341c433158730042c436a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs1& K5?YK+APxW`iVwd4 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..5d6b789b828ddbceb3ea28a8899fbbbb589e9846 GIT binary patch literal 73 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 TVIZfeg+U^M0muiD3=Q7^6Dbnw literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V0.snapshot.golden deleted file mode 100644 index fb26e42a4a3b39b4ec306049a53c52715515ad36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs12 IAO=GN00VCidjJ3c diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..9d25a6f5fdbe7a619366b4f8bd0daf26289875b8 GIT binary patch literal 79 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 VVIZfeg+U^M0muiD42=-_8vul56X5^= literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V1.snapshot.golden deleted file mode 100644 index 5d891aa852221a34644a430acd96c56ba00968b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs12 KAO=IDgcblI!VkUx diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..2eddd844a01219949cea6a491eb130463ae196e1 GIT binary patch literal 85 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 UVIZfeg+U^M0muiD3@E&B0Q+kc*8l(j literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V2.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L0-V2.snapshot.golden deleted file mode 100644 index 9b4018721769c27889024de9653681d950d10818..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs12 JAO-`N1OP(I5HA1# diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..c670c4e6c2398ec22d3de6c31103386cb34c95f6 GIT binary patch literal 73 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 TVIZfeg+U^M0muiDj1Aua6D$(! literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V0.snapshot.golden deleted file mode 100644 index fd63ab5e554c295ad038dd65c917c1e8a82e27da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs12 IAO>Rt00VIkd;kCd diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V1-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..5c9cf09b6e9b453aabc50bdadc2a17343e435bf7 GIT binary patch literal 79 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 VVIZfeg+U^M0muiDjExZb8vulE6XE~> literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V1.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V1.snapshot.golden deleted file mode 100644 index 7045efbd5621012b7b6480f0b9b399294232e921..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs12 KAO>TjgcblI#t**$ diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V2-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..ca304ccd0be040d9fd82b92253348f09e7d588ae GIT binary patch literal 85 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$Bj5?ZZ{Eewo6 UVIZfeg+U^M0muiDj3~Ts0Q-6r*Z=?k literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V2.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T0-__-__-__-__-R1-P1-C1-V2-L1-V2.snapshot.golden deleted file mode 100644 index 48e2ab483982a6144a45136cbcd6463a0d973d7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I^iybJ;9_WGV3dexY-$5iS`rZwTFs12 IAO?g407B6aFaQ7m diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D0-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D0-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..dab3cbb9478629f07f7d92625fe7653ae84f02cc GIT binary patch literal 50 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=vWMGhpXl!a?Yyr{?j1Aua Dm!l4Z literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D0-G0-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D0-G0-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index e8e87c034d17ab2db84b3beaba6c1f67b50e077f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=vWMGhpXl!a?l!#~m08|hS A2mk;8 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D0-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D0-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..468dc36697ae30af0a2413eeb5b6b779fd14ac0b GIT binary patch literal 50 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=vWMGttXl!a?Y+;ayU|?+c F1^}0`4ut>! literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D0-G1-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D0-G1-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index a6a6fa47c7863c6d0a2c94c6d278c85183c1504e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45 ycmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=vWMGttXl!Z%k_`Y<8Vv~m diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D1-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D1-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..327cbad86e51ccb45de35a191400bf735007908e GIT binary patch literal 50 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=vWMYtrXl!a?Yyr{?j1Aua Dm%|Q* literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D1-G0-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D1-G0-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index cc88afacf6b5f277cfac2427f10f73a7ffc7666c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=vWMYtrXl!a?l!#~m08}Oo A3IG5A diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D1-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D1-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..52bd7af22ca2497a74f8612d46c6b7f58d22171c GIT binary patch literal 50 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=vWMY(vXl!a?Y+;ayU|?+c F1^}1R4u=2$ literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D1-G1-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I0-D1-G1-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index 0de4c0c0791a35d12ef61aa2932694e39fdc0227..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45 ycmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=vWMY(vXl!Z%k_`Y<{9 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D0-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D0-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..c287933e022ee3b2e99d6509fab5ac52e118a3a8 GIT binary patch literal 50 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=zWMGttXl!a?Y+;ayU|?+c F1^}1D4u${# literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D0-G1-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D0-G1-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index 9293ffbc02dde60d8801a23d74c42905b11d27c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45 ycmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=zWMGttXl!Z%k_`Yo3 literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D1-G0-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D1-G0-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index 31c3c55e660c03ee16d4c0d924705aceb0e13cf2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=zWMYtrXl!a?l!#~m08}## A3jhEB diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D1-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D1-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..234e4b5821def2fc0cb253513b450819cb0b53b7 GIT binary patch literal 50 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=zWMY(vXl!a?Y+;ayU|?+c F1^}1j4u}8% literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D1-G1-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D1-G1-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index e0298d10043dc741dec9e15303e82e1561a89b46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45 ycmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=zWMY(vXl!Z%k_`YIhyVZp literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D2-G0-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D2-G0-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index 644ea33d1b81a9eb3b6208b37821bf1b8b87d142..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=zVq|2Hh-hqTVw8wz003TO B4GaJP diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D2-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D2-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..d956e40b958073b05817caec818e1b6d416b2dfa GIT binary patch literal 51 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=zVq|2Lh-hqTVr*fMh+trB G_yz!?8xDy8 literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D2-G1-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A0-I1-D2-G1-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index 5c6208a65097a5489d93515fa602bad695233014..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w3kWMN=zVq|2Lh-hqT0+I~?UTzHy diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D0-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D0-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..c3df18e18ae8d51d0a9e07dc05bdbac02e129d0f GIT binary patch literal 56 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w4*?ZBz{JH$m-f}xRtK_a5Dsfn=# KNHZ`td;9n F007A25Gnuw diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D1-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D1-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..0627408917f4590e22e1cb5ab69fe8d5420221f1 GIT binary patch literal 56 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w4*?ZBz{JH$m-f}xR#K_a5Dsfn=# KNHZ`td;9n F007AO5G()y diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D2-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D2-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..8a3759135663f2d64988870dacaec7f7fc3d0458 GIT binary patch literal 57 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w4*?ZBz{JH$m-f}x3#kwGG&v8jo% L1xPb6HhcpBDV-6h literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D2-G0-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D2-G0-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index 90d9a7699b6c217c67b1fb35d2c9922ab9217eab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w4*?ZBz{JH$m-f}x3#kwGG&v8jns HBBB8R)<6&} diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D2-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D2-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..e813f6aa7a7ad803054b2edb1bf48d7cf665e8b3 GIT binary patch literal 57 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w4*?ZBz{JH$m-f}x3#kx?R|v8jo% Mg+U^MfwAEm04b~ysQ>@~ literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D2-G1-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I0-D2-G1-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index c9e8daec41e209951218eeef00438203e01762f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w4*?ZBz{JH$m-f}x3#kx?R|v8f42 GHUI$DN)Rmo diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D0-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D0-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..ddaf8fd8e84ba387a9b6f3ba1b35981f2be1a6c6 GIT binary patch literal 56 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w4*?ZBz{JH$m-g0Yc-K_a5Dsfn=# KNHZ`td;9n F007AF5Gw!x diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D1-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D1-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..3ea1e8eb0799cfebc8ea9d810ae924daa94a3bbe GIT binary patch literal 56 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w4*?ZBz{JH$m-g0Yc_K_a5Dsfn=# KNHZ`td;9n F007Ab5G?=z diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D2-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A1-I1-D2-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..57c395cf88b19c412653013f5380d68026d1e873 GIT binary patch literal 57 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}w4*?ZBz{JH$m-g0YE_kwGG&v8jo% L1xPb6HhcpBDX|r2qf` literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D1-G1-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D1-G1-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index 84df01a0e71eed0a8f7621d652eb35d209eaab2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}wFqWMPnGWMFJ$Vw8wzY-$3M4FGcd B4JiNs diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D2-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D2-G0-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..c7b036d54a7d7dbc63219f1ad0554035e5808334 GIT binary patch literal 54 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}wFqWMPnGWMFJ!WMq(tXl!a?Yyr{? Hj1Aua!F&#- literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D2-G0-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D2-G0-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index bf4e8a2eb4aeb46760deb357159e88514210759f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}wFqWMPnGWMFJ!WMq(tXl!a?l!#~m E0DV9WDF6Tf diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D2-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D2-G1-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..1ec525ef1b775107772b5a6174c4dafe59cfe435 GIT binary patch literal 54 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}wFqWMPnGWMFJ!WMq_xXl!a?Y+;ay JU|?+c1^~f}4yFJA literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D2-G1-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N0-T1-A2-I1-D2-G1-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index 8173c3803960ef200b5ad5064d8b4d7c3d34325d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49 zcmZoI3@uJA3Mk4i%S=g4@kvZd&0%O~U}<8I&}wFqWMPnGWMFJ!WMq_xXl!Z%k_`ZT CN)0Li diff --git a/test/golden-file-data/snapshot-codec/B0-N1-T0-__-__-__-__-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N1-T0-__-__-__-__-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..4fdc747cdc05f5f3d5b9b9974e7f5dee011bdc87 GIT binary patch literal 49 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%b3U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~KpX E0FpiqxBvhE literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N1-T0-__-__-__-__-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N1-T0-__-__-__-__-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index e928daf2a34083bff6d24c2906a57ab7f07ef689..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 xcmZoI3@uJA3Mk4i%S=g4@kvZd&0%b3U}<8I^iybJ;9_WGV3dexY-$3M4FFB34Lkq< diff --git a/test/golden-file-data/snapshot-codec/B0-N2-T0-__-__-__-__-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N2-T0-__-__-__-__-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..12a7ce10920d8c796ff19de40886845a2048c7b2 GIT binary patch literal 49 zcmZoG3@uJA3Mk4i%S=g4@kvZd&0%V1U}<8I^iybJ;9_WGV3dexY-(a`VUUPmU~KpX E0Fqn|xc~qF literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B0-N2-T0-__-__-__-__-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B0-N2-T0-__-__-__-__-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index cfe2ab033b8fa3007d72a22cb5773ec7498166e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 xcmZoI3@uJA3Mk4i%S=g4@kvZd&0%V1U}<8I^iybJ;9_WGV3dexY-$3M4FFBS4Ltw= diff --git a/test/golden-file-data/snapshot-codec/B1-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden b/test/golden-file-data/snapshot-codec/B1-N0-T0-__-__-__-__-R0-__-__-__-__-V0-M0-__-__-__-__-__-__-__-__-__-__-__.snapshot.golden new file mode 100644 index 0000000000000000000000000000000000000000..9bd6a12c4d4a300c4a93e87b0b710db97a2f42ff GIT binary patch literal 32 ncmZomU}$GxX=0G{Q)pt~VrXPwl!$0-YGQ0*kcePlZ1@HMcy$Nr literal 0 HcmV?d00001 diff --git a/test/golden-file-data/snapshot-codec/B1-N0-T0-__-__-__-__-R0-__-__-__-__-V0.snapshot.golden b/test/golden-file-data/snapshot-codec/B1-N0-T0-__-__-__-__-R0-__-__-__-__-V0.snapshot.golden deleted file mode 100644 index 9f56f70728b8fb5ddc933a194efdac9eb7105ea6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27 gcmZooU}$GxX=0G{Q)pt~VrXPwl!$0-Y66lC09Ez|ZU6uP From 52034089aba1ee7d6d6e8447a6a8d82d5a978780 Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Wed, 12 Mar 2025 12:34:10 +0000 Subject: [PATCH 09/15] Use more consistent reference handling for newIncomingMergingRun It should duplicate its input reference for consistency, since it retains it. --- .../LSMTree/Internal/MergeSchedule.hs | 16 +++++++------- src/Database/LSMTree/Internal/Snapshot.hs | 22 +++++++++++-------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/Database/LSMTree/Internal/MergeSchedule.hs b/src/Database/LSMTree/Internal/MergeSchedule.hs index 8ff9ad930..ab7a96959 100644 --- a/src/Database/LSMTree/Internal/MergeSchedule.hs +++ b/src/Database/LSMTree/Internal/MergeSchedule.hs @@ -420,14 +420,14 @@ newIncomingSingleRun r = Single <$> dupRef r {-# INLINE newIncomingMergingRun #-} newIncomingMergingRun :: - PrimMonad m + (PrimMonad m, MonadThrow m) => MergePolicyForLevel -> NominalDebt -> Ref (MergingRun MR.LevelMergeType m h) -> m (IncomingRun m h) newIncomingMergingRun mergePolicy nominalDebt mr = do nominalCreditsVar <- newPrimVar (NominalCredits 0) - return (Merging mergePolicy nominalDebt nominalCreditsVar mr) + Merging mergePolicy nominalDebt nominalCreditsVar <$> dupRef mr {-# SPECIALISE supplyCreditsIncomingRun :: TableConfig @@ -1029,12 +1029,12 @@ newIncomingRunAtLevel tr hfs hbio TraceNewMerge (V.map Run.size rs) (runNumber runPaths) runParams mergePolicy mergeType - mr <- MR.new hfs hbio resolve runParams mergeType runPaths rs - - assert (MR.totalMergeDebt mr <= maxMergeDebt conf mergePolicy ln) $ pure () - - let nominalDebt = nominalDebtForLevel conf ln - newIncomingMergingRun mergePolicy nominalDebt mr + bracket + (MR.new hfs hbio resolve runParams mergeType runPaths rs) + releaseRef $ \mr -> + assert (MR.totalMergeDebt mr <= maxMergeDebt conf mergePolicy ln) $ + let nominalDebt = nominalDebtForLevel conf ln in + newIncomingMergingRun mergePolicy nominalDebt mr mergingRunParamsForLevel :: ActiveDir diff --git a/src/Database/LSMTree/Internal/Snapshot.hs b/src/Database/LSMTree/Internal/Snapshot.hs index 123c9cdfc..334191fed 100644 --- a/src/Database/LSMTree/Internal/Snapshot.hs +++ b/src/Database/LSMTree/Internal/Snapshot.hs @@ -39,7 +39,8 @@ import Control.Concurrent.Class.MonadSTM (MonadSTM) import Control.DeepSeq (NFData (..)) import Control.Monad (void) import Control.Monad.Class.MonadST (MonadST) -import Control.Monad.Class.MonadThrow (MonadMask, bracketOnError) +import Control.Monad.Class.MonadThrow (MonadMask, bracket, + bracketOnError) import Control.Monad.Primitive (PrimMonad) import Control.RefCount import Data.Foldable (sequenceA_) @@ -652,14 +653,17 @@ fromSnapLevels hfs hbio uc conf resolve reg dir (SnapLevels levels) = newIncomingSingleRun run fromSnapIncomingRun ln (SnapMergingRun mergePolicy nominalDebt - nominalCredits smrs) = do - mr <- fromSnapMergingRunState hfs hbio uc resolve dir smrs - ir <- newIncomingMergingRun mergePolicy nominalDebt mr - -- This will set the correct nominal credits, but it will not do any more - -- merging work because fromSnapMergingRunState already supplies all the - -- merging credits already. - supplyCreditsIncomingRun conf ln ir nominalCredits - return ir + nominalCredits smrs) = + bracket + (fromSnapMergingRunState hfs hbio uc resolve dir smrs) + releaseRef $ \mr -> do + + ir <- newIncomingMergingRun mergePolicy nominalDebt mr + -- This will set the correct nominal credits, but it will not do any + -- more merging work because fromSnapMergingRunState already supplies + -- all the merging credits already. + supplyCreditsIncomingRun conf ln ir nominalCredits + return ir {-# SPECIALISE fromSnapMergingRunState :: MR.IsMergeType t From 5cbb822512030d2d36c165b39ee7481862faee09 Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Mon, 10 Mar 2025 11:18:16 +0000 Subject: [PATCH 10/15] Fix the reference handling for fromSnapMergingTree Also tweak the MergingTree constructor utilities to make this more straightforward. --- .../LSMTree/Extras/MergingTreeData.hs | 12 +- src/Database/LSMTree/Internal.hs | 5 +- src/Database/LSMTree/Internal/MergingTree.hs | 12 +- src/Database/LSMTree/Internal/Snapshot.hs | 109 +++++++++++------- 4 files changed, 88 insertions(+), 50 deletions(-) diff --git a/src-extras/Database/LSMTree/Extras/MergingTreeData.hs b/src-extras/Database/LSMTree/Extras/MergingTreeData.hs index 7d4b7915b..013bccc8e 100644 --- a/src-extras/Database/LSMTree/Extras/MergingTreeData.hs +++ b/src-extras/Database/LSMTree/Extras/MergingTreeData.hs @@ -74,17 +74,17 @@ unsafeCreateMergingTree :: unsafeCreateMergingTree hfs hbio resolve indexType path counter = go where go = \case - CompletedTreeMergeData rd -> do + CompletedTreeMergeData rd -> withRun hfs hbio indexType path counter rd $ \run -> - MT.mkMergingTree . MT.CompletedTreeMerge =<< dupRef run - OngoingTreeMergeData mrd -> do + MT.newCompletedMerge run + OngoingTreeMergeData mrd -> withMergingRun hfs hbio resolve indexType path counter mrd $ \mr -> - MT.mkMergingTree . MT.OngoingTreeMerge =<< dupRef mr - PendingLevelMergeData prds mtd -> do + MT.newOngoingMerge mr + PendingLevelMergeData prds mtd -> withPreExistingRuns prds $ \prs -> withMaybeTree mtd $ \mt -> MT.newPendingLevelMerge prs mt - PendingUnionMergeData mtds -> do + PendingUnionMergeData mtds -> withTrees mtds $ \mts -> MT.newPendingUnionMerge mts diff --git a/src/Database/LSMTree/Internal.hs b/src/Database/LSMTree/Internal.hs index 1f0b2efc4..3e4ce6e12 100644 --- a/src/Database/LSMTree/Internal.hs +++ b/src/Database/LSMTree/Internal.hs @@ -1325,12 +1325,13 @@ openSnapshot sesh label tableType override snap resolve = do Nothing -> pure NoUnion Just mTree -> do snapTree <- traverse (openRun hfs hbio uc reg snapDir activeDir) mTree - Union <$> fromSnapMergingTree reg hfs hbio conf uc resolve activeDir snapTree + mt <- fromSnapMergingTree hfs hbio uc resolve activeDir reg snapTree + traverse_ (delayedCommit reg . releaseRef) snapTree + pure (Union mt) -- Convert from the snapshot format, restoring merge progress in the process tableLevels <- fromSnapLevels hfs hbio uc conf resolve reg activeDir snapLevels' traverse_ (delayedCommit reg . releaseRef) snapLevels' - --TODO: also delayedCommit unionLevel tableCache <- mkLevelsCache reg tableLevels newWith reg sesh seshEnv conf' am $! TableContent { diff --git a/src/Database/LSMTree/Internal/MergingTree.hs b/src/Database/LSMTree/Internal/MergingTree.hs index e37ac87c2..7ed4b21d2 100644 --- a/src/Database/LSMTree/Internal/MergingTree.hs +++ b/src/Database/LSMTree/Internal/MergingTree.hs @@ -2,15 +2,15 @@ module Database.LSMTree.Internal.MergingTree ( -- $mergingtrees MergingTree (..) - , newOngoingMerge , PreExistingRun (..) + , newCompletedMerge + , newOngoingMerge , newPendingLevelMerge , newPendingUnionMerge , isStructurallyEmpty -- * Internal state , MergingTreeState (..) , PendingMerge (..) - , mkMergingTree ) where import Control.Concurrent.Class.MonadMVar.Strict @@ -103,6 +103,12 @@ data PreExistingRun m h = PreExistingRun !(Ref (Run m h)) | PreExistingMergingRun !(Ref (MergingRun MR.LevelMergeType m h)) +newCompletedMerge :: + (MonadMVar m, PrimMonad m, MonadMask m) + => Ref (Run m h) + -> m (Ref (MergingTree m h)) +newCompletedMerge run = mkMergingTree . CompletedTreeMerge =<< dupRef run + -- | Create a new 'MergingTree' representing the merge of an ongoing run. -- The usage of this function is primarily to facilitate the reloading of an -- ongoing merge from a persistent snapshot. @@ -110,7 +116,7 @@ newOngoingMerge :: (MonadMVar m, PrimMonad m, MonadMask m) => Ref (MergingRun MR.TreeMergeType m h) -> m (Ref (MergingTree m h)) -newOngoingMerge = mkMergingTree . OngoingTreeMerge +newOngoingMerge mr = mkMergingTree . OngoingTreeMerge =<< dupRef mr -- | Create a new 'MergingTree' representing the merge of a sequence of -- pre-existing runs (completed or ongoing, plus a optional final tree). diff --git a/src/Database/LSMTree/Internal/Snapshot.hs b/src/Database/LSMTree/Internal/Snapshot.hs index 334191fed..0872a2176 100644 --- a/src/Database/LSMTree/Internal/Snapshot.hs +++ b/src/Database/LSMTree/Internal/Snapshot.hs @@ -43,7 +43,7 @@ import Control.Monad.Class.MonadThrow (MonadMask, bracket, bracketOnError) import Control.Monad.Primitive (PrimMonad) import Control.RefCount -import Data.Foldable (sequenceA_) +import Data.Foldable (sequenceA_, traverse_) import Data.Text (Text) import qualified Data.Vector as V import Database.LSMTree.Internal.Config @@ -245,60 +245,91 @@ instance NFData r => NFData (SnapPreExistingRun r) where -------------------------------------------------------------------------------} {-# SPECIALISE fromSnapMergingTree :: - ActionRegistry IO - -> HasFS IO h + HasFS IO h -> HasBlockIO IO h - -> TableConfig -> UniqCounter IO -> ResolveSerialisedValue -> ActiveDir + -> ActionRegistry IO -> SnapMergingTree (Ref (Run IO h)) -> IO (Ref (MT.MergingTree IO h)) #-} --- | Duplicates runs and re-creates merging runs. +-- | Converts a snapshot of a merging tree of runs to a real merging tree. +-- +-- Returns a new reference. Input runs remain owned by the caller. fromSnapMergingTree :: forall m h. (MonadMask m, MonadMVar m, MonadSTM m, MonadST m) - => ActionRegistry m - -> HasFS m h + => HasFS m h -> HasBlockIO m h - -> TableConfig -> UniqCounter m -> ResolveSerialisedValue -> ActiveDir + -> ActionRegistry m -> SnapMergingTree (Ref (Run m h)) -> m (Ref (MT.MergingTree m h)) -fromSnapMergingTree reg hfs hbio conf uc resolve dir (SnapMergingTree snapTreeState) = - fromSnapTreeState snapTreeState +fromSnapMergingTree hfs hbio uc resolve dir = + go where - -- Partially applied functions for convenience - recurrence :: SnapMergingTree (Ref (Run m h)) -> m (Ref (MT.MergingTree m h)) - recurrence = fromSnapMergingTree reg hfs hbio conf uc resolve dir - - getSnapMergingRunState - :: forall t. - MR.IsMergeType t - => SnapMergingRunState t (Ref (Run m h)) - -> m (Ref (MR.MergingRun t m h)) - getSnapMergingRunState = fromSnapMergingRunState hfs hbio uc resolve dir - - -- Conversion definitions - fromSnapTreeState :: SnapMergingTreeState (Ref (Run m h)) -> m (Ref (MT.MergingTree m h)) - fromSnapTreeState (SnapCompletedTreeMerge run) = - MT.newPendingLevelMerge [MT.PreExistingRun run] Nothing - fromSnapTreeState (SnapPendingTreeMerge pMerge) = case pMerge of - SnapPendingLevelMerge peRuns maybeMergeTree -> do - peRuns' <- traverse fromSnapPreExistingRun peRuns - maybeMergeTree' <- traverse recurrence maybeMergeTree - MT.newPendingLevelMerge peRuns' maybeMergeTree' - SnapPendingUnionMerge mergeTrees -> - MT.newPendingUnionMerge =<< traverse recurrence mergeTrees - fromSnapTreeState (SnapOngoingTreeMerge smrs) = - MT.newOngoingMerge =<< getSnapMergingRunState smrs - - fromSnapPreExistingRun :: SnapPreExistingRun (Ref (Run m h)) -> m (MT.PreExistingRun m h) - fromSnapPreExistingRun (SnapPreExistingRun run) = pure $ MT.PreExistingRun run - fromSnapPreExistingRun (SnapPreExistingMergingRun smrs) = - MT.PreExistingMergingRun <$> getSnapMergingRunState smrs + -- Reference strategy: + -- * go returns a fresh reference + -- * go ensures the returned reference will be cleaned up on failure, + -- using withRollback + -- * All results from recursive calls must be released locally on the + -- happy path. + go :: ActionRegistry m + -> SnapMergingTree (Ref (Run m h)) + -> m (Ref (MT.MergingTree m h)) + + go reg (SnapMergingTree (SnapCompletedTreeMerge run)) = + withRollback reg + (MT.newCompletedMerge run) + releaseRef + + go reg (SnapMergingTree (SnapPendingTreeMerge + (SnapPendingLevelMerge prs mmt))) = do + prs' <- traverse (fromSnapPreExistingRun reg) prs + mmt' <- traverse (go reg) mmt + mt <- withRollback reg + (MT.newPendingLevelMerge prs' mmt') + releaseRef + traverse_ (delayedCommit reg . releasePER) prs' + traverse_ (delayedCommit reg . releaseRef) mmt' + return mt + + go reg (SnapMergingTree (SnapPendingTreeMerge + (SnapPendingUnionMerge mts))) = do + mts' <- traverse (go reg) mts + mt <- withRollback reg + (MT.newPendingUnionMerge mts') + releaseRef + traverse_ (delayedCommit reg . releaseRef) mts' + return mt + + go reg (SnapMergingTree (SnapOngoingTreeMerge smrs)) = do + mr <- withRollback reg + (fromSnapMergingRunState hfs hbio uc resolve dir smrs) + releaseRef + mt <- withRollback reg + (MT.newOngoingMerge mr) + releaseRef + delayedCommit reg (releaseRef mr) + return mt + + -- Returns fresh refs, which must be released locally. + fromSnapPreExistingRun :: ActionRegistry m + -> SnapPreExistingRun (Ref (Run m h)) + -> m (MT.PreExistingRun m h) + fromSnapPreExistingRun reg (SnapPreExistingRun run) = + MT.PreExistingRun <$> + withRollback reg (dupRef run) releaseRef + fromSnapPreExistingRun reg (SnapPreExistingMergingRun smrs) = + MT.PreExistingMergingRun <$> + withRollback reg + (fromSnapMergingRunState hfs hbio uc resolve dir smrs) + releaseRef + + releasePER (MT.PreExistingRun r) = releaseRef r + releasePER (MT.PreExistingMergingRun mr) = releaseRef mr {------------------------------------------------------------------------------- Conversion to merge tree snapshot format From 45212f0dd8a8630159f224be3ca5366a4b66516d Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Fri, 7 Mar 2025 21:42:07 +0000 Subject: [PATCH 11/15] Make the snapshot codec CBOR encoding more regular Use utils for maybe, list and vector. And use a single-item encoding for Maybe, rather than it being a pair of things. The pair style means one has to remember to count it as two when setting the tuple lengths. --- .../LSMTree/Internal/Snapshot/Codec.hs | 91 ++++++++++--------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/src/Database/LSMTree/Internal/Snapshot/Codec.hs b/src/Database/LSMTree/Internal/Snapshot/Codec.hs index 0f0cb8090..5ca094328 100644 --- a/src/Database/LSMTree/Internal/Snapshot/Codec.hs +++ b/src/Database/LSMTree/Internal/Snapshot/Codec.hs @@ -20,12 +20,11 @@ import Codec.CBOR.Decoding import Codec.CBOR.Encoding import Codec.CBOR.Read import Codec.CBOR.Write -import Control.Monad (replicateM, when) +import Control.Monad (when) import Control.Monad.Class.MonadThrow (MonadThrow (..)) import Data.Bifunctor import qualified Data.ByteString.Char8 as BSC import Data.ByteString.Lazy (ByteString) -import Data.Foldable (fold) import qualified Data.Map.Strict as Map import qualified Data.Vector as V import Database.LSMTree.Internal.Config @@ -223,7 +222,7 @@ instance Decode SnapshotVersion where instance Encode SnapshotMetaData where encode (SnapshotMetaData label tableType config writeBuffer levels mergingTree) = - encodeListLen 7 + encodeListLen 6 <> encode label <> encode tableType <> encode config @@ -233,7 +232,7 @@ instance Encode SnapshotMetaData where instance DecodeVersioned SnapshotMetaData where decodeVersioned ver@V0 = do - _ <- decodeListLenOf 7 + _ <- decodeListLenOf 6 SnapshotMetaData <$> decodeVersioned ver <*> decodeVersioned ver @@ -523,14 +522,10 @@ instance DecodeVersioned MergeSchedule where -- SnapLevels instance Encode r => Encode (SnapLevels r) where - encode (SnapLevels levels) = - encodeListLen (fromIntegral (V.length levels)) - <> V.foldMap encode levels + encode (SnapLevels levels) = encode levels instance DecodeVersioned r => DecodeVersioned (SnapLevels r) where - decodeVersioned v@V0 = do - n <- decodeListLen - SnapLevels <$> V.replicateM n (decodeVersioned v) + decodeVersioned v@V0 = SnapLevels <$> decodeVersioned v -- SnapLevel @@ -549,14 +544,10 @@ instance DecodeVersioned r => DecodeVersioned (SnapLevel r) where -- Vector instance Encode r => Encode (V.Vector r) where - encode rns = - encodeListLen (fromIntegral (V.length rns)) - <> V.foldMap encode rns + encode = encodeVector instance DecodeVersioned r => DecodeVersioned (V.Vector r) where - decodeVersioned v@V0 = do - n <- decodeListLen - V.replicateM n (decodeVersioned v) + decodeVersioned = decodeVector -- RunNumber @@ -745,34 +736,23 @@ instance DecodeVersioned r => DecodeVersioned (SnapMergingTreeState r) where -- SnapPendingMerge instance Encode r => Encode (SnapPendingMerge r) where - encode (SnapPendingLevelMerge pe mt) = fold - [ encodeListLen 4 - , encodeWord 0 - , encodeMaybe mt - , encodeListLen . toEnum $ length pe - , foldMap encode pe - ] + encode (SnapPendingLevelMerge pe mt) = + encodeListLen 3 + <> encodeWord 0 + <> encodeList pe + <> encodeMaybe mt encode (SnapPendingUnionMerge mts) = - encodeListLen 2 - <> encodeWord 1 - <> encodeListLen (toEnum $ length mts) - <> foldMap encode mts + encodeListLen 2 + <> encodeWord 1 + <> encodeList mts instance DecodeVersioned r => DecodeVersioned (SnapPendingMerge r) where decodeVersioned v@V0 = do n <- decodeListLen tag <- decodeWord case (n, tag) of - (4, 0) -> do - -- Get the whether or not the levels merge exists - peLvls <- decodeMaybe v - peLen <- decodeListLen - peRuns <- replicateM peLen (decodeVersioned v) - pure $ SnapPendingLevelMerge peRuns peLvls - (2, 1) -> do - -- Get the number of pre-existsing unions to read - peLen <- decodeListLen - SnapPendingUnionMerge <$> replicateM peLen (decodeVersioned v) + (3, 0) -> SnapPendingLevelMerge <$> decodeList v <*> decodeMaybe v + (2, 1) -> SnapPendingUnionMerge <$> decodeList v _ -> fail ("[SnapPendingMerge] Unexpected combination of list length and tag: " <> show (n, tag)) -- SnapPreExistingRun @@ -798,14 +778,37 @@ instance DecodeVersioned r => DecodeVersioned (SnapPreExistingRun r) where -- Utilities for encoding/decoding Maybe values +--Note: the Maybe encoding cannot be nested like (Maybe (Maybe a)), but it is +-- ok for fields of records. encodeMaybe :: Encode a => Maybe a -> Encoding encodeMaybe = \case - Nothing -> encodeBool False <> encodeNull - Just en -> encodeBool True <> encode en - + Nothing -> encodeNull + Just en -> encode en decodeMaybe :: DecodeVersioned a => SnapshotVersion -> Decoder s (Maybe a) -decodeMaybe v@V0 = decodeBool >>= \exist -> - if exist - then Just <$> decodeVersioned v - else Nothing <$ decodeNull +decodeMaybe v@V0 = do + tok <- peekTokenType + case tok of + TypeNull -> Nothing <$ decodeNull + _ -> Just <$> decodeVersioned v + +encodeList :: Encode a => [a] -> Encoding +encodeList xs = + encodeListLen (fromIntegral (length xs)) + <> foldr (\x r -> encode x <> r) mempty xs + +decodeList :: DecodeVersioned a => SnapshotVersion -> Decoder s [a] +decodeList v = do + n <- decodeListLen + decodeSequenceLenN (flip (:)) [] reverse n (decodeVersioned v) + +encodeVector :: Encode a => V.Vector a -> Encoding +encodeVector xs = + encodeListLen (fromIntegral (V.length xs)) + <> foldr (\x r -> encode x <> r) mempty xs + +decodeVector :: DecodeVersioned a => SnapshotVersion -> Decoder s (V.Vector a) +decodeVector v = do + n <- decodeListLen + decodeSequenceLenN (flip (:)) [] (V.reverse . V.fromList) + n (decodeVersioned v) From 1e032d203b6917f2f33bfe3c9d26bbe9610e461c Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Wed, 12 Mar 2025 13:20:02 +0000 Subject: [PATCH 12/15] Add specialise pragmas to everything in MergingTree --- src/Database/LSMTree/Internal/MergingTree.hs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Database/LSMTree/Internal/MergingTree.hs b/src/Database/LSMTree/Internal/MergingTree.hs index 7ed4b21d2..3da3c84bf 100644 --- a/src/Database/LSMTree/Internal/MergingTree.hs +++ b/src/Database/LSMTree/Internal/MergingTree.hs @@ -103,12 +103,18 @@ data PreExistingRun m h = PreExistingRun !(Ref (Run m h)) | PreExistingMergingRun !(Ref (MergingRun MR.LevelMergeType m h)) +{-# SPECIALISE newCompletedMerge :: + Ref (Run IO h) + -> IO (Ref (MergingTree IO h)) #-} newCompletedMerge :: (MonadMVar m, PrimMonad m, MonadMask m) => Ref (Run m h) -> m (Ref (MergingTree m h)) newCompletedMerge run = mkMergingTree . CompletedTreeMerge =<< dupRef run +{-# SPECIALISE newOngoingMerge :: + Ref (MergingRun MR.TreeMergeType IO h) + -> IO (Ref (MergingTree IO h)) #-} -- | Create a new 'MergingTree' representing the merge of an ongoing run. -- The usage of this function is primarily to facilitate the reloading of an -- ongoing merge from a persistent snapshot. @@ -118,6 +124,10 @@ newOngoingMerge :: -> m (Ref (MergingTree m h)) newOngoingMerge mr = mkMergingTree . OngoingTreeMerge =<< dupRef mr +{-# SPECIALISE newPendingLevelMerge :: + [PreExistingRun IO h] + -> Maybe (Ref (MergingTree IO h)) + -> IO (Ref (MergingTree IO h)) #-} -- | Create a new 'MergingTree' representing the merge of a sequence of -- pre-existing runs (completed or ongoing, plus a optional final tree). -- This is for merging the entire contents of a table down to a single run @@ -178,6 +188,9 @@ newPendingLevelMerge prs mmt = do then return Nothing else Just <$!> dupRef mt +{-# SPECIALISE newPendingUnionMerge :: + [Ref (MergingTree IO h)] + -> IO (Ref (MergingTree IO h)) #-} -- | Create a new 'MergingTree' representing the union of one or more merging -- trees. This is for unioning the content of multiple tables (represented -- themselves as merging trees). @@ -207,6 +220,7 @@ newPendingUnionMerge mts = do -> return mt _ -> mkMergingTree (PendingTreeMerge (PendingUnionMerge mts'')) +{-# SPECIALISE isStructurallyEmpty :: Ref (MergingTree IO h) -> IO Bool #-} -- | Test if a 'MergingTree' is \"obviously\" empty by virtue of its structure. -- This is not the same as being empty due to a pending or ongoing merge -- happening to produce an empty run. @@ -221,6 +235,9 @@ isStructurallyEmpty (DeRef MergingTree {mergeState}) = -- It may also turn out to be useful to consider CompletedTreeMerge with -- a zero length runs as empty. +{-# SPECIALISE mkMergingTree :: + MergingTreeState IO h + -> IO (Ref (MergingTree IO h)) #-} -- | Constructor helper. -- -- This adopts the references in the MergingTreeState, so callers should @@ -239,6 +256,7 @@ mkMergingTree mergeTreeState = do , mergeRefCounter } +{-# SPECIALISE finalise :: StrictMVar IO (MergingTreeState IO h) -> IO () #-} finalise :: (MonadMVar m, PrimMonad m, MonadMask m) => StrictMVar m (MergingTreeState m h) -> m () finalise mergeState = releaseMTS =<< readMVar mergeState From 153e65b20ce5839d49cfa4ef5e96ee1e7a543a58 Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Wed, 12 Mar 2025 18:19:56 +0000 Subject: [PATCH 13/15] More consistent naming of snapshot merging and incoming run types --- src/Database/LSMTree/Internal/Snapshot.hs | 84 +++++++++---------- .../LSMTree/Internal/Snapshot/Codec.hs | 21 ++--- .../LSMTree/Internal/Snapshot/Codec.hs | 18 ++-- .../LSMTree/Internal/Snapshot/Codec/Golden.hs | 17 ++-- 4 files changed, 70 insertions(+), 70 deletions(-) diff --git a/src/Database/LSMTree/Internal/Snapshot.hs b/src/Database/LSMTree/Internal/Snapshot.hs index 0872a2176..9347fa0fc 100644 --- a/src/Database/LSMTree/Internal/Snapshot.hs +++ b/src/Database/LSMTree/Internal/Snapshot.hs @@ -7,7 +7,7 @@ module Database.LSMTree.Internal.Snapshot ( , SnapLevels (..) , SnapLevel (..) , SnapIncomingRun (..) - , SnapMergingRunState (..) + , SnapMergingRun (..) -- * MergeTree snapshot format , SnapMergingTree(..) , SnapMergingTreeState(..) @@ -170,18 +170,19 @@ instance NFData r => NFData (SnapLevel r) where -- both to zero). -- data SnapIncomingRun r = - SnapMergingRun !MergePolicyForLevel - !NominalDebt - !NominalCredits -- ^ The nominal credits supplied, and that - -- need to be supplied on snapshot open. - !(SnapMergingRunState MR.LevelMergeType r) - | SnapSingleRun !r + SnapIncomingMergingRun + !MergePolicyForLevel + !NominalDebt + !NominalCredits -- ^ The nominal credits supplied, and that + -- need to be supplied on snapshot open. + !(SnapMergingRun MR.LevelMergeType r) + | SnapIncomingSingleRun !r deriving stock (Eq, Functor, Foldable, Traversable) instance NFData r => NFData (SnapIncomingRun r) where - rnf (SnapMergingRun a b c d) = + rnf (SnapIncomingMergingRun a b c d) = rnf a `seq` rnf b `seq` rnf c `seq` rnf d - rnf (SnapSingleRun a) = rnf a + rnf (SnapIncomingSingleRun a) = rnf a -- | The total number of supplied credits. This total is used on snapshot load -- to restore merging work that was lost when the snapshot was created. @@ -189,12 +190,12 @@ newtype SuppliedCredits = SuppliedCredits { getSuppliedCredits :: Int } deriving stock (Eq, Read) deriving newtype NFData -data SnapMergingRunState t r = +data SnapMergingRun t r = SnapCompletedMerge !NumRuns !MergeDebt !r | SnapOngoingMerge !RunParams !MergeCredits !(V.Vector r) !t deriving stock (Eq, Functor, Foldable, Traversable) -instance (NFData t, NFData r) => NFData (SnapMergingRunState t r) where +instance (NFData t, NFData r) => NFData (SnapMergingRun t r) where rnf (SnapCompletedMerge a b c) = rnf a `seq` rnf b `seq` rnf c rnf (SnapOngoingMerge a b c d) = rnf a `seq` rnf b `seq` rnf c `seq` rnf d @@ -208,9 +209,8 @@ newtype SnapMergingTree r = SnapMergingTree (SnapMergingTreeState r) data SnapMergingTreeState r = SnapCompletedTreeMerge !r - | SnapPendingTreeMerge !(SnapPendingMerge r) - | SnapOngoingTreeMerge - !(SnapMergingRunState MR.TreeMergeType r) + | SnapPendingTreeMerge !(SnapPendingMerge r) + | SnapOngoingTreeMerge !(SnapMergingRun MR.TreeMergeType r) deriving stock (Eq, Functor, Foldable, Traversable) instance NFData r => NFData (SnapMergingTreeState r) where @@ -231,9 +231,8 @@ instance NFData r => NFData (SnapPendingMerge r) where rnf (SnapPendingUnionMerge a) = rnf a data SnapPreExistingRun r = - SnapPreExistingRun !r - | SnapPreExistingMergingRun - !(SnapMergingRunState MR.LevelMergeType r) + SnapPreExistingRun !r + | SnapPreExistingMergingRun !(SnapMergingRun MR.LevelMergeType r) deriving stock (Eq, Functor, Foldable, Traversable) instance NFData r => NFData (SnapPreExistingRun r) where @@ -307,7 +306,7 @@ fromSnapMergingTree hfs hbio uc resolve dir = go reg (SnapMergingTree (SnapOngoingTreeMerge smrs)) = do mr <- withRollback reg - (fromSnapMergingRunState hfs hbio uc resolve dir smrs) + (fromSnapMergingRun hfs hbio uc resolve dir smrs) releaseRef mt <- withRollback reg (MT.newOngoingMerge mr) @@ -325,7 +324,7 @@ fromSnapMergingTree hfs hbio uc resolve dir = fromSnapPreExistingRun reg (SnapPreExistingMergingRun smrs) = MT.PreExistingMergingRun <$> withRollback reg - (fromSnapMergingRunState hfs hbio uc resolve dir smrs) + (fromSnapMergingRun hfs hbio uc resolve dir smrs) releaseRef releasePER (MT.PreExistingRun r) = releaseRef r @@ -351,7 +350,7 @@ toSnapMergingTreeState :: toSnapMergingTreeState (MT.CompletedTreeMerge r) = pure $ SnapCompletedTreeMerge r toSnapMergingTreeState (MT.PendingTreeMerge p) = SnapPendingTreeMerge <$> toSnapPendingMerge p toSnapMergingTreeState (MT.OngoingTreeMerge mergingRun) = - SnapOngoingTreeMerge <$> toSnapMergingRunState mergingRun + SnapOngoingTreeMerge <$> toSnapMergingRun mergingRun {-# SPECIALISE toSnapPendingMerge :: MT.PendingMerge IO h -> IO (SnapPendingMerge (Ref (Run IO h))) #-} toSnapPendingMerge :: @@ -372,7 +371,7 @@ toSnapPreExistingRun :: -> m (SnapPreExistingRun (Ref (Run m h))) toSnapPreExistingRun (MT.PreExistingRun run) = pure $ SnapPreExistingRun run toSnapPreExistingRun (MT.PreExistingMergingRun peMergingRun) = - SnapPreExistingMergingRun <$> toSnapMergingRunState peMergingRun + SnapPreExistingMergingRun <$> toSnapMergingRun peMergingRun {------------------------------------------------------------------------------- Conversion to levels snapshot format @@ -408,24 +407,24 @@ toSnapIncomingRun :: toSnapIncomingRun ir = do s <- snapshotIncomingRun ir case s of - Left r -> pure $! SnapSingleRun r + Left r -> pure $! SnapIncomingSingleRun r Right (mergePolicy, nominalDebt, nominalCredits, mergingRun) -> do -- We need to know how many credits were supplied so we can restore merge -- work on snapshot load. - smrs <- toSnapMergingRunState mergingRun - pure $! SnapMergingRun mergePolicy nominalDebt nominalCredits smrs + smrs <- toSnapMergingRun mergingRun + pure $! SnapIncomingMergingRun mergePolicy nominalDebt nominalCredits smrs -{-# SPECIALISE toSnapMergingRunState :: +{-# SPECIALISE toSnapMergingRun :: Ref (MR.MergingRun t IO h) - -> IO (SnapMergingRunState t (Ref (Run IO h))) #-} -toSnapMergingRunState :: + -> IO (SnapMergingRun t (Ref (Run IO h))) #-} +toSnapMergingRun :: (PrimMonad m, MonadMVar m) => Ref (MR.MergingRun t m h) - -> m (SnapMergingRunState t (Ref (Run m h))) -toSnapMergingRunState !mr = do + -> m (SnapMergingRun t (Ref (Run m h))) +toSnapMergingRun !mr = do -- TODO: MR.snapshot needs to return duplicated run references, and we -- need to arrange to release them when the snapshotting is done. (numRuns, mergeDebt, mergeCredits, state) <- MR.snapshot mr @@ -680,47 +679,46 @@ fromSnapLevels hfs hbio uc conf resolve reg dir (SnapLevels levels) = LevelNo -> SnapIncomingRun (Ref (Run m h)) -> m (IncomingRun m h) - fromSnapIncomingRun _ln (SnapSingleRun run) = + fromSnapIncomingRun _ln (SnapIncomingSingleRun run) = newIncomingSingleRun run - fromSnapIncomingRun ln (SnapMergingRun mergePolicy nominalDebt - nominalCredits smrs) = + fromSnapIncomingRun ln (SnapIncomingMergingRun mergePolicy nominalDebt + nominalCredits smrs) = bracket - (fromSnapMergingRunState hfs hbio uc resolve dir smrs) + (fromSnapMergingRun hfs hbio uc resolve dir smrs) releaseRef $ \mr -> do ir <- newIncomingMergingRun mergePolicy nominalDebt mr -- This will set the correct nominal credits, but it will not do any - -- more merging work because fromSnapMergingRunState already supplies + -- more merging work because fromSnapMergingRun already supplies -- all the merging credits already. supplyCreditsIncomingRun conf ln ir nominalCredits return ir -{-# SPECIALISE fromSnapMergingRunState :: +{-# SPECIALISE fromSnapMergingRun :: MR.IsMergeType t => HasFS IO h -> HasBlockIO IO h -> UniqCounter IO -> ResolveSerialisedValue -> ActiveDir - -> SnapMergingRunState t (Ref (Run IO h)) + -> SnapMergingRun t (Ref (Run IO h)) -> IO (Ref (MR.MergingRun t IO h)) #-} -fromSnapMergingRunState :: +fromSnapMergingRun :: (MonadMask m, MonadMVar m, MonadSTM m, MonadST m, MR.IsMergeType t) => HasFS m h -> HasBlockIO m h -> UniqCounter m -> ResolveSerialisedValue -> ActiveDir - -> SnapMergingRunState t (Ref (Run m h)) + -> SnapMergingRun t (Ref (Run m h)) -> m (Ref (MR.MergingRun t m h)) -fromSnapMergingRunState _hfs _hbio _uc _resolve _dir - (SnapCompletedMerge numRuns mergeDebt r) = +fromSnapMergingRun _hfs _hbio _uc _resolve _dir + (SnapCompletedMerge numRuns mergeDebt r) = MR.newCompleted numRuns mergeDebt r -fromSnapMergingRunState hfs hbio uc resolve dir - (SnapOngoingMerge runParams mergeCredits - rs mergeType) = do +fromSnapMergingRun hfs hbio uc resolve dir + (SnapOngoingMerge runParams mergeCredits rs mergeType) = do bracketOnError (do uniq <- incrUniqCounter uc let runPaths = runPath dir (uniqueToRunNumber uniq) diff --git a/src/Database/LSMTree/Internal/Snapshot/Codec.hs b/src/Database/LSMTree/Internal/Snapshot/Codec.hs index 5ca094328..814d2ad41 100644 --- a/src/Database/LSMTree/Internal/Snapshot/Codec.hs +++ b/src/Database/LSMTree/Internal/Snapshot/Codec.hs @@ -560,14 +560,14 @@ instance DecodeVersioned RunNumber where -- SnapIncomingRun instance Encode r => Encode (SnapIncomingRun r) where - encode (SnapMergingRun mpfl nd nc smrs) = + encode (SnapIncomingMergingRun mpfl nd nc smrs) = encodeListLen 5 <> encodeWord 0 <> encode mpfl <> encode nd <> encode nc <> encode smrs - encode (SnapSingleRun x) = + encode (SnapIncomingSingleRun x) = encodeListLen 2 <> encodeWord 1 <> encode x @@ -577,10 +577,11 @@ instance DecodeVersioned r => DecodeVersioned (SnapIncomingRun r) where n <- decodeListLen tag <- decodeWord case (n, tag) of - (5, 0) -> SnapMergingRun <$> decodeVersioned v <*> decodeVersioned v - <*> decodeVersioned v <*> decodeVersioned v - (2, 1) -> SnapSingleRun <$> decodeVersioned v - _ -> fail ("[SnapMergingRun] Unexpected combination of list length and tag: " <> show (n, tag)) + (5, 0) -> SnapIncomingMergingRun + <$> decodeVersioned v <*> decodeVersioned v + <*> decodeVersioned v <*> decodeVersioned v + (2, 1) -> SnapIncomingSingleRun <$> decodeVersioned v + _ -> fail ("[SnapIncomingRun] Unexpected combination of list length and tag: " <> show (n, tag)) -- NumRuns @@ -604,9 +605,9 @@ instance DecodeVersioned MergePolicyForLevel where 1 -> pure LevelLevelling _ -> fail ("[MergePolicyForLevel] Unexpected tag: " <> show tag) --- SnapMergingRunState +-- SnapMergingRun -instance (Encode t, Encode r) => Encode (SnapMergingRunState t r) where +instance (Encode t, Encode r) => Encode (SnapMergingRun t r) where encode (SnapCompletedMerge nr md r) = encodeListLen 4 <> encodeWord 0 @@ -621,7 +622,7 @@ instance (Encode t, Encode r) => Encode (SnapMergingRunState t r) where <> encode rs <> encode mt -instance (DecodeVersioned t, DecodeVersioned r) => DecodeVersioned (SnapMergingRunState t r) where +instance (DecodeVersioned t, DecodeVersioned r) => DecodeVersioned (SnapMergingRun t r) where decodeVersioned v@V0 = do n <- decodeListLen tag <- decodeWord @@ -631,7 +632,7 @@ instance (DecodeVersioned t, DecodeVersioned r) => DecodeVersioned (SnapMergingR <*> decodeVersioned v (5, 1) -> SnapOngoingMerge <$> decodeVersioned v <*> decodeVersioned v <*> decodeVersioned v <*> decodeVersioned v - _ -> fail ("[SnapMergingRunState] Unexpected combination of list length and tag: " <> show (n, tag)) + _ -> fail ("[SnapMergingRun] Unexpected combination of list length and tag: " <> show (n, tag)) -- NominalDebt, NominalCredits, MergeDebt and MergeCredits diff --git a/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs b/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs index 97a77a67f..c36498b4a 100644 --- a/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs +++ b/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs @@ -177,7 +177,7 @@ testAll test = [ , test (Proxy @RunBloomFilterAlloc) , test (Proxy @IndexType) , test (Proxy @RunParams) - , test (Proxy @(SnapMergingRunState LevelMergeType SnapshotRun)) + , test (Proxy @(SnapMergingRun LevelMergeType SnapshotRun)) , test (Proxy @MergeDebt) , test (Proxy @MergeCredits) , test (Proxy @NominalDebt) @@ -298,14 +298,14 @@ deriving newtype instance Arbitrary RunNumber instance Arbitrary r => Arbitrary (SnapIncomingRun r) where arbitrary = oneof [ - SnapMergingRun <$> arbitrary <*> arbitrary - <*> arbitrary <*> arbitrary - , SnapSingleRun <$> arbitrary + SnapIncomingMergingRun <$> arbitrary <*> arbitrary + <*> arbitrary <*> arbitrary + , SnapIncomingSingleRun <$> arbitrary ] - shrink (SnapMergingRun a b c d) = - [ SnapMergingRun a' b' c' d' + shrink (SnapIncomingMergingRun a b c d) = + [ SnapIncomingMergingRun a' b' c' d' | (a', b', c', d') <- shrink (a, b, c, d) ] - shrink (SnapSingleRun a) = SnapSingleRun <$> shrink a + shrink (SnapIncomingSingleRun a) = SnapIncomingSingleRun <$> shrink a deriving newtype instance Arbitrary NumRuns @@ -313,7 +313,7 @@ instance Arbitrary MergePolicyForLevel where arbitrary = elements [LevelTiering, LevelLevelling] shrink _ = [] -instance (Arbitrary t, Arbitrary r) => Arbitrary (SnapMergingRunState t r) where +instance (Arbitrary t, Arbitrary r) => Arbitrary (SnapMergingRun t r) where arbitrary = oneof [ SnapCompletedMerge <$> arbitrary <*> arbitrary <*> arbitrary , SnapOngoingMerge <$> arbitrary <*> arbitrary @@ -370,7 +370,7 @@ deriving stock instance Show SnapshotRun deriving stock instance Show r => Show (SnapLevels r) deriving stock instance Show r => Show (SnapLevel r) deriving stock instance Show r => Show (SnapIncomingRun r) -deriving stock instance (Show t, Show r) => Show (SnapMergingRunState t r) +deriving stock instance (Show t, Show r) => Show (SnapMergingRun t r) deriving stock instance Show r => Show (SnapMergingTree r) deriving stock instance Show r => Show (SnapMergingTreeState r) diff --git a/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs b/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs index dcb0aca75..c73d5cd3e 100644 --- a/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs +++ b/test/Test/Database/LSMTree/Internal/Snapshot/Codec/Golden.hs @@ -236,21 +236,22 @@ enumerateSnapIncomingRun = let inSnaps = [ (fuseAnnotations ["R1", a, b], - SnapMergingRun policy nominalDebt nominalCredits sState) + SnapIncomingMergingRun policy nominalDebt nominalCredits sState) | (a, policy ) <- [("P0", LevelTiering), ("P1", LevelLevelling)] , nominalDebt <- NominalDebt <$> [ magicNumber2 ] , nominalCredits <- NominalCredits <$> [ magicNumber1 ] - , (b, sState ) <- enumerateSnapMergingRunState enumerateLevelMergeType + , (b, sState ) <- enumerateSnapMergingRun enumerateLevelMergeType ] in fold - [ [(fuseAnnotations $ "R0" : replicate 4 blank, SnapSingleRun enumerateOpenRunInfo)] + [ [(fuseAnnotations $ "R0" : replicate 4 blank, + SnapIncomingSingleRun enumerateOpenRunInfo)] , inSnaps ] -enumerateSnapMergingRunState :: +enumerateSnapMergingRun :: [(ComponentAnnotation, t)] - -> [(ComponentAnnotation, SnapMergingRunState t SnapshotRun)] -enumerateSnapMergingRunState mTypes = + -> [(ComponentAnnotation, SnapMergingRun t SnapshotRun)] +enumerateSnapMergingRun mTypes = [ (fuseAnnotations ["C0", blank, blank], SnapCompletedMerge numRuns mergeDebt enumerateOpenRunInfo) | numRuns <- NumRuns <$> [ magicNumber1 ] @@ -302,7 +303,7 @@ enumerateSnapMergingTreeState expandable = enumerateSnapOngoingTreeMerge :: [(ComponentAnnotation, SnapMergingTreeState SnapshotRun)] enumerateSnapOngoingTreeMerge = do - (tagX, valX) <- enumerateSnapMergingRunState enumerateTreeMergeType + (tagX, valX) <- enumerateSnapMergingRun enumerateTreeMergeType let value = SnapOngoingTreeMerge valX pure ( fuseAnnotations $ ["G0", blank, tagX] <> replicate 5 blank, value ) @@ -328,7 +329,7 @@ enumerateSnapPreExistingRun :: [(ComponentAnnotation, SnapPreExistingRun Snapsho enumerateSnapPreExistingRun = ( fuseAnnotations ("E0" : replicate 3 blank), SnapPreExistingRun enumerateOpenRunInfo) : [ (fuseAnnotations ["E1", tagX], SnapPreExistingMergingRun valX) - | (tagX, valX) <- enumerateSnapMergingRunState enumerateLevelMergeType + | (tagX, valX) <- enumerateSnapMergingRun enumerateLevelMergeType ] enumerateTreeMergeType :: [(ComponentAnnotation, MR.TreeMergeType)] From 0204324d8d4dcb7af7fb517f8f9ca34fc43c0612 Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Wed, 12 Mar 2025 18:59:34 +0000 Subject: [PATCH 14/15] Split IncomingRun out of MergeSchedule Also update the credit docs for nominal credits. --- lsm-tree.cabal | 1 + src/Database/LSMTree/Internal/IncomingRun.hs | 378 ++++++++++++++++++ .../LSMTree/Internal/MergeSchedule.hs | 293 +------------- 3 files changed, 391 insertions(+), 281 deletions(-) create mode 100644 src/Database/LSMTree/Internal/IncomingRun.hs diff --git a/lsm-tree.cabal b/lsm-tree.cabal index f0c0c7cd0..b9b41c841 100644 --- a/lsm-tree.cabal +++ b/lsm-tree.cabal @@ -131,6 +131,7 @@ library Database.LSMTree.Internal.CRC32C Database.LSMTree.Internal.Cursor Database.LSMTree.Internal.Entry + Database.LSMTree.Internal.IncomingRun Database.LSMTree.Internal.Index Database.LSMTree.Internal.Index.Compact Database.LSMTree.Internal.Index.CompactAcc diff --git a/src/Database/LSMTree/Internal/IncomingRun.hs b/src/Database/LSMTree/Internal/IncomingRun.hs new file mode 100644 index 000000000..a21357592 --- /dev/null +++ b/src/Database/LSMTree/Internal/IncomingRun.hs @@ -0,0 +1,378 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE UnboxedTuples #-} + +#if !(MIN_VERSION_GLASGOW_HASKELL(9,0,0,0)) +-- Fix for ghc 8.10.x with deriving newtype Prim +{-# LANGUAGE DataKinds #-} +#endif + +module Database.LSMTree.Internal.IncomingRun ( + IncomingRun (..) + , MergePolicyForLevel (..) + , duplicateIncomingRun + , releaseIncomingRun + , newIncomingSingleRun + , newIncomingMergingRun + , snapshotIncomingRun + + -- * Credits and credit tracking + -- $credittracking + , NominalDebt (..) + , NominalCredits (..) + , nominalDebtAsCredits + , supplyCreditsIncomingRun + , immediatelyCompleteIncomingRun + ) where + +import Control.ActionRegistry +import Control.Concurrent.Class.MonadMVar.Strict +import Control.DeepSeq (NFData (..)) +import Control.Monad.Class.MonadST (MonadST) +import Control.Monad.Class.MonadSTM (MonadSTM (..)) +import Control.Monad.Class.MonadThrow (MonadMask, MonadThrow (..)) +import Control.Monad.Primitive +import Control.RefCount +import Data.Primitive (Prim) +import Data.Primitive.PrimVar +import Database.LSMTree.Internal.Assertions (assert) +import Database.LSMTree.Internal.Config +import Database.LSMTree.Internal.Entry (NumEntries (..)) +import Database.LSMTree.Internal.MergingRun (MergeCredits (..), + MergeDebt (..), MergingRun) +import qualified Database.LSMTree.Internal.MergingRun as MR +import Database.LSMTree.Internal.Run (Run) + +import GHC.Exts (Word (W#), quotRemWord2#, timesWord2#) + +{------------------------------------------------------------------------------- + Incoming runs +-------------------------------------------------------------------------------} + +-- | An incoming run is either a single run, or a merge. +data IncomingRun m h = + Single !(Ref (Run m h)) + | Merging !MergePolicyForLevel + !NominalDebt + !(PrimVar (PrimState m) NominalCredits) + !(Ref (MergingRun MR.LevelMergeType m h)) + +data MergePolicyForLevel = LevelTiering | LevelLevelling + deriving stock (Show, Eq) + +instance NFData MergePolicyForLevel where + rnf LevelTiering = () + rnf LevelLevelling = () + +{-# SPECIALISE duplicateIncomingRun :: ActionRegistry IO -> IncomingRun IO h -> IO (IncomingRun IO h) #-} +duplicateIncomingRun :: + (PrimMonad m, MonadMask m) + => ActionRegistry m + -> IncomingRun m h + -> m (IncomingRun m h) +duplicateIncomingRun reg (Single r) = + Single <$> withRollback reg (dupRef r) releaseRef + +duplicateIncomingRun reg (Merging mp md mcv mr) = + Merging mp md <$> (newPrimVar =<< readPrimVar mcv) + <*> withRollback reg (dupRef mr) releaseRef + +{-# SPECIALISE releaseIncomingRun :: IncomingRun IO h -> IO () #-} +releaseIncomingRun :: + (PrimMonad m, MonadMask m) + => IncomingRun m h -> m () +releaseIncomingRun (Single r) = releaseRef r +releaseIncomingRun (Merging _ _ _ mr) = releaseRef mr + +{-# INLINE newIncomingSingleRun #-} +newIncomingSingleRun :: + (PrimMonad m, MonadThrow m) + => Ref (Run m h) + -> m (IncomingRun m h) +newIncomingSingleRun r = Single <$> dupRef r + +{-# INLINE newIncomingMergingRun #-} +newIncomingMergingRun :: + (PrimMonad m, MonadThrow m) + => MergePolicyForLevel + -> NominalDebt + -> Ref (MergingRun MR.LevelMergeType m h) + -> m (IncomingRun m h) +newIncomingMergingRun mergePolicy nominalDebt mr = do + nominalCreditsVar <- newPrimVar (NominalCredits 0) + Merging mergePolicy nominalDebt nominalCreditsVar <$> dupRef mr + +{-# SPECIALISE snapshotIncomingRun :: + IncomingRun IO h + -> IO (Either (Ref (Run IO h)) + (MergePolicyForLevel, + NominalDebt, + NominalCredits, + Ref (MergingRun MR.LevelMergeType IO h))) #-} +snapshotIncomingRun :: + PrimMonad m + => IncomingRun m h + -> m (Either (Ref (Run m h)) + (MergePolicyForLevel, + NominalDebt, + NominalCredits, + Ref (MergingRun MR.LevelMergeType m h))) +snapshotIncomingRun (Single r) = pure (Left r) +snapshotIncomingRun (Merging mergePolicy nominalDebt nominalCreditsVar mr) = do + nominalCredits <- readPrimVar nominalCreditsVar + pure (Right (mergePolicy, nominalDebt, nominalCredits, mr)) + +{------------------------------------------------------------------------------- + Credits +-------------------------------------------------------------------------------} + +{- $credittracking + +With scheduled merges, each update (e.g., insert) on a table contributes to the +progression of ongoing merges in the levels structure. This ensures that merges +are finished in time before a new merge has to be started. The points in the +evolution of the levels structure where new merges are started are known: a +flush of a full write buffer will create a new run on the first level, and +after sufficient flushes (e.g., 4) we will start at least one new merge on the +second level. This may cascade down to lower levels depending on how full the +levels are. As such, we have a well-defined measure to determine when merges +should be finished: it only depends on the maximum size of the write buffer! + +The simplest solution to making sure merges are done in time is to step them to +completion immediately when started. This does not, however, spread out work +over time nicely. Instead, we schedule merge work based on how many updates are +made on the table, taking care to ensure that the merge is finished /just/ in +time before the next flush comes around, and not too early. + +The progression is tracked using nominal credits. Each individual update +contributes a single credit to each level, since each level contains precisely +one ongoing merge. Contributing a credit does not, however, translate directly +to performing one /unit/ of merging work: + +* The amount of work to do for one credit is adjusted depending on the actual + size of the merge we are doing. Last-level merges, for example, can have + larger inputs, and therefore we have to do a little more work for each + credit. Or input runs involved in a merge can be less than maximal size for + the level, and so there may be less merging work to do. As such, we /scale/ + 'NominalCredits' to 'MergeCredits', and then supply the 'MergeCredits' to + the 'MergingRun'. + +* Supplying 'MergeCredits' to a 'MergingRun' does not necessarily directly + translate into performing merging work. Merge credits are accumulated until + they go over a threshold, after which a batch of merge work will be performed. + Configuring this threshold should allow a good balance between spreading out + I\/O and achieving good (concurrent) performance. + +Merging runs can be shared across tables, which means that multiple threads +can contribute to the same merge concurrently. Incoming runs however are /not/ +shared between tables. As such the tracking of 'NominalCredits' does not need +to use any concurrency precautions. +-} + +-- | Total merge debt to complete the merge in an incoming run. +-- +-- This corresponds to the number (worst case, minimum number) of update +-- operations inserted into the table, before we will expect the merge to +-- complete. +newtype NominalDebt = NominalDebt Int + deriving stock Eq + deriving newtype (NFData) + +-- | Merge credits that get supplied to a table's levels. +-- +-- This corresponds to the number of update operations inserted into the table. +newtype NominalCredits = NominalCredits Int + deriving stock Eq + deriving newtype (Prim, NFData) + +nominalDebtAsCredits :: NominalDebt -> NominalCredits +nominalDebtAsCredits (NominalDebt c) = NominalCredits c + +{-# SPECIALISE supplyCreditsIncomingRun :: + TableConfig + -> LevelNo + -> IncomingRun IO h + -> NominalCredits + -> IO () #-} +-- | Supply a given number of nominal credits to the merge in an incoming run. +-- This is a relative addition of credits, not a new absolute total value. +supplyCreditsIncomingRun :: + (MonadSTM m, MonadST m, MonadMVar m, MonadMask m) + => TableConfig + -> LevelNo + -> IncomingRun m h + -> NominalCredits + -> m () +supplyCreditsIncomingRun _ _ (Single _r) _ = return () +supplyCreditsIncomingRun conf ln (Merging _ nominalDebt nominalCreditsVar mr) + deposit = do + (_nominalCredits, + nominalCredits') <- depositNominalCredits nominalDebt nominalCreditsVar + deposit + let !mergeDebt = MR.totalMergeDebt mr + !mergeCredits' = scaleNominalToMergeCredit nominalDebt mergeDebt + nominalCredits' + !thresh = creditThresholdForLevel conf ln + (_suppliedCredits, + _suppliedCredits') <- MR.supplyCreditsAbsolute mr thresh mergeCredits' + return () + --TODO: currently each supplying credits action results in contributing + -- credits to the underlying merge, but this need not be the case. We + -- _could_ do threshold based batching at the level of the IncomingRun. + -- The IncomingRun does not need to worry about concurrency, so does not + -- pay the cost of atomic operations on the counters. Then when we + -- accumulate a batch we could supply that to the MergingRun (which must + -- use atomic operations for its counters). We could potentially simplify + -- MergingRun by dispensing with batching for the MergeCredits counters. + +-- TODO: the thresholds for doing merge work should be different for each level, +-- maybe co-prime? +creditThresholdForLevel :: TableConfig -> LevelNo -> MR.CreditThreshold +creditThresholdForLevel conf (LevelNo _i) = + let AllocNumEntries (NumEntries x) = confWriteBufferAlloc conf + in MR.CreditThreshold (MR.UnspentCredits (MergeCredits x)) + +-- | Deposit nominal credits in the local credits var, ensuring the total +-- credits does not exceed the total debt. +-- +-- Depositing /could/ leave the credit higher than the total debt. It is not +-- avoided by construction. The scenario is this: when a completed merge is +-- underfull, we combine it with the incoming run, so it means we have one run +-- fewer on the level then we'd normally have. This means that the level +-- becomes full at a later time, so more time passes before we call +-- 'MR.expectCompleted' on any levels further down the tree. This means we keep +-- supplying nominal credits to levels further down past the point their +-- nominal debt is paid off. So the solution here is just to drop any nominal +-- credits that are in excess of the nominal debt. +-- +-- This is /not/ itself thread safe. All 'TableContent' update operations are +-- expected to be serialised by the caller. See concurrency comments for +-- 'TableContent' for detail. +depositNominalCredits :: + PrimMonad m + => NominalDebt + -> PrimVar (PrimState m) NominalCredits + -> NominalCredits + -> m (NominalCredits, NominalCredits) +depositNominalCredits (NominalDebt nominalDebt) + nominalCreditsVar + (NominalCredits deposit) = do + NominalCredits before <- readPrimVar nominalCreditsVar + let !after = NominalCredits (min (before + deposit) nominalDebt) + writePrimVar nominalCreditsVar after + return (NominalCredits before, after) + +-- | Linearly scale a nominal credit (between 0 and the nominal debt) into an +-- equivalent merge credit (between 0 and the total merge debt). +-- +-- Crucially, @100% nominal credit ~~ 100% merge credit@, so when we pay off +-- the nominal debt, we also exactly pay off the merge debt. That is: +-- +-- > scaleNominalToMergeCredit nominalDebt mergeDebt nominalDebt == mergeDebt +-- +-- (modulo some newtype conversions) +-- +scaleNominalToMergeCredit :: + NominalDebt + -> MergeDebt + -> NominalCredits + -> MergeCredits +scaleNominalToMergeCredit (NominalDebt nominalDebt) + (MergeDebt (MergeCredits mergeDebt)) + (NominalCredits nominalCredits) = + -- The scaling involves an operation: (a * b) `div` c + -- but where potentially the variables a,b,c may be bigger than a 32bit + -- integer can hold. This would be the case for runs that have more than + -- 4 billion entries. + -- + -- (This is assuming 64bit Int, the problem would be even worse for 32bit + -- systems. The solution here would also work for 32bit systems, allowing + -- up to, 2^31, 2 billion entries per run.) + -- + -- To work correctly in this case we need higher range for the intermediate + -- result a*b which could be bigger than 64bits can hold. A correct + -- implementation can use Rational, but a fast implementation should use + -- only integer operations. This is relevant because this is on the fast + -- path for small insertions into the table that often do no merging work + -- and just update credit counters. + + -- The fast implementation uses integer operations that produce a 128bit + -- intermediate result for the a*b result, and use a 128bit numerator in + -- the division operation (but 64bit denominator). These are known as + -- "widening multiplication" and "narrowing division". GHC has direct + -- support for these operations as primops: timesWord2# and quotRemWord2#, + -- but they are not exposed through any high level API shipped with GHC. + + -- The specification using Rational is: + let mergeCredits_spec = floor $ toRational nominalCredits + * toRational mergeDebt + / toRational nominalDebt + -- Note that it doesn't matter if we use floor or ceiling here. + -- Rounding errors will not compound because we sum nominal debt and + -- convert absolute nominal to absolute merging credit. We don't + -- convert each deposit and sum all the rounding errors. + -- When nominalCredits == nominalDebt then the result is exact anyway + -- (being mergeDebt) so the rounding mode makes no difference when we + -- get to the end of the merge. Using floor makes things simpler for + -- the fast integer implementation below, so we take that as the spec. + + -- If the nominalCredits is between 0 and nominalDebt then it's + -- guaranteed that the mergeCredit is between 0 and mergeDebt. + -- The mergeDebt fits in an Int, therefore the result does too. + -- Therefore the undefined behaviour case of timesDivABC_fast is + -- avoided and the w2i cannot overflow. + mergeCredits_fast = w2i $ timesDivABC_fast (i2w nominalCredits) + (i2w mergeDebt) + (i2w nominalDebt) + in assert (0 < nominalDebt) $ + assert (0 <= nominalCredits && nominalCredits <= nominalDebt) $ + assert (mergeCredits_spec == mergeCredits_fast) $ + MergeCredits mergeCredits_fast + where + {-# INLINE i2w #-} + {-# INLINE w2i #-} + i2w :: Int -> Word + w2i :: Word -> Int + i2w = fromIntegral + w2i = fromIntegral + +-- | Compute @(a * b) `div` c@ for unsigned integers for the full range of +-- 64bit unsigned integers, provided that @a <= c@ and thus the result will +-- fit in 64bits. +-- +-- The @a * b@ intermediate result is computed using 128bit precision. +-- +-- Note: the behaviour is undefined if the result will not fit in 64bits. +-- It will probably result in immediate termination with SIGFPE. +-- +timesDivABC_fast :: Word -> Word -> Word -> Word +timesDivABC_fast (W# a) (W# b) (W# c) = + case timesWord2# a b of + (# ph, pl #) -> + case quotRemWord2# ph pl c of + (# q, _r #) -> W# q + +{-# SPECIALISE immediatelyCompleteIncomingRun :: + TableConfig + -> LevelNo + -> IncomingRun IO h + -> IO (Ref (Run IO h)) #-} +-- | Supply enough credits to complete the merge now. +immediatelyCompleteIncomingRun :: + (MonadSTM m, MonadST m, MonadMVar m, MonadMask m) + => TableConfig + -> LevelNo + -> IncomingRun m h + -> m (Ref (Run m h)) +immediatelyCompleteIncomingRun conf ln ir = + case ir of + Single r -> dupRef r + Merging _ (NominalDebt nominalDebt) nominalCreditsVar mr -> do + + NominalCredits nominalCredits <- readPrimVar nominalCreditsVar + let !deposit = NominalCredits (nominalDebt - nominalCredits) + supplyCreditsIncomingRun conf ln ir deposit + + -- This ensures the merge is really completed. However, we don't + -- release the merge yet, but we do return a new reference to the run. + MR.expectCompleted mr diff --git a/src/Database/LSMTree/Internal/MergeSchedule.hs b/src/Database/LSMTree/Internal/MergeSchedule.hs index ab7a96959..b69f03cd2 100644 --- a/src/Database/LSMTree/Internal/MergeSchedule.hs +++ b/src/Database/LSMTree/Internal/MergeSchedule.hs @@ -53,7 +53,6 @@ module Database.LSMTree.Internal.MergeSchedule ( import Control.ActionRegistry import Control.Concurrent.Class.MonadMVar.Strict -import Control.DeepSeq (NFData (..)) import Control.Monad.Class.MonadST (MonadST) import Control.Monad.Class.MonadSTM (MonadSTM (..)) import Control.Monad.Class.MonadThrow (MonadMask, MonadThrow (..)) @@ -62,13 +61,12 @@ import Control.RefCount import Control.Tracer import Data.BloomFilter (Bloom) import Data.Foldable (fold) -import Data.Primitive (Prim) -import Data.Primitive.PrimVar import qualified Data.Vector as V import Database.LSMTree.Internal.Assertions (assert) import Database.LSMTree.Internal.Config import Database.LSMTree.Internal.Entry (Entry, NumEntries (..), unNumEntries) +import Database.LSMTree.Internal.IncomingRun import Database.LSMTree.Internal.Index (Index) import Database.LSMTree.Internal.Lookup (ResolveSerialisedValue) import Database.LSMTree.Internal.MergingRun (MergeCredits (..), @@ -93,8 +91,6 @@ import qualified System.FS.API as FS import System.FS.API (HasFS) import System.FS.BlockIO.API (HasBlockIO) -import GHC.Exts (Word (W#), quotRemWord2#, timesWord2#) - {------------------------------------------------------------------------------- Traces -------------------------------------------------------------------------------} @@ -353,281 +349,6 @@ releaseLevels reg levels = iforLevelM_ :: Monad m => Levels m h -> (LevelNo -> Level m h -> m ()) -> m () iforLevelM_ lvls k = V.iforM_ lvls $ \i lvl -> k (LevelNo (i + 1)) lvl -{------------------------------------------------------------------------------- - Incoming runs --------------------------------------------------------------------------------} - --- | An incoming run is either a single run, or a merge. -data IncomingRun m h = - Single !(Ref (Run m h)) - | Merging !MergePolicyForLevel - !NominalDebt - !(PrimVar (PrimState m) NominalCredits) - !(Ref (MergingRun MR.LevelMergeType m h)) - -data MergePolicyForLevel = LevelTiering | LevelLevelling - deriving stock (Show, Eq) - -instance NFData MergePolicyForLevel where - rnf LevelTiering = () - rnf LevelLevelling = () - --- | Total merge debt to complete the merge in an incoming run. --- --- This corresponds to the number (worst case, minimum number) of update --- operatons inserted into the table, before we will expect the merge to --- complete. -newtype NominalDebt = NominalDebt Int - deriving stock Eq - deriving newtype (NFData) - --- | Merge credits that get supplied to a table's levels. --- --- This corresponds to the number of update operatons inserted into the table. -newtype NominalCredits = NominalCredits Int - deriving stock Eq - deriving newtype (Prim, NFData) - -nominalDebtAsCredits :: NominalDebt -> NominalCredits -nominalDebtAsCredits (NominalDebt c) = NominalCredits c - -{-# SPECIALISE duplicateIncomingRun :: ActionRegistry IO -> IncomingRun IO h -> IO (IncomingRun IO h) #-} -duplicateIncomingRun :: - (PrimMonad m, MonadMask m) - => ActionRegistry m - -> IncomingRun m h - -> m (IncomingRun m h) -duplicateIncomingRun reg (Single r) = - Single <$> withRollback reg (dupRef r) releaseRef - -duplicateIncomingRun reg (Merging mp md mcv mr) = - Merging mp md <$> (newPrimVar =<< readPrimVar mcv) - <*> withRollback reg (dupRef mr) releaseRef - -{-# SPECIALISE releaseIncomingRun :: IncomingRun IO h -> IO () #-} -releaseIncomingRun :: - (PrimMonad m, MonadMask m) - => IncomingRun m h -> m () -releaseIncomingRun (Single r) = releaseRef r -releaseIncomingRun (Merging _ _ _ mr) = releaseRef mr - -{-# INLINE newIncomingSingleRun #-} -newIncomingSingleRun :: - (PrimMonad m, MonadThrow m) - => Ref (Run m h) - -> m (IncomingRun m h) -newIncomingSingleRun r = Single <$> dupRef r - -{-# INLINE newIncomingMergingRun #-} -newIncomingMergingRun :: - (PrimMonad m, MonadThrow m) - => MergePolicyForLevel - -> NominalDebt - -> Ref (MergingRun MR.LevelMergeType m h) - -> m (IncomingRun m h) -newIncomingMergingRun mergePolicy nominalDebt mr = do - nominalCreditsVar <- newPrimVar (NominalCredits 0) - Merging mergePolicy nominalDebt nominalCreditsVar <$> dupRef mr - -{-# SPECIALISE supplyCreditsIncomingRun :: - TableConfig - -> LevelNo - -> IncomingRun IO h - -> NominalCredits - -> IO () #-} --- | Supply a given number of nominal credits to the merge in an incoming run. --- This is a relative addition of credits, not a new absolute total value. -supplyCreditsIncomingRun :: - (MonadSTM m, MonadST m, MonadMVar m, MonadMask m) - => TableConfig - -> LevelNo - -> IncomingRun m h - -> NominalCredits - -> m () -supplyCreditsIncomingRun _ _ (Single _r) _ = return () -supplyCreditsIncomingRun conf ln (Merging _ nominalDebt nominalCreditsVar mr) - deposit = do - (_nominalCredits, - nominalCredits') <- depositNominalCredits nominalDebt nominalCreditsVar - deposit - let !mergeDebt = MR.totalMergeDebt mr - !mergeCredits' = scaleNominalToMergeCredit nominalDebt mergeDebt - nominalCredits' - !thresh = creditThresholdForLevel conf ln - (_suppliedCredits, - _suppliedCredits') <- MR.supplyCreditsAbsolute mr thresh mergeCredits' - --TODO: consider tracing supply of credits - return () - --- | Deposit nominal credits in the local credits var, ensuring the total --- credits does not exceed the total debt. --- --- Depositing /could/ leave the credit higher than the total debt. It is not --- avoided by construction. The scenario is this: when a completed merge is --- underfull, we combine it with the incoming run, so it means we have one run --- fewer on the level then we'd normally have. This means that the level --- becomes full at a later time, so more time passes before we call --- 'MR.expectCompleted' on any levels further down the tree. This means we keep --- supplying nominal credits to levels further down past the point their --- nominal debt is paid off. So the solution here is just to drop any nominal --- credits that are in excess of the nominal debt. --- --- This is /not/ itself thread safe. All 'TableContent' update operations are --- expected to be serialised by the caller. See concurrency comments for --- 'TableContent' for detail. -depositNominalCredits :: - PrimMonad m - => NominalDebt - -> PrimVar (PrimState m) NominalCredits - -> NominalCredits - -> m (NominalCredits, NominalCredits) -depositNominalCredits (NominalDebt nominalDebt) - nominalCreditsVar - (NominalCredits deposit) = do - NominalCredits before <- readPrimVar nominalCreditsVar - let !after = NominalCredits (min (before + deposit) nominalDebt) - writePrimVar nominalCreditsVar after - return (NominalCredits before, after) - --- | Linearly scale a nominal credit (between 0 and the nominal debt) into an --- equivalent merge credit (between 0 and the total merge debt). --- --- Crucially, @100% nominal credit ~~ 100% merge credit@, so when we pay off --- the nominal debt, we also exactly pay off the merge debt. That is: --- --- > scaleNominalToMergeCredit nominalDebt mergeDebt nominalDebt == mergeDebt --- --- (modulo some newtype conversions) --- -scaleNominalToMergeCredit :: - NominalDebt - -> MergeDebt - -> NominalCredits - -> MergeCredits -scaleNominalToMergeCredit (NominalDebt nominalDebt) - (MergeDebt (MergeCredits mergeDebt)) - (NominalCredits nominalCredits) = - -- The scaling involves an operation: (a * b) `div` c - -- but where potentially the variables a,b,c may be bigger than a 32bit - -- integer can hold. This would be the case for runs that have more than - -- 4 billion entries. - -- - -- (This is assuming 64bit Int, the problem would be even worse for 32bit - -- systems. The solution here would also work for 32bit systems, allowing - -- up to, 2^31, 2 billion entries per run.) - -- - -- To work correctly in this case we need higher range for the intermediate - -- result a*b which could be bigger than 64bits can hold. A correct - -- implementation can use Rational, but a fast implementation should use - -- only integer operations. This is relevant because this is on the fast - -- path for small insertions into the table that often do no merging work - -- and just update credit counters. - - -- The fast implementation uses integer operations that produce a 128bit - -- intermediate result for the a*b result, and use a 128bit numerator in - -- the division operation (but 64bit denominator). These are known as - -- "widening multiplication" and "narrowing division". GHC has direct - -- support for these operations as primops: timesWord2# and quotRemWord2#, - -- but they are not exposed through any high level API shipped with GHC. - - -- The specification using Rational is: - let mergeCredits_spec = floor $ toRational nominalCredits - * toRational mergeDebt - / toRational nominalDebt - -- Note that it doesn't matter if we use floor or ceiling here. - -- Rounding errors will not compound because we sum nominal debt and - -- convert absolute nominal to absolute merging credit. We don't - -- convert each deposit and sum all the rounding errors. - -- When nominalCredits == nominalDebt then the result is exact anyway - -- (being mergeDebt) so the rounding mode makes no difference when we - -- get to the end of the merge. Using floor makes things simpler for - -- the fast integer implementation below, so we take that as the spec. - - -- If the nominalCredits is between 0 and nominalDebt then it's - -- guaranteed that the mergeCredit is between 0 and mergeDebt. - -- The mergeDebt fits in an Int, therefore the result does too. - -- Therefore the undefined behaviour case of timesDivABC_fast is - -- avoided and the w2i cannot overflow. - mergeCredits_fast = w2i $ timesDivABC_fast (i2w nominalCredits) - (i2w mergeDebt) - (i2w nominalDebt) - in assert (0 < nominalDebt) $ - assert (0 <= nominalCredits && nominalCredits <= nominalDebt) $ - assert (mergeCredits_spec == mergeCredits_fast) $ - MergeCredits mergeCredits_fast - where - {-# INLINE i2w #-} - {-# INLINE w2i #-} - i2w :: Int -> Word - w2i :: Word -> Int - i2w = fromIntegral - w2i = fromIntegral - --- | Compute @(a * b) `div` c@ for unsigned integers for the full range of --- 64bit unsigned integers, provided that @a <= c@ and thus the result will --- fit in 64bits. --- --- The @a * b@ intermediate result is computed using 128bit precision. --- --- Note: the behaviour is undefined if the result will not fit in 64bits. --- It will probably result in immediate termination with SIGFPE. --- -timesDivABC_fast :: Word -> Word -> Word -> Word -timesDivABC_fast (W# a) (W# b) (W# c) = - case timesWord2# a b of - (# ph, pl #) -> - case quotRemWord2# ph pl c of - (# q, _r #) -> W# q - -{-# SPECIALISE immediatelyCompleteIncomingRun :: - Tracer IO (AtLevel MergeTrace) - -> TableConfig - -> LevelNo - -> IncomingRun IO h - -> IO () #-} --- | Supply enough credits to complete the merge now. -immediatelyCompleteIncomingRun :: - (MonadSTM m, MonadST m, MonadMVar m, MonadMask m) - => Tracer m (AtLevel MergeTrace) - -> TableConfig - -> LevelNo - -> IncomingRun m h - -> m () -immediatelyCompleteIncomingRun tr conf ln ir = - case ir of - Single{} -> return () - Merging _ (NominalDebt nominalDebt) nominalCreditsVar mr -> do - - NominalCredits nominalCredits <- readPrimVar nominalCreditsVar - let !deposit = NominalCredits (nominalDebt - nominalCredits) - supplyCreditsIncomingRun conf ln ir deposit - - -- This ensures the merge is really completed. However, we don't - -- release the merge yet and only briefly inspect the resulting run. - bracket (MR.expectCompleted mr) releaseRef $ \r -> - traceWith tr $ AtLevel ln $ - TraceCompletedMerge (Run.size r) (Run.runFsPathsNumber r) - -{-# SPECIALISE snapshotIncomingRun :: - IncomingRun IO h - -> IO (Either (Ref (Run IO h)) - (MergePolicyForLevel, - NominalDebt, - NominalCredits, - Ref (MergingRun MR.LevelMergeType IO h))) #-} -snapshotIncomingRun :: - PrimMonad m - => IncomingRun m h - -> m (Either (Ref (Run m h)) - (MergePolicyForLevel, - NominalDebt, - NominalCredits, - Ref (MergingRun MR.LevelMergeType m h))) -snapshotIncomingRun (Single r) = pure (Left r) -snapshotIncomingRun (Merging mergePolicy nominalDebt nominalCreditsVar mr) = do - nominalCredits <- readPrimVar nominalCreditsVar - pure (Right (mergePolicy, nominalDebt, nominalCredits, mr)) - {------------------------------------------------------------------------------- Union level -------------------------------------------------------------------------------} @@ -980,7 +701,14 @@ addRunToLevels tr conf@TableConfig{..} resolve hfs hbio root uc r0 reg levels ul V.forM_ rs $ \r -> delayedCommit reg (releaseRef r) case confMergeSchedule of Incremental -> pure () - OneShot -> immediatelyCompleteIncomingRun tr conf ln ir + OneShot -> + bracket + (immediatelyCompleteIncomingRun conf ln ir) + releaseRef $ \r -> + + traceWith tr $ AtLevel ln $ + TraceCompletedMerge (Run.size r) (Run.runFsPathsNumber r) + return ir {-# SPECIALISE newIncomingRunAtLevel :: Tracer IO (AtLevel MergeTrace) @@ -1194,6 +922,9 @@ supplyCredits :: supplyCredits conf deposit levels = iforLevelM_ levels $ \ln (Level ir _rs) -> supplyCreditsIncomingRun conf ln ir deposit + --TODO: consider tracing supply of credits, + -- supplyCreditsIncomingRun could easily return the supplied credits + -- before & after, which may be useful for tracing. maxMergeDebt :: TableConfig -> MergePolicyForLevel -> LevelNo -> MergeDebt maxMergeDebt TableConfig { From 2080353b7ba418f0c2c3696284d17f11901cf8d2 Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Wed, 12 Mar 2025 19:37:31 +0000 Subject: [PATCH 15/15] Reduce the number of shrinks involving RunParams The shrink tests were timing out. --- .../Database/LSMTree/Internal/Snapshot/Codec.hs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs b/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs index c36498b4a..c86d5a65f 100644 --- a/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs +++ b/test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs @@ -294,7 +294,12 @@ arbitraryShortVector = do n <- chooseInt (0, 5) V.fromList <$> vector n -deriving newtype instance Arbitrary RunNumber +instance Arbitrary RunNumber where + arbitrary = RunNumber <$> arbitrarySizedNatural + shrink (RunNumber n) = + -- fewer shrinks + [RunNumber 0 | n > 0] + ++ [RunNumber (n `div` 2) | n >= 2] instance Arbitrary r => Arbitrary (SnapIncomingRun r) where arbitrary = oneof [ @@ -343,13 +348,11 @@ instance Arbitrary RunParams where instance Arbitrary RunDataCaching where arbitrary = elements [CacheRunData, NoCacheRunData] - shrink NoCacheRunData = [CacheRunData] - shrink _ = [] + shrink _ = [] instance Arbitrary IndexType where arbitrary = elements [Ordinary, Compact] - shrink Compact = [Ordinary] - shrink _ = [] + shrink _ = [] instance Arbitrary RunBloomFilterAlloc where arbitrary = oneof [