SOLR-18249: Stage shard backup metadata before publication - #4969
Open
nick-boss-tech wants to merge 6 commits into
Open
nick-boss-tech wants to merge 6 commits into
nick-boss-tech wants to merge 6 commits into
Conversation
LocalFileSystemRepository stages metadata in a sibling file and requests an ATOMIC_MOVE only. It no longer retries with a plain move when atomic publication is unsupported. After an I/O or runtime failure during staging or publication, it attempts to remove the temp file; cleanup failures are retained as suppressed exceptions on the original failure. This is not a portable atomic-replacement guarantee: Java NIO leaves replacement of an existing target provider-specific even when ATOMIC_MOVE is supported. The Javadocs now describe that limit and clarify that BackupRepository's createOutput-based default provides no generic atomicity or failure-preservation guarantee. The SOLR-18249 changelog describes staged publication without promising portable atomic replacement. The interface-default test now invokes BackupRepository's actual default method and records its createOutput call. A LocalFS publication-failure test injects AtomicMoveNotSupportedException and checks byte-for-byte preservation and readability of existing metadata plus temp-file cleanup. Verification: core spotlessJavaCheck passed; focused ShardBackupMetadataTest passed all 5 tests using single-source compilation to work around workspace inode pressure.
…le atomically The interface default writes directly through createOutput with no atomicity guarantee, so naming it writeAtomically over-promises. Rename to writeBytes and document that atomicity depends on the override. Pass REPLACE_EXISTING alongside ATOMIC_MOVE so repeated publication over an existing metadata file replaces it atomically instead of failing with FileAlreadyExistsException on platforms where an atomic rename does not replace.
…the base-compatible overwrite test
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.
🤖 AI text below 🤖 (posted on behalf of Nick Shanin)
https://issues.apache.org/jira/browse/SOLR-18249
What happens today
ShardBackupMetadata.storeoverwrites the previous metadata file in place: it deletes the existing file and then writes the new one throughcreateOutput. A crash or failure mid-write can destroy the only good copy of a shard's backup metadata.What this change does
The new metadata is serialized to a buffer and published in one step (
BackupRepository.writeBytes, overridden inLocalFileSystemRepository):ATOMIC_MOVEwithREPLACE_EXISTING); it no longer falls back to a non-atomic move, so an unsupported filesystem fails closed instead of silently losing atomicity;DelegatingBackupRepositoryforwardswriteBytes, so a wrapped local repository keeps the atomic path;DeleteBackupCmdnow skips directory names it cannot parse as shard metadata files, so a staged temp file left behind by a JVM death no longer makes backup deletion and retention throw.Proof
Verified at head c4cd38c: tidy, Error Prone compile, and the module check pass.
ShardBackupMetadataOverwriteTest(1 of 1): a recording repository shows an overwrite never deletes the previous metadata file. The class uses only the pre-change API and fails against base production code with "overwrite must not delete the previous metadata file" (fork test runner run 37238130723), because basestoredeletes the existing file before writing.ShardBackupMetadataTest(4 of 4): overwrite reads back, a failed overwrite keeps the previous bytes, an unsupported atomic move preserves the existing metadata and cleans the temp file, and the interface defaultwriteBytesroutes throughcreateOutput. In the failed-overwrite case the repository throws fromwriteBytesbefore the store code runs, so it pins the harness more than the production path. These cases do not compile against base code (they exercise thewriteBytesmethod this PR adds), so no fail-on-base proof is possible for them.DeleteBackupCmdTest(5 of 5): two new cases place a leftover.tmp.staging file in the shard metadata directory and show that delete and retention both complete; with onlyDeleteBackupCmd.javaswapped to base, both fail withIllegalArgumentExceptionfrom the unguarded filename parse.A choice to check
When the filesystem cannot move a file atomically, this PR fails the backup rather than completing it non-atomically. The alternative is a logged fallback to a non-atomic move, which keeps backups running on such filesystems but silently gives up the guarantee this change exists to provide. Failing was chosen; please say if you would rather see the logged fallback.
Separately, the deletion skip logs at DEBUG only; whether it should be WARN is left open.
Limits
writeBytes(viacreateOutput) makes no atomicity guarantee; only repositories whose backing store supports it overridewriteBytesto stage the bytes and publish them atomically.storeno longer deletes before writing, so a third-partyBackupRepositorywhosecreateOutputrefuses to overwrite an existing object worked on base and fails with this change. The bundled S3 and GCS repositories overwrite in place and are unaffected..bakcopy without the.jsonsuffix, a.jsonname with the wrong prefix or too few parts, a non-numeric id), logging each skip at DEBUG only. On base each such name stopped the delete or retention operation before anything was deleted, so a genuinely damaged directory now gets a quiet partial cleanup where base failed loudly. Names the backup store writes always parse, so referenced backups are unaffected. Deletion does not remove a skipped file; a purge deletes it as an unknown file.Changelog:
changelog/unreleased/SOLR-18249.yml(fixed)AI assistance
AI agents assisted with research, implementation, review, and drafting. Nick Shanin directed the work and takes responsibility for this contribution.