Skip to content

Commit

Permalink
feat: When ro-replay upper bound is unset use latest checkpointer block
Browse files Browse the repository at this point in the history
Change-Id: I808d5799db1639b45945fe36ffbd1762f070b734
  • Loading branch information
edmundnoble committed May 30, 2024
1 parent 440760f commit 4bb4ca3
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 29 deletions.
1 change: 1 addition & 0 deletions changes/2024-05-30T145604-0400.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a crash when using read-only replay with no upper bound
26 changes: 14 additions & 12 deletions src/Chainweb/Chainweb.hs
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ validatingMempoolConfig cid v gl gp mv = Mempool.InMemConfig

data StartedChainweb logger where
StartedChainweb :: (CanReadablePayloadCas cas, Logger logger) => !(Chainweb logger cas) -> StartedChainweb logger
Replayed :: !Cut -> !Cut -> StartedChainweb logger
Replayed :: !Cut -> !(Maybe Cut) -> StartedChainweb logger

data ChainwebStatus
= ProcessStarted
Expand Down Expand Up @@ -503,25 +503,27 @@ withChainwebInternal conf logger peer serviceSock rocksDb pactDbDir backupDir re
unsafeMkCut v <$> readHighestCutHeaders v (logFunctionText logger) webchain (cutHashesTable rocksDb)
lowerBoundCut <-
tryLimitCut webchain (fromMaybe 0 $ _cutInitialBlockHeightLimit $ _configCuts conf) highestCut
upperBoundCut <-
tryLimitCut webchain (fromMaybe maxBound $ _cutFastForwardBlockHeightLimit $ _configCuts conf) highestCut
upperBoundCut <- forM (_cutFastForwardBlockHeightLimit $ _configCuts conf) $ \upperBound ->
tryLimitCut webchain upperBound highestCut
let
replayOneChain :: (ChainResources logger, (BlockHeader, BlockHeader)) -> IO ()
replayOneChain :: (ChainResources logger, (BlockHeader, Maybe BlockHeader)) -> IO ()
replayOneChain (cr, (l, u)) = do
let chainPact = _chainResPact cr
let logCr = logFunctionText
$ addLabel ("component", "pact")
$ addLabel ("sub-component", "init")
$ _chainResLogger cr
logCr Info $ "pact db replaying between blocks "
<> T.pack (show (_blockHeight l, _blockHash l)) <> " and "
<> T.pack (show (_blockHeight u, _blockHash u))
void $ _pactReadOnlyReplay chainPact l u
logCr Info "pact db synchronized"
mapConcurrently_ replayOneChain $
HM.intersectionWith (,)
pactSyncChains
(HM.intersectionWith (,) (_cutMap lowerBoundCut) (_cutMap upperBoundCut))
let bounds =
HM.intersectionWith (,)
pactSyncChains
(HM.mapWithKey
(\cid bh ->
(bh, (HM.! cid) . _cutMap <$> upperBoundCut))
(_cutMap lowerBoundCut)
)
mapConcurrently_ replayOneChain bounds
logg Info "finished fast forward replay"
logFunctionJson logger Info PactReplaySuccessful
inner $ Replayed lowerBoundCut upperBoundCut
Expand All @@ -539,7 +541,7 @@ withChainwebInternal conf logger peer serviceSock rocksDb pactDbDir backupDir re
synchronizePactDb pactSyncChains newCut
logg Info "finished replaying Pact DBs to fast forward cut"
logFunctionJson logger Info PactReplaySuccessful
inner $ Replayed initialCut newCut
inner $ Replayed initialCut (Just newCut)
else do
initialCut <- _cut mCutDb
logg Info "start synchronizing Pact DBs to initial cut"
Expand Down
34 changes: 22 additions & 12 deletions src/Chainweb/Pact/PactService.hs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ import Chainweb.Pact.Types
import Chainweb.Pact.Validations
import Chainweb.Payload
import Chainweb.Payload.PayloadStore
import Chainweb.Storage.Table
import Chainweb.Time
import Chainweb.Transaction
import Chainweb.TreeDB
Expand Down Expand Up @@ -641,22 +640,34 @@ execReadOnlyReplay
:: forall logger tbl
. (Logger logger, CanReadablePayloadCas tbl)
=> BlockHeader
-> BlockHeader
-> Maybe BlockHeader
-> PactServiceM logger tbl ()
execReadOnlyReplay lowerBound upperBound = pactLabel "execReadOnlyReplay" $ do
execReadOnlyReplay lowerBound maybeUpperBound = pactLabel "execReadOnlyReplay" $ do
ParentHeader cur <- findLatestValidBlockHeader
logger <- view psLogger
bhdb <- view psBlockHeaderDb
pdb <- view psPdb
v <- view chainwebVersion
cid <- view chainId
-- lower bound must be an ancestor of upper.
liftIO (ancestorOf bhdb (_blockHash lowerBound) (_blockHash upperBound)) >>=
flip unless (throwM $ PactInternalError "lower bound is not an ancestor of upper bound")
upperBound <- case maybeUpperBound of
Just upperBound -> do
liftIO (ancestorOf bhdb (_blockHash lowerBound) (_blockHash upperBound)) >>=
flip unless (throwM $ PactInternalError "lower bound is not an ancestor of upper bound")

