mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-12 17:40:43 +02:00
* fix(storage): delete and unmount every copy of a duplicate volume id NewStore has no cross-disk duplicate guard (unlike the Rust volume server, which refuses to start in that state), so a stale twin of a volume id can mount on a second disk after a disk repair. DeleteVolume and UnmountVolume returned after the first matching disk, leaving the twin to survive and re-register as the volume's content. Walk every disk and act on all copies, emitting one heartbeat delta per copy. * fix(storage): surface partial delete/unmount failures across duplicate copies Address review: if removing one copy of a duplicate volume id fails with a real error (disk IO, permissions), the loop logged it and could still return success once another copy was removed -- leaving the stale copy to re-register, the exact divergence this guards against. DeleteVolume and UnmountVolume now accumulate such errors and return them (still attempting every disk), so a copy left behind is never reported as success. Add a DeleteVolume duplicate-copies regression test.
52 lines
1.9 KiB
Go
52 lines
1.9 KiB
Go
package storage
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/needle"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/super_block"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// A volume id can end up mounted on more than one disk of a server (a stale twin
|
|
// re-attached after a disk repair, since NewStore has no cross-disk duplicate
|
|
// guard). UnmountVolume must remove EVERY copy, not just the first match, or the
|
|
// stale twin survives and re-registers as the volume's content on the next
|
|
// heartbeat.
|
|
func TestUnmountVolumeRemovesAllDuplicateCopies(t *testing.T) {
|
|
store := newTestStore(t, 2)
|
|
const vid = needle.VolumeId(4242)
|
|
|
|
store.Locations[0].SetVolume(vid, createTestVolume(vid, false))
|
|
store.Locations[1].SetVolume(vid, createTestVolume(vid, false))
|
|
|
|
require.NoError(t, store.UnmountVolume(vid))
|
|
|
|
_, found0 := store.Locations[0].FindVolume(vid)
|
|
_, found1 := store.Locations[1].FindVolume(vid)
|
|
require.False(t, found0, "copy on disk 0 must be unmounted")
|
|
require.False(t, found1, "the stale twin on disk 1 must also be unmounted")
|
|
}
|
|
|
|
// DeleteVolume must likewise destroy every copy of a duplicate volume id, not just
|
|
// the first match, so the stale twin cannot survive the delete.
|
|
func TestDeleteVolumeRemovesAllDuplicateCopies(t *testing.T) {
|
|
store := newTestStore(t, 2)
|
|
const vid = needle.VolumeId(4243)
|
|
|
|
// Real volumes (not stubs) so Destroy can close and unlink them cleanly.
|
|
for _, loc := range store.Locations {
|
|
v, err := NewVolume(loc.Directory, loc.IdxDirectory, "", vid, NeedleMapInMemory,
|
|
&super_block.ReplicaPlacement{}, &needle.TTL{}, 0, needle.GetCurrentVersion(), 0, 0)
|
|
require.NoError(t, err)
|
|
loc.SetVolume(vid, v)
|
|
}
|
|
|
|
require.NoError(t, store.DeleteVolume(vid, false, false))
|
|
|
|
_, found0 := store.Locations[0].FindVolume(vid)
|
|
_, found1 := store.Locations[1].FindVolume(vid)
|
|
require.False(t, found0, "copy on disk 0 must be deleted")
|
|
require.False(t, found1, "the stale twin on disk 1 must also be deleted")
|
|
}
|