Skip to content

Commit bd9c058

Browse files
ffakenznoonio
andauthored
add docs about event log rotation (#2131)
<!-- Describe your change here --> - Added a new section in docs explaining how event log rotation works, including usage examples and rough guidance. - Added `--persistence-rotate-after` as a new optional argument to the hydra-cluster options, so it can be passed when running with `--busy` (by default, behavior remains unchanged). --- <!-- Consider each and tick it off one way or the other --> * [X] CHANGELOG updated or not needed * [X] Documentation updated or not needed * [X] Haddocks updated or not needed * [X] No new TODOs introduced or explained herafter --------- Co-authored-by: Noon <[email protected]>
1 parent 0377bcd commit bd9c058

File tree

4 files changed

+35
-5
lines changed

4 files changed

+35
-5
lines changed

docs/docs/dev/architecture/event-sourcing.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,27 @@ When implementing an event source or sink, you might want to consider testing th
3636
- [ ] Concurrent use of `sourceEvents` is possible
3737

3838
- [ ] General: allocated resources are released (use with/bracket pattern)
39+
40+
### Event Log Rotation
41+
42+
Long-living heads may produce a large number of persisted events, which can impact the restart time of the hydra-node as it needs to read in all the previous to recreate its state.
43+
44+
Event log rotation was introduced to improve recovery times by reducing the number of events that need to be replayed on startup. This is achieved by periodically replacing the current event log with a new one that starts from a checkpoint event, which captures the latest aggregated head state.
45+
46+
Only rotated log files are saved with an incrementing `logId` suffix in their names, while the main `state` log file remains unchanged to preserve backward compatibility. This `logId` suffix corresponds to the ID of the last event included in that file.
47+
Rotation can be enabled via the optional `--persistence-rotate-after` command-line argument, which specifies the number of events after which rotation should occur.
48+
> For example, with `--persistence-rotate-after 100`, you’ll get rotated files named: state-99, state-199, state-299, and so on, each containing 100 events. This is because event IDs start at 0.
49+
50+
Note that, depending on the rotation configuration used, the current `state` file may already contain more events than the specified threshold, causing a rotation to occur immediately on startup before any new inputs are processed.
51+
52+
Upon rotation, a server output is produced to notify external agents when a checkpoint occurs, allowing them to perform archival or cleanup actions without interrupting the Hydra Head.
53+
54+
The appropriate value for `--persistence-rotate-after` depends on your specific use case and the expected transaction volume.
55+
56+
> As a rough guideline, in a simple scenario (running a single party on devnet that repeatedly re-spends the same committed UTxO) we observed that setting `--persistence-rotate-after 10000` results in rotated log files of about 8 MB every 3 minutes.
57+
>
58+
> Keep in mind that the size and frequency of rotated files will vary depending on several factors:
59+
> * Transaction sizes: Larger transactions result in larger event payloads.
60+
> * Number of party members: More parties increase the number of L2 protocol messages per snapshot, generating more events.
61+
> * Ledger UTxO size: A higher number of UTxOs increases the size of certain events like snapshots.
62+
> * Transaction throughput (TPS): Higher TPS leads to more events being produced over time.

hydra-cluster/exe/hydra-cluster/Main.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ run options =
4848
let hydraScriptsTxId = intercalate "," $ toString . serialiseToRawBytesHexText <$> txId
4949
let envPath = workDir </> ".env"
5050
writeFile envPath $ "HYDRA_SCRIPTS_TX_ID=" <> hydraScriptsTxId
51-
singlePartyOpenAHead tracer workDir backend txId $ \client walletSk _headId -> do
51+
singlePartyOpenAHead tracer workDir backend txId persistenceRotateAfter $ \client walletSk _headId -> do
5252
case scenario of
5353
Idle -> forever $ pure ()
5454
RespendUTxO -> do
5555
-- Start respending the same UTxO with a 100ms delay.
5656
-- XXX: Should make this configurable
5757
respendUTxO client walletSk 0.1
5858
where
59-
Options{knownNetwork, stateDirectory, publishHydraScripts, useMithril, scenario} = options
59+
Options{knownNetwork, stateDirectory, publishHydraScripts, useMithril, scenario, persistenceRotateAfter} = options
6060

6161
withRunningCardanoNode tracer workDir network action =
6262
findRunningCardanoNode (contramap FromCardanoNode tracer) workDir network >>= \case

hydra-cluster/src/Hydra/Cluster/Options.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Data.ByteString.Char8 qualified as BSC
66
import Data.List qualified as List
77
import Hydra.Cardano.Api (TxId, deserialiseFromRawBytesHex)
88
import Hydra.Cluster.Fixture (KnownNetwork (..))
9+
import Hydra.Options (persistenceRotateAfterParser)
910
import Hydra.Prelude
1011
import Options.Applicative (Parser, eitherReader, flag, flag', help, long, metavar, strOption)
1112
import Options.Applicative.Builder (option)
@@ -16,6 +17,7 @@ data Options = Options
1617
, publishHydraScripts :: PublishOrReuse
1718
, useMithril :: UseMithril
1819
, scenario :: Scenario
20+
, persistenceRotateAfter :: Maybe Natural
1921
}
2022
deriving stock (Show, Eq, Generic)
2123
deriving anyclass (ToJSON)
@@ -40,6 +42,7 @@ parseOptions =
4042
<*> parsePublishHydraScripts
4143
<*> parseUseMithril
4244
<*> parseScenario
45+
<*> optional persistenceRotateAfterParser
4346
where
4447
parseKnownNetwork =
4548
flag' (Just Preview) (long "preview" <> help "The preview testnet")

hydra-cluster/src/Hydra/Cluster/Scenarios.hs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ import Hydra.Ledger.Cardano (mkSimpleTx, mkTransferTx, unsafeBuildTransaction)
106106
import Hydra.Ledger.Cardano.Evaluate (maxTxExecutionUnits)
107107
import Hydra.Logging (Tracer, traceWith)
108108
import Hydra.Node.DepositPeriod (DepositPeriod (..))
109-
import Hydra.Options (CardanoChainConfig (..), ChainBackendOptions (..), DirectOptions (..), startChainFrom)
109+
import Hydra.Options (CardanoChainConfig (..), ChainBackendOptions (..), DirectOptions (..), RunOptions (..), startChainFrom)
110110
import Hydra.Tx (HeadId, IsTx (balance), Party, txId)
111111
import Hydra.Tx.ContestationPeriod qualified as CP
112112
import Hydra.Tx.Utils (dummyValidatorScript, verificationKeyToOnChainId)
@@ -500,10 +500,11 @@ singlePartyOpenAHead ::
500500
FilePath ->
501501
backend ->
502502
[TxId] ->
503+
Maybe Natural ->
503504
-- | Continuation called when the head is open
504505
(HydraClient -> SigningKey PaymentKey -> HeadId -> IO a) ->
505506
IO a
506-
singlePartyOpenAHead tracer workDir backend hydraScriptsTxId callback =
507+
singlePartyOpenAHead tracer workDir backend hydraScriptsTxId persistenceRotateAfter callback =
507508
(`finally` returnFundsToFaucet tracer backend Alice) $ do
508509
refuelIfNeeded tracer backend Alice 25_000_000
509510
-- Start hydra-node on chain tip
@@ -521,7 +522,9 @@ singlePartyOpenAHead tracer workDir backend hydraScriptsTxId callback =
521522
utxoToCommit <- seedFromFaucet backend walletVk 100_000_000 (contramap FromFaucet tracer)
522523

523524
let hydraTracer = contramap FromHydraNode tracer
524-
withHydraNode hydraTracer aliceChainConfig workDir 1 aliceSk [] [1] $ \n1 -> do
525+
options <- prepareHydraNode aliceChainConfig workDir 1 aliceSk [] [] id
526+
let options' = options{persistenceRotateAfter}
527+
withPreparedHydraNode hydraTracer workDir 1 options' $ \n1 -> do
525528
-- Initialize & open head
526529
send n1 $ input "Init" []
527530
blockTime <- Backend.getBlockTime backend

0 commit comments

Comments
 (0)