Persisted mem-table checkpoints for crash recovery (Node #261) #64
adamantmm
started this conversation in
ADM Nodes, Delegates & Pools
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
The ADAMANT node now supports persisted, rotating checkpoints of derived
mem_*state. After a forced interruption that leaves memory mirrors inconsistent, startup can restore the latest verified checkpoint and replay only blocks after the checkpoint height — instead of rebuilding all memory tables from height 1.This implements the design from #227, merged in #261 (
feat/memtable-checkpoints→dev, head84dc3613).Checkpoints are a local recovery cache only. Blocks and deterministic replay remain the source of truth. If verification or replay fails, the node falls back to the existing full rebuild path (fail-closed).
Problem
Derived tables (
mem_accounts,mem_round, delegate/multisig junction tables, and their unconfirmed mirrors) can become inconsistent if the process is killed while block, account, or round writes are in flight:Graceful shutdown (
Ctrl+C/SIGTERM) is still the required operational path. Checkpoints reduce recovery time when a forced kill happens anyway.What was implemented
Storage (SQL migration)
mem_state_checkpoint_meta(slot,schemaVersion,height,blockId,round,nethash,createdAt,status,digest)mem_ckpt_0..2_*for confirmed state:mem_accountsmem_accounts2delegatesmem_accounts2multisignaturesmem_roundmem_accounts2u_*) are not checkpointed; they are rebuilt from confirmed state on restore.Migration:
sql/migrations/20260709120000_createMemStateCheckpoints.sqlCore logic
logic/memCheckpoint.jsmodules/memCheckpoints.jsonBlockApplied,tryRecover, schema guard onblockchainReadysql/memCheckpoints.jsmodules/loader.jsload({ startOffset, skipTableReset })modules/blocks/chain.jsWhen checkpoints are created
applyBlockpipeline has persisted the block.SYNC_ROUND_INTERVAL = 100) so sync throughput is not reduced by a fullmem_*copy on every boundary.Non-blocking copy (MVCC pin)
Checkpoint creation uses a PostgreSQL
REPEATABLE READtransaction:onPinnedreleases the block-processing critical section (library.sequence) as soon as thewritingmetadata row is persisted.markMetaCompletecontinue in the background against the frozen snapshot — later blocks are invisible to it.mem_roundmust be settled at the checkpoint roundheightThis avoids holding the forging/sync critical section for the entire copy (~350–400 ms on testnet-sized state in live tests).
Verification chain (fail-closed)
Before a checkpoint is accepted for recovery:
status === 'complete'schemaVersionmatchesnethashmatches node configdigestmem_*(on startup; disables creation if mismatch)mem_round, no orphaned block refs, delegates presentRecovery tries all complete slots newest-first (
getCompleteDesc), so one corrupted newest slot does not force a full rebuild if an older valid slot exists.Startup recovery flow
checkMemTables()detects inconsistency →reload()loading.snapshot/ verification mode is active → skip checkpoints, full genesis rebuildmemCheckpoints.tryRecover():mem_*u_*) state from confirmed mirrorslastBlockfrom checkpoint blockcheckpoint.height + 1to tipConfiguration
config.default.json:Disable with
"enabled": falseif needed. Default is enabled.Safety / consensus notes
applyBlock.mem_*remain unsafe; there is no supported deterministic repair without replay or a trusted DB snapshot.Storage impact (from issue investigation)
On current mainnet-sized
mem_*footprint (~48 MB total across six base tables):Validation performed
Unit tests:
test/unit/logic/memCheckpoint.js— 18/18 passing (lifecycle, digest rejection, rotating-slot fallback, invariant short-circuit).Manual testnet (
162.55.32.80,adamanttest, branch84dc3613):DELETE FROM mem_accounts→ restore from checkpoint → replay ~10 blocks →Blockchain ready(~78 ms restore-to-ready in that run)mem_accountscount restored correctlyLocal testnet (
test/config.json,memCheckpoints.enabled: true):mem_accounts= 1175,Blockchain readyOperator reminders
Cleaned up successfully).mem_*; checkpoints shorten recovery but do not replace correct shutdown.2d7cc2cebuilds, expect a one-time digest-format mismatch on old checkpoints → fail-closed full rebuild, then new-format checkpoints.Adamant-im/docsare planned as follow-up (#261 notes).References
dev:logic/memCheckpoint.js,modules/memCheckpoints.js,modules/loader.js,sql/memCheckpoints.jsFeedback from node operators — especially mainnet copy latency at round boundaries and recovery times on larger chains — is welcome.
Beta Was this translation helpful? Give feedback.
All reactions