Skip to content

node: Avoid duplicate key constraints when changing chain shard#6199

Open
erayack wants to merge 4 commits intographprotocol:masterfrom
erayack:fix-chain-shard-duplicate-key-constraint
Open

node: Avoid duplicate key constraints when changing chain shard#6199
erayack wants to merge 4 commits intographprotocol:masterfrom
erayack:fix-chain-shard-duplicate-key-constraint

Conversation

@erayack
Copy link
Copy Markdown

@erayack erayack commented Oct 16, 2025

Description

This PR fixes issue #6196 by implementing a robust backup naming system that prevents duplicate key constraint violations when reverting chain shard changes.

Problem

When changing a chain's shard using graphman chain change-shard, the existing chain is renamed to <chain>-old. However, if a subsequent attempt is made to revert the chain's shard back to the original shard, the operation fails due to a unique constraint violation because <chain>-old already exists in the database.

Solution

  • Robust backup naming: Implemented next_backup_name() function that generates unique backup names by appending numeric suffixes when conflicts exist
  • Backup reuse optimization: When reverting to the original shard, the system now reuses the existing backup instead of creating a new one
  • Backup preservation: Previous backups are preserved with unique names to maintain data integrity
  • Logging: Added logging to track backup operations and outcomes

Changes

  • Added ChainSwapOutcome struct to track the results of chain swap operations
  • Implemented next_backup_name() function for conflict-free backup naming
  • Enhanced change_block_cache_shard() function with robust backup handling logic
  • Added detailed logging for backup operations

Testing

The fix handles the following scenarios:

  1. Initial shard change (creates <chain>-old)
  2. Reverting to original shard (reuses existing backup)
  3. Multiple shard changes (preserves all backups with unique names)

Fixes #6196

@github-actions
Copy link
Copy Markdown

This pull request hasn't had any activity for the last 90 days. If there's no more activity over the course of the next 14 days, it will automatically be closed.

@github-actions github-actions Bot added the Stale label Jan 15, 2026
@github-actions github-actions Bot closed this Jan 29, 2026
@lutter
Copy link
Copy Markdown
Collaborator

lutter commented Jan 29, 2026

We really need to review this, this shouldn't have been closed

@lutter lutter reopened this Jan 29, 2026
@github-actions github-actions Bot removed the Stale label Jan 30, 2026
@erayack erayack force-pushed the fix-chain-shard-duplicate-key-constraint branch 3 times, most recently from ed4e528 to c5439e8 Compare April 26, 2026 09:33
@erayack
Copy link
Copy Markdown
Author

erayack commented Apr 26, 2026

Hi @lutter, I rebased PR #6199 onto current master and resolved the conflict in node/src/manager/commands/chain.rs.

The PR is now mergeable. I kept the original fix intent, but ported it to the current async chain-store code, preserved the safe chain-store creation ordering, simplified the backup-name selection flow, and added focused tests for backup-name formatting.

@erayack erayack changed the title Fix duplicate key constraint when reverting chain shard changes fix: duplicate key constraint when reverting chain shard changes Apr 26, 2026
@erayack erayack changed the title fix: duplicate key constraint when reverting chain shard changes node: Avoid duplicate key constraints when changing chain shard Apr 26, 2026
Comment thread node/src/manager/commands/chain.rs Outdated
@erayack
Copy link
Copy Markdown
Author

erayack commented Apr 29, 2026

Implemented the behavior you suggested. @dimitrovmaksim

Current flow is now:

  • if {chain}-old exists on a different shard, the command aborts and tells the user to remove it explicitly with graphman chain remove
  • if {chain}-old exists on the target shard, the command prompts before reusing it
  • otherwise it keeps the current cache as {chain}-old and creates a fresh cache on the target shard

I also changed the reuse ordering to avoid the unique constraint issue: {chain}-old -> temp, {chain} -> {chain}-old, temp -> {chain}

I added focused unit tests for the boundary decision logic.

Comment thread node/src/manager/commands/chain.rs Outdated
Comment thread node/src/manager/commands/chain.rs
Comment thread node/src/manager/commands/chain.rs Outdated
Comment thread node/src/manager/commands/chain.rs Outdated
Comment thread node/src/manager/commands/chain.rs Outdated
@erayack erayack force-pushed the fix-chain-shard-duplicate-key-constraint branch from 334940b to 704e10d Compare May 1, 2026 22:04
@erayack
Copy link
Copy Markdown
Author

erayack commented May 4, 2026

Hi @dimitrovmaksim again. Force-pushed 334940b704e10d addressing the latest round:

  • Removed redundant transaction outcome plumbing (ChainSwapOutcome) and made the transaction return ().
  • Inlined the backup disposition control flow and removed the extra enum/helper layering.
  • Added chain-identifier safety checks for backup reuse ({chain}-old must match the active chain identifier), switched transient rename to {chain}-old-temp, and removed the debug_assert!/unwrap() path.

Tests still pass locally. Ready for another look when you have a moment.

Comment thread node/src/manager/commands/chain.rs Outdated
);
}

if allocated_chain.is_some() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: Same as the allocated_chain = ... comment. I think this print is redundant.

Comment thread node/src/manager/commands/chain.rs Outdated
"Changed block cache shard for {} from {} to {}",
chain_name, old_shard, shard
);
println!("Latest backup recorded as `{}`", canonical_backup_name);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: I would remove this println!, seems like a left-over from the sufixed backup names

Comment thread node/src/manager/commands/chain.rs Outdated
};

let chain = BlockStore::allocate_chain(conn, &chain_name, &shard, &ident).await?;
let allocated_chain = if reuse_existing_backup {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: allocated_chain could be boolean, as it is used only for the conditional printing here https://github.com/erayack/graph-node/blob/8b649af8184d6a83e730b4ddb74289c07d3c5cd8/node/src/manager/commands/chain.rs#L380-L385. Tbf I think this message may be redundant as we already notify the user that the chain has been moved to another shard. Personally i would remove that print and change this to

if !reuse_existing_backup {
    let chain = BlockStore::allocate_chain(&mut conn, &chain_name, &target_shard, &ident).await?;
    store.add_chain_store(&chain, true).await?;
}

Comment thread node/src/manager/commands/chain.rs Outdated
.ok_or_else(|| anyhow!("unknown chain: {}", &chain_name))?;
let new_name = format!("{}-old", &chain_name);
let ident = chain_store.chain_identifier().await?;
let target_shard = Shard::new(shard.clone())?;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: You can drop the clone() here and let Shard::new consume the String, then you can use target_shard directly later as print arguments instead of shard

@dimitrovmaksim
Copy link
Copy Markdown
Member

Hey @erayack sorry for the delay, I added couple of nit comment. I'll ask another team member to take a look before approving, just to be sure I'm not missing something.

Btw could you drop the merge commit and instead rebase your branch ontop of master? 🙏🏻

erayack added 4 commits May 6, 2026 22:14
- Implement robust backup naming system to avoid conflicts
- Add support for reusing existing backups when appropriate
- Preserve previous backups with unique names
- Add comprehensive logging for backup operations

Fixes graphprotocol#6196
@erayack erayack force-pushed the fix-chain-shard-duplicate-key-constraint branch from 8b649af to d8ba7e4 Compare May 6, 2026 19:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Cannot Revert Chain Shard Change Due to Duplicate Key Constraint

3 participants