Repository navigation
fix(core): reset core state when the stored chain differs from genesis - #598
Merged
Merged
Conversation
A node moving to a new chain (the genesis rollover) restarts with the new image and an empty CometBFT directory for the new chain ID, but its PostgreSQL still holds the old chain's core state. Before #591 the genesis-generation path ran core's migrations down on that start, which emptied core_blocks and let the node state sync. #591 removed that reset, because it also fired when a restore brought PostgreSQL back without its CometBFT files, and added checkCometDataFiles to refuse that case. A migrating node looks the same to that check, so it now stops core with "PostgreSQL contains block N but CometBFT database is missing" instead of syncing the new chain. Tell the two apart by the chain ID stored on the latest block. When it names another chain, clear core's consensus state before the existing checks: the state-sync snapshot table set, minus core_db_migrations, with the same no-CASCADE TRUNCATE the restore uses, so mediorum and ETL tables are untouched. The node then starts unsynced and state syncs. A restore of this chain's own state still carries this chain's ID and is still refused by checkCometDataFiles, and an empty chain_id is never treated as foreign. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A validator node moving to
audius-mainnet-betachanges its image tag and restarts. The new image's genesis names a new chain ID, so CometBFT starts from an empty data directory, but the node's PostgreSQL still holds the old chain's core state (core_blocksat ~34M onaudius-mainnet-alpha-beta).Before #591, the genesis-generation path in
SetupNodesetRunDownMigrationwhenever it had to write a genesis file, and startup ran core's migrations down. That emptiedcore_blocks, so the node state-synced onto the new chain. Runbook step 10 ("operators set an explicit image tag") relies on this.#591 removed that reset, correctly for the case it targeted: a restore that brought PostgreSQL back without its CometBFT files was wiping its own database. It added
checkCometDataFilesto refuse that case instead. But a migrating node looks the same to that check, so on currentmainit stops core with:Even without the check,
Infowould report the old chain's height and app hash, and the handshake would refuse it.Fix
Tell a chain change from a restore by the chain ID stored on the latest block. Every row in
core_blocksrecords the chain that produced it.checkCometDataFiles.chain_id: not a positive identification, so never reset.What gets cleared is the state-sync snapshot table set (
stateSyncSnapshotTables), minuscore_db_migrations, using the same no-CASCADETRUNCATEthe restore uses (#551). That is all of core's consensus state (TestSnapshotTablesCoverCoreSchemaalready enforces that everycore_*table is either in the set or explicitly exempt), so a node that block-syncs instead of state-syncing doesn't replay the new chain over the old chain's validators and auth state. Mediorum and ETL tables are untouched.core_db_migrationsis kept because, unlike a restore, nothing reloads it, and clearing it would make the next startup re-run every migration.This does not reinstate running migrations down. On a populated old-chain database some down steps can fail (e.g.
00019re-addsUNIQUE (cid)onsound_recordings), and a truncate of the known set does the same job without that risk.Tests
TestIsForeignChainBlock: previous chain is foreign; same chain and emptychain_idare not.TestForeignChainResetStmtClearsCoreConsensusState: every snapshot table exceptcore_db_migrationsis cleared.TestForeignChainResetStmtKeepsMigrationsAndNonCoreTables:core_db_migrations, mediorum and ETL tables are not named, and there is noCASCADE.make test-unitpasses, including #591'sTestCheckCometDataFilesand the state-sync truncate tests.Rollout
Needs to be in the new-network build (#553) before runbook step 10. A migrating node also needs a snapshot to sync from: the bootstrap's first is at height 20,000 with
BlockInterval=20000, andProdStateSyncRpcsmust point at nodes serving snapshots.🤖 Generated with Claude Code