mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-11 00:50:43 +02:00
master
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ed9d58873e |
filer.remote.sync: skip an upload whose source entry was deleted or rewritten (#11149)
* filer.remote.sync: skip an upload whose source entry was deleted or rewritten A replay from an earlier offset (-timeAgo) re-emits create and update events for entries the filer has since deleted or rewritten. Their chunks are gone from the volume servers, so the upload can never succeed, and failing the event holds the sync offset before it: every restart of the subscription replays it into the same dead chunks, and progress on everything after it in the log is never persisted. One such entry stops replication for the whole mount. When the upload fails, look the entry up on the filer. Gone, or holding other content than the event described, the event is superseded and is skipped with an error log; the event that superseded it follows in the log and brings the remote to the current state. Otherwise the failure stands and the event is retried as before. Fixes #11148 * filer.remote.sync: compare chunks by file id when deciding an event is superseded filer.IsSameData compares chunk ETags, so a delete-and-recreate of identical bytes, which stores the same content under new file ids and drops the old ones, looked still as described and kept failing the event on its dead chunks. Compare by file id with DoMinusChunks, the way the filer itself decides which chunks an update leaves for deletion: the event is superseded when the current entry no longer references every chunk it named, and still as described when it does, including when more chunks were appended after it. * filer.remote.sync: ask the filer on the first failed upload attempt, not after the backoff The superseded check ran after util.Retry had given up, so every dead entry still cost the full retry cycle, about 13s, before it was skipped: the SDK reports a missing chunk as "RequestError", which IsTransientError takes as worth retrying. Move the check into the retry loop with util.RetryOnError. Any failed attempt asks the filer, and the loop stops at once when the entry is gone, surfacing errSuperseded for the caller to skip. An entry the filer still holds keeps the retry policy it had. filer.remote.gateway shares retriedWriteFile and the same offset-pinning processor, so its three call sites skip a superseded event the same way. |
||
|
|
9fef11526e |
filer.remote.sync: confirm a missing RemoteEntry against the filer before re-uploading (#11146)
* filer.remote.sync: confirm a missing RemoteEntry against the filer before re-uploading The event is the entry as it was when the update was logged. A chmod or utimes right after a write is logged while the sync is still uploading the write, so it carries no RemoteEntry even though the object is on the remote by the time it is processed. Gating on the event alone turned every such update into a delete and a second upload of the same bytes; cp -p, rsync and Django's FileSystemStorage all write that way. Look up the filer's current entry when the event has no RemoteEntry: the upload stamps it as soon as it completes, so the stamp is there for the race and absent for a file that was never replicated. Skip the update when the entry has since been deleted rather than upload from chunks that may be gone; the delete event that follows removes the remote object. Tests build entries from chunks, which is what IsSameData compares in production, and cover both no-RemoteEntry cases through a stub filer. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer.remote.sync: do not delete the remote object before overwriting it in place The update write path deleted the old object and then wrote the new one, even when both are the same key. S3, GCS and Azure all overwrite on write, so the delete bought nothing and left the remote with no object between the two calls, or at all if the write then failed and pinned the offset. On a versioned remote bucket it also left a delete marker per rewrite. Delete only when the key changes, which is what the delete was for. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * filer.remote.sync: trim comments Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> |
||
|
|
c9c6e6fb1d |
filer.remote.sync: upload files that were never replicated (#11140)
An entry whose content is rewritten unchanged before it first reached the remote took the metadata-only branch, and UpdateFileMetadata returns early when the extended attributes match without checking that the object is there. shouldSendToRemote had already reported the entry as needing to be sent, so the effect was that it stayed local for as long as its content did not change, with the sync reporting healthy progress over it. Require RemoteEntry to be set before treating an update as metadata-only. Gating at the caller covers the S3, GCS and Azure clients, which share the same early return. Fixes #11139 |
||
|
|
51ec0d2122 |
fix(remote_gateway): prevent double-versioning when syncing to versioned central bucket (#8710)
* fix(remote_gateway): prevent double-versioning when syncing to versioned central bucket
When a file is uploaded to a versioned bucket on edge, SeaweedFS stores
it internally as {object}.versions/v_{versionId}. The remote_gateway was
syncing this internal path directly to the central S3 endpoint. When
central's bucket also has versioning enabled, this caused central to
apply its own versioning on top, producing corrupt paths like:
object.versions/v_{edgeId}.versions/v_{centralId}
Fix: rewrite internal .versions/v_{id} paths to the original S3 object
key before uploading to the remote. Skip version file delete/update
events that are internal bookkeeping.
Fixes https://github.com/seaweedfs/seaweedfs/discussions/8481#discussioncomment-16209342
* fix(remote_gateway): propagate delete markers to remote as deletions
Delete markers are zero-content version entries (ExtDeleteMarkerKey=true)
created by S3 DELETE on a versioned bucket. Previously they were silently
dropped by the HasData() filter, so deletions on edge never reached
central.
Now: detect delete markers before the HasData check, rewrite the
.versions path to the original S3 key, and issue client.DeleteFile()
on the remote.
* fix(remote_gateway): tighten isVersionedPath to avoid false positives
Address PR review feedback:
- Add isDir parameter to isVersionedPath so it only matches the exact
internal shapes: directories whose name ends with .versions (isDir=true),
and files with the v_ prefix inside a .versions parent (isDir=false).
Previously the function was too broad and could match user-created paths
like "my.versions/data.txt".
- Update all 4 call sites to pass the entry's IsDirectory field.
- Rename TestVersionedDirectoryNotFilteredByHasData to
TestVersionsDirectoryFilteredByHasData so the name reflects the
actual assertion (directories ARE filtered by HasData).
- Expand TestIsVersionedPath with isDir cases and false-positive checks.
* fix(remote_gateway): persist sync marker after delete-marker propagation
The delete-marker branch was calling client.DeleteFile() and returning
without updating the local entry, making event replay re-issue the
remote delete. Now call updateLocalEntry after a successful DeleteFile
to stamp the delete-marker entry with a RemoteEntry, matching the
pattern used by the normal create path.
* refactor(remote_gateway): extract syncDeleteMarker and fix root path edge case
- Extract syncDeleteMarker() shared helper used by both bucketed and
mounted-dir event processors, replacing the duplicated delete + persist
local marker logic.
- Fix rewriteVersionedSourcePath for root-level objects: when lastSlash
is 0 (e.g. "/file.xml.versions"), return "/" as the parent dir instead
of an empty string.
- The strings.Contains(dir, ".versions/") condition flagged in review was
already removed in a prior commit that tightened isVersionedPath.
* fix(remote_gateway): skip updateLocalEntry for versioned path rewrites
After rewriting a .versions/v_{id} path to the logical S3 key and
uploading, the code was calling updateLocalEntry on the original v_*
entry, stamping it with a RemoteEntry for the logical key. This is
semantically wrong: the logical object has no filer entry in versioned
buckets, and the internal v_* entry should not carry a RemoteEntry for
a different path.
Skip updateLocalEntry when the path was rewritten from a versioned
source. Replay safety is preserved because S3 PutObject is idempotent.
* fix(remote_gateway): scope versioning checks to /buckets/ namespace
isVersionedPath and rewriteVersionedSourcePath could wrongly match
paths in non-bucket mounts (e.g. /mnt/remote/file.xml.versions).
Add the same /buckets/ prefix guard used by isMultipartUploadDir so
the .versions / v_ logic only applies within the bucket namespace.
|