mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-14 07:48:41 +01:00
mount: refuse to merge a whole-file rewrite over a peer's chunk change
maybeMergeChunks re-uploads the handle's whole view as unstamped chunks. Merged after a peer's change, they tie with a peer's own rewrite or bring back a range the peer truncated, so that flush fails as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3fc45d9f83
commit
8499d787cd
2 files changed
+20
-4
No files matched your search
@@ -308,6 +308,15 @@ func TestMergeContentEntry(t *testing.T) {
|
||||
t.Fatal("merged into a replaced file")
|
||||
}
|
||||
|
||||
rewritten := proto.Clone(base).(*filer_pb.Entry)
|
||||
rewritten.Chunks = []*filer_pb.FileChunk{{Fid: &filer_pb.FileId{VolumeId: 1, FileKey: 8, Cookie: 1}, Size: 2}}
|
||||
if _, ok := mergeContentEntry(stored, base, rewritten, rewritten); ok {
|
||||
t.Fatal("merged a stale whole-file rewrite over a peer append")
|
||||
}
|
||||
if _, ok := mergeContentEntry(proto.Clone(base).(*filer_pb.Entry), base, rewritten, rewritten); !ok {
|
||||
t.Fatal("refused a rewrite with no peer chunk change")
|
||||
}
|
||||
|
||||
empty := flushConditionEntry()
|
||||
inline := proto.Clone(empty).(*filer_pb.Entry)
|
||||
inline.Content = []byte("peer")
|
||||
|
||||
@@ -373,9 +373,10 @@ const maxContentFlushMerges = 3
|
||||
// content it changed, taking those values from sent (local in stored's id
|
||||
// space). Everything else keeps the stored value. A local truncation keeps
|
||||
// its size and clips stored chunks to it; otherwise the larger size wins.
|
||||
// A different file at the path, inline content changed on both sides, or
|
||||
// inline content next to chunks (reads stop at the inline bytes) cannot be
|
||||
// merged.
|
||||
// A different file at the path, inline content changed on both sides,
|
||||
// inline content next to chunks (reads stop at the inline bytes), or a local
|
||||
// whole-file rewrite (maybeMergeChunks, unstamped chunks) over a peer's chunk
|
||||
// change cannot be merged: the rewrite carries the handle's stale view.
|
||||
func mergeContentEntry(stored, base, local, sent *filer_pb.Entry) (*filer_pb.Entry, bool) {
|
||||
if stored == nil || base == nil || stored.IsDirectory || !bytes.Equal(stored.HardLinkId, base.HardLinkId) {
|
||||
return nil, false
|
||||
@@ -400,14 +401,20 @@ func mergeContentEntry(stored, base, local, sent *filer_pb.Entry) (*filer_pb.Ent
|
||||
for _, c := range local.Chunks {
|
||||
inLocal[contentChunkKey(c)] = true
|
||||
}
|
||||
peerChanged := len(stored.Chunks) != len(base.Chunks)
|
||||
merged.Chunks = merged.Chunks[:0]
|
||||
for _, c := range stored.Chunks {
|
||||
if key := contentChunkKey(c); !inBase[key] || inLocal[key] {
|
||||
key := contentChunkKey(c)
|
||||
peerChanged = peerChanged || !inBase[key]
|
||||
if !inBase[key] || inLocal[key] {
|
||||
merged.Chunks = append(merged.Chunks, proto.Clone(c).(*filer_pb.FileChunk))
|
||||
}
|
||||
}
|
||||
for _, c := range local.Chunks {
|
||||
if !inBase[contentChunkKey(c)] {
|
||||
if peerChanged && c.ModifiedTsNs == 0 && !c.IsChunkManifest {
|
||||
return nil, false
|
||||
}
|
||||
merged.Chunks = append(merged.Chunks, proto.Clone(c).(*filer_pb.FileChunk))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user