Skip to content

worker: skip objects deleted from the source during copy - #279

Closed
den-dw wants to merge 1 commit into
clyso:mainfrom
den-dw:fix/copy-source-deleted
Closed

den-dw wants to merge 1 commit into
clyso:mainfrom
den-dw:fix/copy-source-deleted

Conversation

@den-dw

@den-dw den-dw commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Problem

CopyObject stats the source object, then reads its content with GetObject. A short-lived object can be deleted in between. We see it all the time with a container registry (distribution) behind the proxy: blob uploads go to _uploads/<id>/data and _uploads/<id>/hashstates/..., which the registry deletes seconds later.

The read then fails with NoSuchKey:

process task failed. task will be retried  error="unable to stat from object: The specified key does not exist."
process task failed. task will be retried  error="unable to upload object: The specified key does not exist."

The retry stats the object, finds nothing and skips it (object sync: skip object sync: object missing in source). Nothing is lost, but every such race is counted in worker_failed_tasks_total, so an alert on that metric fires on a healthy replication.

Fix

If reading or uploading the content fails, CopyObject stats the source object again. If the object is gone, it returns dom.ErrNotFound, the same as when the first stat finds nothing, and objectSync skips the object without a retry. Other errors are returned as before.

The second stat checks the source object directly: a NoSuchKey from PutObject alone doesn't tell whether reading the source or writing the destination failed.

Testing

go build ./..., go vet ./service/worker/..., go test ./service/worker/....

I didn't add an e2e test: the object would have to be deleted exactly between the stat and the read, and the e2e environment has no hook for that. We're rolling this change out in production on top of v0.7.10, where a GitLab container registry is replicated between two S3 providers.

CopyObject stats the source object, then reads it with GetObject. A
short-lived object (e.g. a container registry upload under _uploads/) can
be deleted in between: the read fails with NoSuchKey, the task fails and is
retried, and only the retry sees the object missing and skips it. Each such
race shows up as a failed task in worker_failed_tasks_total.

When reading or uploading the content fails, check whether the source
object still exists and return dom.ErrNotFound if it does not, the same as
when the first stat finds no object.

Signed-off-by: Denis Ivanov <dendw.work@gmail.com>
@arttor

arttor commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

will be fixed in #285

@arttor arttor closed this Oct 27, 2026
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.

2 participants