mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-03 18:47:41 +01:00
* fix(storage): update deletion metrics in SortedFileNeedleMap.Delete (#12363) * storage: test canDelete volume counters across restarts A canDelete volume reloaded from .sdx must count its deletes so volume.deleteEmpty and vacuum see it drain, and the counters it reports must match what the next reload computes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Chris Lu <chrislusf@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
3 files changed
+59
No files matched your search
@@ -127,6 +127,9 @@ func (m *SortedFileNeedleMap) Delete(key NeedleId, offset Offset) error {
|
||||
return err
|
||||
}
|
||||
_, _, err = erasure_coding.SearchNeedleFromSortedIndex(f.file, m.dbFileSize, key, erasure_coding.MarkNeedleDeleted)
|
||||
if err == nil {
|
||||
m.logDelete(size)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -65,6 +65,21 @@ func TestSortedFileNeedleMap_DeleteAppendsTombstone(t *testing.T) {
|
||||
t.Fatalf("Delete: %v", err)
|
||||
}
|
||||
|
||||
if got, want := m.DeletedCount(), 1; got != want {
|
||||
t.Fatalf("DeletedCount after delete: got %d, want %d", got, want)
|
||||
}
|
||||
if got, want := m.DeletedSize(), uint64(1024); got != want {
|
||||
t.Fatalf("DeletedSize after delete: got %d, want %d", got, want)
|
||||
}
|
||||
|
||||
// Idempotent delete must not double count deletions.
|
||||
if err := m.Delete(deletedKey, deletedOff); err != nil {
|
||||
t.Fatalf("idempotent Delete: %v", err)
|
||||
}
|
||||
if got, want := m.DeletedCount(), 1; got != want {
|
||||
t.Fatalf("DeletedCount after idempotent delete: got %d, want %d", got, want)
|
||||
}
|
||||
|
||||
if got, want := fileSize(t, idxPath), wantIdxSize+int64(NeedleMapEntrySize); got != want {
|
||||
t.Fatalf("idx size after delete: got %d, want %d (front-overwrite regression)", got, want)
|
||||
}
|
||||
|
||||
@@ -152,3 +152,44 @@ func TestMountVolumeAnnouncesReadOnlyState(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// After a restart a canDelete volume is served from .sdx; its deletes must
|
||||
// still move the counters volume.deleteEmpty and vacuum read, and land them
|
||||
// where the next reload would.
|
||||
func TestMarkVolumeReadonlyCanDelete_CountsDeletesAfterRestart(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
store := newSingleDirStore(t, dir)
|
||||
const vid = needle.VolumeId(13)
|
||||
require.NoError(t, store.AddVolume(vid, "", NeedleMapInMemory, "000", "", 0, needle.GetCurrentVersion(), 0, types.HardDriveType, 0))
|
||||
|
||||
needles := []*needle.Needle{newRandomNeedle(1), newRandomNeedle(2), newRandomNeedle(3)}
|
||||
for _, n := range needles {
|
||||
_, err := store.WriteVolumeNeedle(vid, n, true, false)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, store.MarkVolumeReadonly(vid, true, true))
|
||||
store.Close()
|
||||
|
||||
store2 := newSingleDirStore(t, dir)
|
||||
v := store2.GetVolume(vid)
|
||||
require.NotNil(t, v)
|
||||
require.IsType(t, &SortedFileNeedleMap{}, v.nm)
|
||||
for _, n := range needles {
|
||||
_, err := store2.DeleteVolumeNeedle(vid, n)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
counters := func(v *Volume) [4]uint64 {
|
||||
return [4]uint64{v.FileCount(), v.DeletedCount(), v.ContentSize(), v.DeletedSize()}
|
||||
}
|
||||
afterDeletes := counters(v)
|
||||
require.Equal(t, uint64(len(needles)), afterDeletes[0])
|
||||
require.Equal(t, afterDeletes[0], afterDeletes[1], "an emptied volume must report every file deleted")
|
||||
require.Equal(t, afterDeletes[2], afterDeletes[3])
|
||||
store2.Close()
|
||||
|
||||
store3 := newSingleDirStore(t, dir)
|
||||
v3 := store3.GetVolume(vid)
|
||||
require.NotNil(t, v3)
|
||||
require.Equal(t, afterDeletes, counters(v3))
|
||||
store3.Close()
|
||||
}
|
||||
Reference in new issue
Block a user