Skip to content

fix(mirror): skip recopies when source ETag is preserved in metadata - #351

Merged
cxymds merged 5 commits into
mainfrom
cursor/fix-mirror-incremental-identity-fce3
Aug 25, 2026
Merged

fix(mirror): skip recopies when source ETag is preserved in metadata#351
cxymds merged 5 commits into
mainfrom
cursor/fix-mirror-incremental-identity-fce3

Conversation

@loverustfs

@loverustfs loverustfs commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Related issue

Fixes #342

Background

rc mirror copies S3-to-S3 objects through a download-then-upload path. Multipart completion on the destination stores a different ETag than the source (hash-2 vs hash-1 in the report). The planner compared ListObjects ETags, so a second --overwrite run recopied every object even when the bytes were unchanged.

mc cp --preserve keeps the source ETag via server-side copy. Cross-alias mirror cannot do that, and ListObjects does not return user metadata, so ETag-only planning cannot see a client-recorded identity.

Solution

  • Remote uploads that have a source ETag now write x-amz-meta-rc-source-etag.
  • --compare auto|etag|size (default auto):
    • auto: skip when ETags match, or when size matches and destination metadata records the source ETag
    • etag: skip only on identical stored ETags
    • size: skip when sizes match
  • In auto mode, destinations with the same size but a different listed ETag are inspected with HeadObject so the identity metadata can be read.
  • Destination race/removal checks ignore identity metadata so List vs HEAD differences do not look like a concurrent change.

This is additive. Existing flags and default skip-on-matching-ETag behavior are unchanged. Objects copied before this change still recopy once; after that they skip.

This PR must be marked BREAKING because docs/reference/rc/mirror.md is a protected CLI behavior contract. The change is additive; no JSON schema or config schema_version bump applies.

Tests

  • Unit: Auto/ETag/Size skip rules, missing or mismatched identity recopies, size mismatch never skips, case-insensitive identity metadata, HeadObject lookup gating, identity ignored in race checks, upload records the source ETag
  • Integration: dry-run remote-to-remote Auto skip after HEAD, ETag mode recopies without HEAD, Auto recopies when identity is missing or wrong, size mismatch recopies without HEAD
  • Help contract includes --compare
  • Regression: scripts/regression/mirror-identity.sh

Validation

cargo fmt --all
cargo clippy --workspace -- -D warnings
cargo test --workspace
./scripts/regression/mirror-identity.sh
Open in Web Open in Cursor 

cursoragent and others added 3 commits August 24, 2026 16:02
S3-to-S3 mirror uploads through a temporary file, so multipart
completion stores a different ETag than the source. Auto compare now
records the source ETag in x-amz-meta-rc-source-etag and HeadObject
fills that identity when listed ETags differ, so unchanged objects are
skipped on the next run.

Co-authored-by: RustFS <hello@rustfs.com>
…pare

Auto compare recopies when destination identity is absent or wrong,
and size mismatches skip HeadObject. Mark the protected mirror
contract with a BREAKING migration note for the additive change.

Co-authored-by: RustFS <hello@rustfs.com>
The previous synchronize event ran before the PR body included
BREAKING. This empty commit re-runs CI against the updated description.

Co-authored-by: RustFS <hello@rustfs.com>
Comment thread crates/cli/src/commands/mirror.rs Outdated
@cxymds
cxymds merged commit c0ef89e into main Aug 25, 2026
17 checks passed
@cxymds
cxymds deleted the cursor/fix-mirror-incremental-identity-fce3 branch August 25, 2026 06:08
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.

rc mirror S3-to-S3 does not preserve ETag, causing unchanged objects to be copied again

3 participants