-- upper bound must be an ancestor of latest header.
liftIO (ancestorOf bhdb (_blockHash upperBound) (_blockHash cur)) >>=
flip unless (throwM $ PactInternalError "upper bound is not an ancestor of latest header")

return upperBound
Nothing -> do
liftIO (ancestorOf bhdb (_blockHash lowerBound) (_blockHash cur)) >>=
flip unless (throwM $ PactInternalError "lower bound is not an ancestor of latest header")

-- upper bound must be an ancestor of latest header.
liftIO (ancestorOf bhdb (_blockHash upperBound) (_blockHash cur)) >>=
flip unless (throwM $ PactInternalError "upper bound is not an ancestor of latest header")
return cur
liftIO $ logFunctionText logger Info $ "pact db replaying between blocks "
<> sshow (_blockHeight lowerBound, _blockHash lowerBound) <> " and "
<> sshow (_blockHeight upperBound, _blockHash upperBound)

let genHeight = genesisHeight v cid
-- we don't want to replay the genesis header in here.
Expand Down Expand Up @@ -690,10 +701,9 @@ execReadOnlyReplay lowerBound upperBound = pactLabel "execReadOnlyReplay" $ do
printError e = throwM e
handle printError $ runPact $ readFrom (Just $ ParentHeader bhParent) $ do
liftIO $ writeIORef heightRef (_blockHeight bh)
plData <- liftIO $ fromJuste <$> tableLookup
(_transactionDb pdb)
(_blockPayloadHash bh)
void $ execBlock bh (CheckablePayload plData)
payload <- liftIO $ fromJuste <$>
lookupPayloadDataWithHeight pdb (Just $ _blockHeight bh) (_blockPayloadHash bh)
void $ execBlock bh (CheckablePayload payload)
)
validationFailed <- readIORef validationFailedRef
when validationFailed $
Expand Down
2 changes: 1 addition & 1 deletion src/Chainweb/Pact/Service/BlockValidation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ lookupPactTxs confDepth txs reqQ = do

pactReadOnlyReplay
:: BlockHeader
-> BlockHeader
-> Maybe BlockHeader
-> PactQueue
-> IO ()
pactReadOnlyReplay l u reqQ = do
Expand Down
2 changes: 1 addition & 1 deletion src/Chainweb/Pact/Service/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ instance Show HistoricalLookupReq where

data ReadOnlyReplayReq = ReadOnlyReplayReq
{ _readOnlyReplayLowerBound :: !BlockHeader
, _readOnlyReplayUpperBound :: !BlockHeader
, _readOnlyReplayUpperBound :: !(Maybe BlockHeader)
}
instance Show ReadOnlyReplayReq where
show (ReadOnlyReplayReq l u) =
Expand Down
2 changes: 1 addition & 1 deletion src/Chainweb/WebPactExecutionService.hs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ data PactExecutionService = PactExecutionService
)
, _pactReadOnlyReplay :: !(
BlockHeader ->
BlockHeader ->
Maybe BlockHeader ->
IO ()
)
-- ^ Lookup pact hashes as of a block header to detect duplicates
Expand Down
7 changes: 5 additions & 2 deletions test/Chainweb/Test/MultiNode.hs
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ replayTest loglevel v n rdb pactDbDir step = do
& set (configCuts . cutInitialBlockHeightLimit) (Just replayInitialHeight)
& set configOnlySyncPact True)
n (Seconds 20) rdb pactDbDir $ \nid cw -> case cw of
Replayed l u -> do
Replayed l (Just u) -> do
writeIORef firstReplayCompleteRef True
_ <- flip HM.traverseWithKey (_cutMap l) $ \cid bh ->
assertEqual ("lower chain " <> sshow cid) replayInitialHeight (_blockHeight bh)
Expand All @@ -589,6 +589,7 @@ replayTest loglevel v n rdb pactDbDir step = do
_ <- flip HM.traverseWithKey (_cutMap u) $ \cid bh ->
assertGe ("upper chain " <> sshow cid) (Actual $ _blockHeight bh) (Expected replayInitialHeight)
return ()
Replayed _ Nothing -> error "replayTest: no replay upper bound"
_ -> error "replayTest: not a replay"
assertEqual "first replay completion" True =<< readIORef firstReplayCompleteRef
let fastForwardHeight = 10
Expand All @@ -600,13 +601,15 @@ replayTest loglevel v n rdb pactDbDir step = do
& set (configCuts . cutFastForwardBlockHeightLimit) (Just fastForwardHeight)
& set configOnlySyncPact True)
n (Seconds 20) rdb pactDbDir $ \_ cw -> case cw of
Replayed l u -> do
Replayed l (Just u) -> do
writeIORef secondReplayCompleteRef True
_ <- flip HM.traverseWithKey (_cutMap l) $ \cid bh ->
assertEqual ("lower chain " <> sshow cid) replayInitialHeight (_blockHeight bh)
_ <- flip HM.traverseWithKey (_cutMap u) $ \cid bh ->
assertEqual ("upper chain " <> sshow cid) fastForwardHeight (_blockHeight bh)
return ()
Replayed _ Nothing -> do
error "replayTest: no replay upper bound"
_ -> error "replayTest: not a replay"
assertEqual "second replay completion" True =<< readIORef secondReplayCompleteRef
tastylog "done."
Expand Down

0 comments on commit 4bb4ca3

Please sign in to comment.