Skip to content

Conversation

amesgen
Copy link
Member

@amesgen amesgen commented Jun 30, 2025

Closes #1424

Based on top of #1513, see there for the relation to this change.

Note that we need #1573 before this can be released in a node.

This PR is intended to be reviewed commit-by-commit.


This PR replaces the previous logic for when to create snapshots (It would be possible to preserve it, but I don't see a big motivation). Concretely, SnapshotFrequencyArgs contains

  • sfaInterval :: SlotNo: Create snapshots every sfaInterval many slots.

    Default: 2*k = 4320 slots, so 72 min on mainnet as before.

  • sfaOffset :: SlotNo: Allows to determine the offset of where snapshots are taken, see below.

    Default: 0

  • sfaRateLimit :: DiffTime: A minimum duration between snapshots (used to avoid excessive snapshots while syncing).

    Default: 10 minutes (previous value was 6 minutes, which seemed a bit low, so I increased it somewhat. Maybe it should be increased even more now that we no longer have the substantialAmountOfBlocksWereProcessed check.)

Concretely, the node will try to create snapshots for the last immutable blocks before the slots

sfaOffset, sfaOffset + sfaInterval, sfaOffset + 2 * sfaInterval, sfaOffset + 3 * sfaInterval, ...

but can skip creating some of these depending on the sfaRateLimit (which can be disabled by setting it to a non-positive value). Also see the Haddocks.

For example, setting sfaInterval = 10*2160*20 (one mainnet Shelley epoch) and sfaOffset = 172800 will cause the node to create snapshots for the last block in every Shelley epoch (because the first Shelley slot is 4492800, and 4492800 `mod` (10*2160*20) = 172800. By tweaking sfaOffset, one can take snapshots eg right before the midway point in each epoch.

There is some code that could be shared between V1 and V2 (already even before this PR), but given the upcoming removal of V1, this seems acceptable for now.


Also includes a test running a ChainDB in IOSim to test that everything is hooked up correctly (in particular regarding the background threads).

See #1513 (comment) for sync regression tests.

@amesgen
Copy link
Member Author

amesgen commented Jun 30, 2025

I integrated this into 10.5.1 for testing:

In the Node config, the following config would result in the node creating a snapshot for the last block in every Shelly epoch (having 10*2160/20 = 432000 slots):

LedgerDB:
  Backend: V2InMemory # or V1LMDB
  # Number of slots in a Shelley epoch, so we will create snapshots
  # precisely for the last block in each Shelley epoch
  SnapshotInterval: 432000
  # Due to Byron, the first slots of Shelley epochs are offset by this amount
  SlotOffset: 172800
  # Disable the rate limit, to be sure that we definitely create snapshots
  # for all epochs
  RateLimit: 0

Currently, this treats SnapshotInterval (which previously was a number of seconds) as a number of slots. This is convenient, and should be fine for mainnet AFAICT, but maybe we still want to add some kind of warning (see also the TODO in the code there).

@amesgen amesgen force-pushed the amesgen/predictable-snapshots branch from 0e21795 to 4d5bb72 Compare June 30, 2025 08:52
}
forM_ snapshotSlots $ \slot -> do
-- Prune the 'DbChangelog' such that the resulting anchor state has slot
-- number @slot@.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
-- number @slot@.
-- number @slot@ or younger.

?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each s in snapshotSlots is the slot of a ledger state in DbChangelog (see the contract of onDiskSnapshotSelector), so it is true as written. (Otherwise, we wouldn't take a snapshot for the requested slot, so snapshots wouldn't be predictable.)

@amesgen amesgen force-pushed the amesgen/ledgerdb-garbage-collect-states branch from a8fa7e2 to b503dc3 Compare June 30, 2025 11:52
@amesgen amesgen force-pushed the amesgen/predictable-snapshots branch from 4d5bb72 to 6f063b3 Compare June 30, 2025 11:52
@amesgen amesgen force-pushed the amesgen/ledgerdb-garbage-collect-states branch from b503dc3 to 6c78fad Compare July 2, 2025 08:14
@amesgen amesgen force-pushed the amesgen/predictable-snapshots branch from 6f063b3 to dab2fbf Compare July 2, 2025 08:19
@amesgen amesgen force-pushed the amesgen/ledgerdb-garbage-collect-states branch from 6c78fad to 48bb1fe Compare July 7, 2025 08:26
@amesgen amesgen force-pushed the amesgen/predictable-snapshots branch from dab2fbf to 70fcdc3 Compare July 7, 2025 08:27
@amesgen amesgen force-pushed the amesgen/ledgerdb-garbage-collect-states branch from 48bb1fe to ad7acfa Compare July 9, 2025 12:26
@amesgen amesgen force-pushed the amesgen/predictable-snapshots branch from 70fcdc3 to 2ecc9b9 Compare July 9, 2025 12:26
@amesgen amesgen force-pushed the amesgen/ledgerdb-garbage-collect-states branch from ad7acfa to d791dfb Compare August 4, 2025 11:26
@amesgen amesgen force-pushed the amesgen/predictable-snapshots branch from 2ecc9b9 to 9a0585f Compare August 4, 2025 11:27
@amesgen amesgen force-pushed the amesgen/ledgerdb-garbage-collect-states branch from d791dfb to 247c489 Compare August 8, 2025 14:13
@amesgen amesgen force-pushed the amesgen/predictable-snapshots branch from 9a0585f to 5fdc096 Compare August 8, 2025 14:13
@amesgen amesgen force-pushed the amesgen/ledgerdb-garbage-collect-states branch from 247c489 to 08bda65 Compare August 13, 2025 14:41
@amesgen amesgen force-pushed the amesgen/predictable-snapshots branch from 5fdc096 to 2e8e7be Compare August 13, 2025 14:42
@amesgen amesgen force-pushed the amesgen/ledgerdb-garbage-collect-states branch from 08bda65 to dec284f Compare August 13, 2025 15:27
Superseded by the rework of the snapshot policy for predictable snapshots, with
dedicated new tests
It is no longer needed by the predictable snapshotting logic.
@amesgen amesgen force-pushed the amesgen/predictable-snapshots branch from 2e8e7be to e542170 Compare August 13, 2025 15:28
-- of 'sfaInterval'.
data SnapshotFrequencyArgs = SnapshotFrequencyArgs
{ sfaInterval :: OverrideOrDefault SlotNo
-- ^ Try to write snapshots every 'sfaInterval' many slots. Must be positive.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it necessarily positive by using a SlotNo?

let immutableStates =
AS.dropNewest (fromIntegral (envMaxRollbacks env)) $ changelogStates chlog
immutableSlots :: [SlotNo] =
nubOrd . mapMaybe (withOriginToMaybe . getTipSlot) $
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This nubOrd I imagine is there only because of EBBs, right?

Comment on lines +343 to +348
atomically $ modifyTVar (ldbChangelog env) (prune pruneStrat)
-- Flush the LedgerDB such that we can take a snapshot for the new anchor
-- state due to the previous prune.
withWriteLock
(ldbLock env)
(flushLedgerDB (ldbChangelog env) (ldbBackingStore env))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused. I seem to remeber the flow was the opposite, first flush then prune, no?

EDIT: Ah now I think pruning just affects the changelog states and not the diffs.

(configCodec . getExtLedgerCfg . ledgerDbCfg $ ldbCfg env)
(LedgerDBSnapshotEvent >$< ldbTracer env)
(ldbHasFS env)
(anchorHandle $ snd $ prune pruneStrat lseq)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we prune the lseq that we provide to the function that takes the snapshot, but we do not modify the ldbSeq. However from what I understood about V1 above, there we do prune the dbChangelog in the environment. Why this discrepancy?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Above we do:

atomically $ modifyTVar (ldbChangelog env) (prune pruneStrat)

Base automatically changed from amesgen/ledgerdb-garbage-collect-states to main September 2, 2025 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: 👀 In review
Development

Successfully merging this pull request may close these issues.

2 participants