Skip to content

Commit 3bed5e0

Browse files
committed
ChainDB: pass new block to ledger validation
In `ChainDB.addBlock`, we receive a new block and use it to try switch to a longer chain. This will require applying the block to the ledger, which requires reading the block from disk and parsing it again. This is right in the critical path of (bulk) chain sync. Since we have the block in memory, we can avoid the redundant read and pass it to the validation code directly. Note that when switching to a fork or when we can extend the new chain with more blocks after the new block, we'll still have to read blocks from disk in order to validate them, but not the new block.
1 parent da1bd01 commit 3bed5e0

File tree

3 files changed

+42
-11
lines changed

3 files changed

+42
-11
lines changed

ouroboros-consensus/src/Ouroboros/Storage/ChainDB/Impl/Background.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ scheduledChainSelection cdb@CDB{..} curSlot = do
374374
-- which case, the ChainDB has to be (re)started, triggering a full
375375
-- chain selection, which would include these blocks. So there is no
376376
-- risk of "forgetting" to add a block.
377-
mapM_ (chainSelectionForBlock cdb) hdrs
377+
mapM_ (chainSelectionForBlock cdb . Left) hdrs
378378

379379
-- | Whenever the current slot changes, call 'scheduledChainSelection' for the
380380
-- (new) current slot.

ouroboros-consensus/src/Ouroboros/Storage/ChainDB/Impl/ChainSel.hs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ import Ouroboros.Consensus.Util.STM (WithFingerprint (..))
6464
import Ouroboros.Storage.ChainDB.API (InvalidBlockReason (..))
6565
import Ouroboros.Storage.ChainDB.Impl.ImmDB (ImmDB)
6666
import qualified Ouroboros.Storage.ChainDB.Impl.ImmDB as ImmDB
67-
import Ouroboros.Storage.ChainDB.Impl.LgrDB (LgrDB)
67+
import Ouroboros.Storage.ChainDB.Impl.LgrDB (LgrDB,
68+
NewBlockInMemory (..))
6869
import qualified Ouroboros.Storage.ChainDB.Impl.LgrDB as LgrDB
6970
import qualified Ouroboros.Storage.ChainDB.Impl.Query as Query
7071
import qualified Ouroboros.Storage.ChainDB.Impl.Reader as Reader
@@ -153,6 +154,7 @@ initialChainSelection immDB volDB lgrDB tracer cfg varInvalid curSlot = do
153154
(contramap (TraceInitChainSelEvent . InitChainSelValidation) tracer)
154155
cfg
155156
varInvalid
157+
NoNewBlockInMemory
156158
curChainAndLedger
157159
(fmap (mkCandidateSuffix 0) candidates)
158160

@@ -206,7 +208,7 @@ addBlock cdb@CDB{..} b = do
206208
| otherwise -> do
207209
VolDB.putBlock cdbVolDB b
208210
trace $ AddedBlockToVolDB (blockPoint b) (blockNo b) (toIsEBB (cdbIsEBB b))
209-
chainSelectionForBlock cdb (getHeader b)
211+
chainSelectionForBlock cdb (Right b)
210212
where
211213
trace :: TraceAddBlockEvent blk -> m ()
212214
trace = traceWith (contramap TraceAddBlockEvent cdbTracer)
@@ -265,9 +267,11 @@ chainSelectionForBlock
265267
, HasCallStack
266268
)
267269
=> ChainDbEnv m blk
268-
-> Header blk
270+
-> Either (Header blk) blk
271+
-- ^ @'Header' blk@ in case the block is no longer in memory, @blk@ in
272+
-- case it still is.
269273
-> m ()
270-
chainSelectionForBlock cdb@CDB{..} hdr = do
274+
chainSelectionForBlock cdb@CDB{..} hdrOrBlk = do
271275
curSlot <- atomically $ getCurrentSlot cdbBlockchainTime
272276

273277
(invalid, isMember, succsOf, predecessor, curChain, tipPoint, ledgerDB, immBlockNo)
@@ -328,6 +332,9 @@ chainSelectionForBlock cdb@CDB{..} hdr = do
328332
where
329333
secParam@(SecurityParam k) = protocolSecurityParam cdbNodeConfig
330334

335+
hdr :: Header blk
336+
hdr = either id getHeader hdrOrBlk
337+
331338
p :: Point blk
332339
p = headerPoint hdr
333340

@@ -445,6 +452,7 @@ chainSelectionForBlock cdb@CDB{..} hdr = do
445452
(contramap (TraceAddBlockEvent . AddBlockValidation) cdbTracer)
446453
cdbNodeConfig
447454
cdbInvalid
455+
(either (const NoNewBlockInMemory) NewBlockInMemory hdrOrBlk)
448456

