Skip to content

Commit a1e4821

Browse files
committed
ChainSync: let GSM disable and re-enable CSJ; also enable LoP in PreSyncing
1 parent c0c529f commit a1e4821

File tree

7 files changed

+140
-54
lines changed

7 files changed

+140
-54
lines changed

ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/Node/GSM.hs

+5-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ module Ouroboros.Consensus.Node.GSM (
1212
, DurationFromNow (..)
1313
, GsmEntryPoints (..)
1414
, GsmNodeKernelArgs (..)
15-
, GsmState (..)
1615
, GsmView (..)
1716
, MarkerFileView (..)
1817
, WrapDurationUntilTooOld (..)
@@ -124,7 +123,7 @@ data GsmView m upstreamPeer selection chainSyncState = GsmView {
124123
setCaughtUpPersistentMark :: Bool -> m ()
125124
-- ^ EG touch/delete the marker file on disk
126125
,
127-
writeGsmState :: GsmState -> m ()
126+
writeGsmState :: GsmTransition -> m ()
128127
-- ^ EG update the TVar that the Diffusion Layer monitors, or en-/disable
129128
-- certain components of Genesis
130129
,
@@ -258,7 +257,7 @@ realGsmEntryPoints tracerArgs gsmView = GsmEntryPoints {
258257
(g', ev) <- blockWhileCaughtUp g
259258

260259
setCaughtUpPersistentMark False
261-
writeGsmState PreSyncing
260+
writeGsmState CaughtUpPreSyncing
262261
traceWith tracer ev
263262

264263
enterPreSyncing' g'
@@ -267,7 +266,7 @@ realGsmEntryPoints tracerArgs gsmView = GsmEntryPoints {
267266
enterPreSyncing' g = do
268267
blockUntilHonestAvailabilityAssumption
269268

270-
writeGsmState Syncing
269+
writeGsmState PreSyncingSyncing
271270
traceWith tracer GsmEventPreSyncingToSyncing
272271

273272
enterSyncing' g
@@ -283,12 +282,12 @@ realGsmEntryPoints tracerArgs gsmView = GsmEntryPoints {
283282

284283
case mev of
285284
Nothing -> do
286-
writeGsmState PreSyncing
285+
writeGsmState SyncingPreSyncing
287286
traceWith tracer GsmEventSyncingToPreSyncing
288287

289288
enterPreSyncing' g
290289
Just ev -> do
291-
writeGsmState CaughtUp
290+
writeGsmState SyncingCaughtUp
292291
setCaughtUpPersistentMark True
293292
traceWith tracer ev
294293

ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/NodeKernel.hs

+8-5
Original file line numberDiff line numberDiff line change
@@ -275,11 +275,14 @@ initNodeKernel args@NodeKernelArgs { registry, cfg, tracers
275275
, GSM.setCaughtUpPersistentMark = \upd ->
276276
(if upd then GSM.touchMarkerFile else GSM.removeMarkerFile)
277277
gsmMarkerFileView
278-
, GSM.writeGsmState = \gsmState ->
279-
atomicallyWithMonotonicTime $ \time -> do
280-
writeTVar varGsmState gsmState
281-
handles <- cschcMap varChainSyncHandles
282-
traverse_ (($ time) . ($ gsmState) . cschOnGsmStateChanged) handles
278+
, GSM.writeGsmState = \gsmTransition ->
279+
join $ atomicallyWithMonotonicTime $ \time -> do
280+
writeTVar varGsmState $ GSM.gsmNewState gsmTransition
281+
let nil = pure ()
282+
snoc acc hndl = do
283+
io <- cschOnGsmStateChanged hndl gsmTransition time
284+
pure $ do acc; io
285+
cschcMap varChainSyncHandles >>= foldM snoc nil
283286
, GSM.isHaaSatisfied = do
284287
readTVar varOutboundConnectionsState <&> \case
285288
-- See the upstream Haddocks for the exact conditions under

ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/GSM.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,8 @@ prop_sequential1 ctx cmds = runSimQC $ do
221221
atomically $ do
222222
writeTVar varMarker $ if b then Present else Absent
223223
,
224-
GSM.writeGsmState = \x -> atomically $ do
225-
writeTVar varGsmState x
224+
GSM.writeGsmState =
225+
atomically . writeTVar varGsmState . GSM.gsmNewState
226226
,
227227
GSM.isHaaSatisfied =
228228
isHaaSatisfied . Map.keysSet <$> readTVar varStates

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/MiniProtocol/ChainSync/Client.hs

+75-38
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ import Control.Monad.Class.MonadTimer (MonadTimer)
8080
import Control.Monad.Except (runExcept, throwError)
8181
import Control.Tracer
8282
import Data.Foldable (traverse_)
83+
import Data.Function (fix)
8384
import Data.Functor ((<&>))
8485
import Data.Kind (Type)
8586
import Data.Map.Strict (Map)
@@ -114,7 +115,7 @@ import qualified Ouroboros.Consensus.MiniProtocol.ChainSync.Client.HistoricityCh
114115
import qualified Ouroboros.Consensus.MiniProtocol.ChainSync.Client.InFutureCheck as InFutureCheck
115116
import qualified Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping as Jumping
116117
import Ouroboros.Consensus.MiniProtocol.ChainSync.Client.State
117-
import Ouroboros.Consensus.Node.GsmState (GsmState (..))
118+
import Ouroboros.Consensus.Node.GsmState (GsmState (..), GsmTransition (..))
118119
import Ouroboros.Consensus.Node.NetworkProtocolVersion
119120
import Ouroboros.Consensus.Protocol.Abstract
120121
import Ouroboros.Consensus.Storage.ChainDB (ChainDB)
@@ -404,14 +405,16 @@ bracketChainSyncClient
404405
cschJumping <- newTVarIO (Disengaged DisengagedDone)
405406
let handle = ChainSyncClientHandle {
406407
cschGDDKill = throwTo tid DensityTooLow
407-
, cschOnGsmStateChanged = updateLopBucketConfig lopBucket
408+
, cschOnGsmStateChanged = \gsmTransition time -> do
409+
updateLopBucketConfig lopBucket gsmTransition time
410+
pure $ pure ()
408411
, cschState
409412
, cschJumping
410413
, cschJumpInfo
411414
}
412415
insertHandle = atomicallyWithMonotonicTime $ \time -> do
413416
initialGsmState <- getGsmState
414-
updateLopBucketConfig lopBucket initialGsmState time
417+
initializeLopBucketConfig lopBucket initialGsmState time
415418
cschcAddHandle varHandles peer handle
416419
deleteHandle = atomically $ cschcRemoveHandle varHandles peer
417420
bracket_ insertHandle deleteHandle $ f Jumping.noJumping
@@ -425,16 +428,39 @@ bracketChainSyncClient
425428
tid <- myThreadId
426429
atomicallyWithMonotonicTime $ \time -> do
427430
initialGsmState <- getGsmState
428-
updateLopBucketConfig lopBucket initialGsmState time
431+
initializeLopBucketConfig lopBucket initialGsmState time
429432
cschJumpInfo <- newTVar Nothing
430433
context <- Jumping.makeContext varHandles jumpSize tracerCsj
431-
Jumping.registerClient context peer cschState $ \cschJumping -> ChainSyncClientHandle
432-
{ cschGDDKill = throwTo tid DensityTooLow
433-
, cschOnGsmStateChanged = updateLopBucketConfig lopBucket
434-
, cschState
435-
, cschJumping
436-
, cschJumpInfo
437-
}
434+
Jumping.registerClient context peer cschState $ \cschJumping ->
435+
fix $ \handle -> ChainSyncClientHandle -- NB @handle@ only occurs under a lambda
436+
{ cschGDDKill = throwTo tid DensityTooLow
437+
, cschOnGsmStateChanged = \gsmTransition time' -> do
438+
updateLopBucketConfig lopBucket gsmTransition time'
439+
let peerContext =
440+
context {Jumping.peer = peer, Jumping.handle = handle}
441+
-- 'cschJumpInfo' does not need to be reset. The ChainSync
442+
-- client constantly updates it, even when its not
443+
-- registered with CSJ. That's necessary: when this peer
444+
-- /re-registers/, it might immediately be the Dynamo, and
445+
-- the Dynamo must not have 'Nothing' in its
446+
-- 'cschJumpInfo'.
447+
case gsmTransition of
448+
PreSyncingSyncing -> pure $ pure ()
449+
SyncingCaughtUp -> do
450+
writeTVar cschJumping $ Disengaged DisengagedDone
451+
-- The GSM only transitions to CaughtUp if all peers
452+
-- have send MsgAwaitReply, so DisengagedDone is
453+
-- correct here.
454+
mbEv <- Jumping.unregisterClient peerContext
455+
pure $ traverse_ (traceWith (Jumping.tracer peerContext)) mbEv
456+
CaughtUpPreSyncing -> do
457+
mbEv <- Jumping.reregisterClient peerContext
458+
pure $ traverse_ (traceWith (Jumping.tracer peerContext)) mbEv
459+
SyncingPreSyncing -> pure $ pure ()
460+
, cschState
461+
, cschJumping
462+
, cschJumpInfo
463+
}
438464

439465
releaseContext (peerContext, _mbEv) = do
440466
mbEv <- atomically $ Jumping.unregisterClient peerContext
@@ -444,33 +470,44 @@ bracketChainSyncClient
444470
invalidBlockRejector
445471
tracer version pipelining getIsInvalidBlock (csCandidate <$> readTVar varState)
446472

447-
-- | Update the configuration of the bucket to match the given GSM state.
448-
-- NOTE: The new level is currently the maximal capacity of the bucket;
449-
-- maybe we want to change that later.
450-
updateLopBucketConfig :: LeakyBucket.Handlers m -> GsmState -> Time -> STM m ()
451-
updateLopBucketConfig lopBucket gsmState =
452-
LeakyBucket.updateConfig lopBucket $ \_ ->
453-
let config = lopBucketConfig gsmState in
454-
(LeakyBucket.capacity config, config)
455-
456-
-- | Wrapper around 'LeakyBucket.execAgainstBucket' that handles the
457-
-- disabled bucket by running the given action with dummy handlers.
458-
lopBucketConfig :: GsmState -> LeakyBucket.Config m
459-
lopBucketConfig gsmState =
460-
case (gsmState, csBucketConfig) of
461-
(Syncing, ChainSyncLoPBucketEnabled ChainSyncLoPBucketEnabledConfig {csbcCapacity, csbcRate}) ->
462-
LeakyBucket.Config
463-
{ capacity = fromInteger $ csbcCapacity,
464-
rate = csbcRate,
465-
onEmpty = throwIO EmptyBucket,
466-
fillOnOverflow = True
467-
}
468-
-- NOTE: If we decide to slow the bucket down when “almost caught-up”,
469-
-- we should add a state to the GSM and corresponding configuration
470-
-- fields and a bucket config here.
471-
(_, ChainSyncLoPBucketDisabled) -> LeakyBucket.dummyConfig
472-
(PreSyncing, ChainSyncLoPBucketEnabled _) -> LeakyBucket.dummyConfig
473-
(CaughtUp, ChainSyncLoPBucketEnabled _) -> LeakyBucket.dummyConfig
473+
mkLopConfig ChainSyncLoPBucketEnabledConfig {csbcCapacity, csbcRate} =
474+
LeakyBucket.Config {
475+
capacity = fromInteger csbcCapacity
476+
, rate = csbcRate
477+
, onEmpty = throwIO EmptyBucket
478+
, fillOnOverflow = True
479+
}
480+
481+
-- | Update the configuration of the bucket as part of the given GSM transition.
482+
updateLopBucketConfig :: LeakyBucket.Handlers m -> GsmTransition -> Time -> STM m ()
483+
updateLopBucketConfig lopBucket gsmTransition =
484+
LeakyBucket.updateConfig lopBucket $ \(oldLevel, _oldConfig) ->
485+
case csBucketConfig of
486+
ChainSyncLoPBucketDisabled -> (oldLevel, LeakyBucket.dummyConfig)
487+
ChainSyncLoPBucketEnabled csbc ->
488+
let config = mkLopConfig csbc
489+
in
490+
case gsmTransition of
491+
PreSyncingSyncing -> (oldLevel, config)
492+
SyncingCaughtUp -> (oldLevel, LeakyBucket.dummyConfig)
493+
CaughtUpPreSyncing -> (LeakyBucket.capacity config, config)
494+
SyncingPreSyncing -> (oldLevel, config)
495+
496+
initializeLopBucketConfig :: LeakyBucket.Handlers m -> GsmState -> Time -> STM m ()
497+
initializeLopBucketConfig lopBucket gsmState =
498+
LeakyBucket.updateConfig lopBucket $ \(uninitializedLevel, _oldConfig) ->
499+
let disabled = (uninitializedLevel, LeakyBucket.dummyConfig)
500+
in
501+
case csBucketConfig of
502+
ChainSyncLoPBucketDisabled -> disabled
503+
ChainSyncLoPBucketEnabled csbc ->
504+
let config = mkLopConfig csbc
505+
enabled = (LeakyBucket.capacity config, config)
506+
in
507+
case gsmState of
508+
PreSyncing -> enabled
509+
Syncing -> enabled
510+
CaughtUp -> disabled
474511

475512
-- Our task: after connecting to an upstream node, try to maintain an
476513
-- up-to-date header-only fragment representing their chain. We maintain

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/MiniProtocol/ChainSync/Client/Jumping.hs

+25
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ module Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping (
191191
, mkJumping
192192
, noJumping
193193
, registerClient
194+
, reregisterClient
194195
, rotateDynamo
195196
, unregisterClient
196197
) where
@@ -799,6 +800,30 @@ registerClient context peer csState mkHandle = do
799800
cschcAddHandle (handlesCol context) peer handle
800801
pure (context {peer, handle}, mbEv)
801802

803+
-- | Same as 'registerClient', but for a peer that had been previously disengaged.
804+
--
805+
-- This is specifically used for when the GSM transitions from
806+
-- 'Ouroboros.Consensus.Node.GsmState.CaughtUp' back to
807+
-- 'Ouroboros.Consensus.Node.GsmState.PreSyncing'.
808+
reregisterClient ::
809+
( LedgerSupportsProtocol blk,
810+
IOLike m
811+
) =>
812+
PeerContext m peer blk ->
813+
STM m (Maybe (TraceEventCsj peer blk))
814+
reregisterClient peerContext = do
815+
let h = handle peerContext
816+
(csjState, mbEv) <- getDynamo (handlesCol peerContext) >>= \case
817+
Nothing -> do
818+
fragment <- csCandidate <$> readTVar (cschState h)
819+
pure (Dynamo DynamoStarted $ pointSlot $ AF.anchorPoint fragment, Just InitializedAsDynamo)
820+
Just (_, dynHandle) -> do
821+
mJustInfo <- readTVar (cschJumpInfo dynHandle)
822+
(\x -> (x, Nothing)) <$> newJumper mJustInfo (Happy FreshJumper Nothing)
823+
writeTVar (cschJumping h) csjState
824+
cschcAddHandle (handlesCol peerContext) (peer peerContext) h
825+
pure mbEv
826+
802827
-- | Unregister a client from a 'PeerContext'; this might trigger the election
803828
-- of a new dynamo or objector if the peer was one of these two.
804829
unregisterClient ::

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/MiniProtocol/ChainSync/Client/State.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import Ouroboros.Consensus.HeaderStateHistory (HeaderStateHistory)
3535
import Ouroboros.Consensus.HeaderValidation (HeaderWithTime (..))
3636
import Ouroboros.Consensus.Ledger.SupportsProtocol
3737
(LedgerSupportsProtocol)
38-
import Ouroboros.Consensus.Node.GsmState (GsmState)
38+
import Ouroboros.Consensus.Node.GsmState (GsmTransition)
3939
import Ouroboros.Consensus.Util.IOLike (IOLike, NoThunks (..), STM,
4040
StrictTVar, Time, modifyTVar, newTVar, readTVar)
4141
import Ouroboros.Network.AnchoredFragment (AnchoredFragment,
@@ -81,7 +81,7 @@ data ChainSyncClientHandle m blk = ChainSyncClientHandle {
8181

8282
-- | Callback called by the GSM when the GSM state changes. They take the
8383
-- current time and should execute rapidly. Used to enable/disable the LoP.
84-
, cschOnGsmStateChanged :: !(GsmState -> Time -> STM m ())
84+
, cschOnGsmStateChanged :: !(GsmTransition -> Time -> STM m (m ()))
8585

8686
-- | Data shared between the client and external components like GDD.
8787
, cschState :: !(StrictTVar m (ChainSyncState blk))

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Node/GsmState.hs

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
{-# LANGUAGE DeriveAnyClass #-}
22
{-# LANGUAGE DeriveGeneric #-}
3+
{-# LANGUAGE LambdaCase #-}
34

45
-- | This module contains the definition of a state in the Genesis State Machine
56
-- (GSM). The GSM itself is defined in 'ouroboros-consensus-diffusion', but the
67
-- ChainSync client relies on its state.
7-
module Ouroboros.Consensus.Node.GsmState (GsmState (..)) where
8+
module Ouroboros.Consensus.Node.GsmState (
9+
GsmState (..)
10+
, GsmTransition (..)
11+
, gsmNewState
12+
) where
813

914
import GHC.Generics (Generic)
1015
import NoThunks.Class (NoThunks)
@@ -21,3 +26,20 @@ data GsmState =
2126
CaughtUp
2227
-- ^ We are caught-up.
2328
deriving (Eq, Show, Read, Generic, NoThunks)
29+
30+
data GsmTransition =
31+
PreSyncingSyncing
32+
|
33+
SyncingCaughtUp
34+
|
35+
CaughtUpPreSyncing
36+
|
37+
SyncingPreSyncing
38+
deriving (Eq, Show, Read)
39+
40+
gsmNewState :: GsmTransition -> GsmState
41+
gsmNewState = \case
42+
PreSyncingSyncing -> Syncing
43+
SyncingCaughtUp -> CaughtUp
44+
CaughtUpPreSyncing -> PreSyncing
45+
SyncingPreSyncing -> PreSyncing

0 commit comments

Comments
 (0)