fix(cloud-backups): audit-rotate rename-aside collides across coexisting archives - #6
Merged
Merged
Conversation
…ing archives The rotate step renames the archived table's constraints/indexes aside so the fresh CREATE ... LIKE can reclaim the canonical names (audit_pkey, ..._key). The rename target used a suffix derived from the ORIGINAL (constant) name -- `left(conname,55) || '_' || substr(md5(conname),1,7)` -- which is identical on every rotation (each fresh table's PK is again `audit_pkey`). When two archives coexist (a --no-drop staging re-run, or a leftover that couldn't be dropped / was quarantined), the second rotation tries to rename its `audit_pkey` to a name the first archive's constraint already holds -> `relation "audit_pkey_eb1c437" already exists`, and rotation fails every run until the backlog is dropped. Derive the suffix from the UNIQUE archive name instead (`substr(md5(archive),1,8)`), so each archive's renamed constraints/indexes are schema-unique. The renamed names are throwaway (the archive is dropped later); only uniqueness matters. left(name,54) +'_'+8 stays within the 63-byte identifier limit. Validated: two consecutive --no-drop runs against Postgres+MinIO now coexist as audit + two archives (audit_pkey_3120682c / audit_pkey_55579c74), where the old code failed the second rotation. Unit test asserts the suffix derives from the archive name and differs across archives. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> ReARM-Agent: 1420896f-adf5-4843-896f-d863cfcc6528 ReARM-Agentic-Session: 1403ccf5-4b2a-4718-850a-3a42d188532d
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.
The bug
rotateSQLrenames the archived table's constraints/indexes aside so the freshCREATE ... LIKEcan reclaim the canonical names. The rename target derived its suffix from the original (constant) constraint/index name:Every rotation starts from a fresh table whose PK is again
audit_pkey, so the suffix is identical each run. When two archives coexist — a--no-dropstaging re-run, or a leftover that couldn't be dropped / was quarantined — the second rotation tries to rename itsaudit_pkeyto a name the first archive's constraint already holds:Rotation then fails every run until the backlog is manually dropped. This defeats the recovery mechanism, whose whole job is to tolerate leftover archives. Hit live on the demo instance during a
--no-dropre-run.Not data-affecting:
rotateSQLis a singleBEGIN…COMMIT, so the failed rotation rolls back cleanly — the liveaudittable is untouched (fail-safe worked).The fix
Derive the rename suffix from the unique archive name (
substr(md5(archive),1,8)) instead of the constant original name. Each archive's renamed constraints/indexes are then schema-unique. The renamed names are throwaway (the archive is dropped later); only uniqueness matters.left(name,54)+'_'+8stays within the 63-byte identifier limit.Validation
Two consecutive
--no-dropruns against Postgres + MinIO now coexist:The old code failed the second rotation with the collision above. Unit test asserts the suffix derives from the archive name and differs across archives. build / test / gofmt clean.
🤖 Generated with Claude Code