449457
-- | Try to swap the current (chain) fragment with the given candidate
450458
-- fragment. The 'LgrDB.LedgerDB' is updated in the same transaction.
@@ -608,13 +616,14 @@ chainSelection
608616
-> Tracer m (TraceValidationEvent blk)
609617
-> NodeConfig (BlockProtocol blk)
610618
-> StrictTVar m (WithFingerprint (InvalidBlocks blk))
619+
-> NewBlockInMemory blk
611620
-> ChainAndLedger blk -- ^ The current chain and ledger
612621
-> NonEmpty (CandidateSuffix blk) -- ^ Candidates
613622
-> m (Maybe (ChainAndLedger blk))
614623
-- ^ The (valid) chain and corresponding LedgerDB that was selected, or
615624
-- 'Nothing' if there is no valid chain preferred over the current
616625
-- chain.
617-
chainSelection lgrDB tracer cfg varInvalid
626+
chainSelection lgrDB tracer cfg varInvalid newBlockInMemory
618627
curChainAndLedger@(ChainAndLedger curChain _) candidates =
619628
assert (all (preferAnchoredCandidate cfg curChain . _suffix) candidates) $
620629
assert (all (isJust . fitCandidateSuffixOn curChain) candidates) $
@@ -627,7 +636,7 @@ chainSelection lgrDB tracer cfg varInvalid
627636
validate :: ChainAndLedger blk -- ^ Current chain and ledger
628637
-> CandidateSuffix blk -- ^ Candidate fragment
629638
-> m (Maybe (ChainAndLedger blk))
630-
validate = validateCandidate lgrDB tracer cfg varInvalid
639+
validate = validateCandidate lgrDB tracer cfg varInvalid newBlockInMemory
631640

632641
-- 1. Take the first candidate from the list of sorted candidates
633642
-- 2. Validate it
@@ -719,12 +728,13 @@ validateCandidate
719728
-> Tracer m (TraceValidationEvent blk)
720729
-> NodeConfig (BlockProtocol blk)
721730
-> StrictTVar m (WithFingerprint (InvalidBlocks blk))
731+
-> NewBlockInMemory blk
722732
-> ChainAndLedger blk -- ^ Current chain and ledger
723733
-> CandidateSuffix blk -- ^ Candidate fragment
724734
-> m (Maybe (ChainAndLedger blk))
725-
validateCandidate lgrDB tracer cfg varInvalid
735+
validateCandidate lgrDB tracer cfg varInvalid newBlockInMemory
726736
(ChainAndLedger curChain curLedger) candSuffix =
727-
LgrDB.validate lgrDB curLedger rollback newBlocks >>= \case
737+
LgrDB.validate lgrDB curLedger newBlockInMemory rollback newBlocks >>= \case
728738
LgrDB.MaximumRollbackExceeded supported _ -> do
729739
trace $ CandidateExceedsRollback {
730740
_supportedRollback = supported

ouroboros-consensus/src/Ouroboros/Storage/ChainDB/Impl/LgrDB.hs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ module Ouroboros.Storage.ChainDB.Impl.LgrDB (
3838
-- * Validation
3939
, validate
4040
, ValidateResult
41+
, NewBlockInMemory (..)
4142
-- * Garbage collect points of previously applied blocks
4243
, garbageCollectPrevApplied
4344
-- * Re-exports
@@ -354,15 +355,31 @@ getDiskPolicy LgrDB{ args = LgrDbArgs{..} } = lgrDiskPolicy
354355
type ValidateResult blk =
355356
LedgerDB.SwitchResult (ExtValidationError blk) (ExtLedgerState blk) (Point blk) 'False
356357

358+
-- | When adding a block to the chain in
359+
-- 'Ouroboros.Storage.ChainDB.Impl.ChainSel.addBlock', the resulting chain has
360+
-- to be validated. To avoid reading the new block from disk and deserialising
361+
-- it while we still have it in memory, it can pass be to the validation
362+
-- functions directly in the form of this data type.
363+
--
364+
-- This removes a disk read from the critical path of adding a block that
365+
-- extends the current chain, improving bulk sync speed.
366+
--
367+
-- When performing validation in a context other than @addBlock@, use
368+
-- 'NoNewBlockInMemory'.
369+
data NewBlockInMemory blk
370+
= NewBlockInMemory blk
371+
| NoNewBlockInMemory
372+
357373
validate :: forall m blk. (IOLike m, ProtocolLedgerView blk, HasCallStack)
358374
=> LgrDB m blk
359375
-> LedgerDB blk
360376
-- ^ This is used as the starting point for validation, not the one
361377
-- in the 'LgrDB'.
378+
-> NewBlockInMemory blk
362379
-> Word64 -- ^ How many blocks to roll back
363380
-> [Header blk]
364381
-> m (ValidateResult blk)
365-
validate LgrDB{..} ledgerDB numRollbacks = \hdrs -> do
382+
validate LgrDB{..} ledgerDB newBlockInMemory numRollbacks = \hdrs -> do
366383
blocks <- toBlocks hdrs <$> atomically (readTVar varPrevApplied)
367384
res <- LedgerDB.ledgerDbSwitch conf numRollbacks blocks ledgerDB
368385
atomically $ modifyTVar varPrevApplied $
@@ -374,7 +391,11 @@ validate LgrDB{..} ledgerDB numRollbacks = \hdrs -> do
374391
toBlocks hdrs prevApplied =
375392
[ ( if Set.member (headerPoint hdr) prevApplied
376393
then Reapply else Apply
377-
, toRefOrVal (Left hdr) )
394+
, toRefOrVal $ case newBlockInMemory of
395+
NewBlockInMemory blk
396+
| blockHash blk == headerHash hdr
397+
-> Right blk
398+
_ -> Left hdr )
378399
| hdr <- hdrs ]
379400

380401
-- | Based on the 'ValidateResult', return the hashes corresponding to

0 commit comments

Comments
 (0)