mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-20 21:14:25 +02:00
* pb: heartbeat fields for the federated volume-id bound (#11763) Heartbeat carries max_volume_id_seen and last_seen_topology_id up, and HeartbeatResponse echoes max_volume_id and topology_id down, so the allocation bound is replicated to every volume server. * master: echo the volume-id bound and fold reported bounds in (#11763) Every heartbeat response carries the leader current max volume id and topology id; every heartbeat reported max_volume_id_seen is folded into the topology bound via UpAdjustMaxVolumeId. * volume: persist a per-dir vid stamp of the highest id ever seen (#11763) The store keeps max_volume_id_seen + last_seen_topology_id in a .vid_stamp file per volume dir, fed by heartbeat echoes, AllocateVolume, and local volume/EC creation, seeded at boot from stamps and local holdings, and reported in every heartbeat. A rebuilt master recovers the bound from the first surviving veteran report. * volume-rust: mirror the vid stamp and heartbeat echo handling (#11763) Same mechanism as the Go volume server: .vid_stamp per dir, bound folded in from add_location scans and allocate/mount calls, echoed fields applied on heartbeat responses, reported on every heartbeat. * volume: serialize vid stamp writes and fsync the directory (#11763) * volume-rust: serialize vid stamp writes and fsync the directory (#11763) * master: echo the allocation bound to connected servers on change The bound was only sent at connect time or on digest mismatch, so a healthy stream never learned later increases. If the high-id server then went offline and the master lost state, survivors restored a stale bound and the id could be reissued. Resend the heartbeat response whenever the bound or topology id moved. * volume: retry failed vid stamp saves on later updates A transient write failure left the in-memory bound raised but the stamp stale, and repeat updates at the same bound never retried. Track a dirty flag so any later update re-attempts the save. * volume: cover stamp bootstrap for pre-stamp dirs in test * volume-rust: retry failed vid stamp saves, mirroring Go * volume-rust: stamp every dir with the combined bound on add_location A dir whose own stamp sat below the combined bound kept its stale value after fold; losing the higher-stamped disk then restarted the server below ids it had already learned. Rewrite all stamps after each location folds in. * volume: stamp mounted volume and ec shard ids (#11763) * volume-rust: stamp mounted volume ids, mirroring Go (#11763) * volume: retry dirty vid stamp save on unchanged topology id (#11763) * volume-rust: retry dirty vid stamp save on unchanged topology id (#11763) * master: record echoed bound from the sent heartbeat response (#11763)
95 lines
2.6 KiB
Go
95 lines
2.6 KiB
Go
package storage
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/stats"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/needle"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/types"
|
|
"github.com/seaweedfs/seaweedfs/weed/util"
|
|
)
|
|
|
|
func newVidStampStore(t *testing.T, dir string) *Store {
|
|
t.Helper()
|
|
return NewStore(nil, "localhost", 8080, 18080, "http://localhost:8080", "store-id",
|
|
[]string{dir}, []int32{100}, []util.MinFreeSpace{{}}, "",
|
|
NeedleMapInMemory, []types.DiskType{types.HardDriveType}, nil, 3,
|
|
stats.DefaultDiskIOProbeConfig())
|
|
}
|
|
|
|
func TestVidStampWrittenOnBoundIncrease(t *testing.T) {
|
|
dir := t.TempDir()
|
|
store := newVidStampStore(t, dir)
|
|
defer store.Close()
|
|
|
|
store.UpdateMaxVolumeIdSeen(needle.VolumeId(42))
|
|
|
|
data, err := os.ReadFile(filepath.Join(dir, vidStampFileName))
|
|
if err != nil {
|
|
t.Fatalf("read stamp: %v", err)
|
|
}
|
|
stamp, err := readVidStamp(dir)
|
|
if err != nil {
|
|
t.Fatalf("parse stamp: %v", err)
|
|
}
|
|
if stamp.MaxVolumeId != 42 {
|
|
t.Fatalf("stamp max = %d, want 42", stamp.MaxVolumeId)
|
|
}
|
|
if len(data) == 0 {
|
|
t.Fatal("empty stamp")
|
|
}
|
|
}
|
|
|
|
func TestVidStampSurvivesRestart(t *testing.T) {
|
|
dir := t.TempDir()
|
|
|
|
store := newVidStampStore(t, dir)
|
|
store.UpdateMaxVolumeIdSeen(needle.VolumeId(42))
|
|
store.UpdateLastSeenTopologyId("cluster-a")
|
|
store.Close()
|
|
|
|
restarted := newVidStampStore(t, dir)
|
|
defer restarted.Close()
|
|
if got := restarted.MaxVolumeIdSeen(); got != needle.VolumeId(42) {
|
|
t.Fatalf("restored bound = %d, want 42", got)
|
|
}
|
|
if got := restarted.LastSeenTopologyId(); got != "cluster-a" {
|
|
t.Fatalf("restored topology id = %q, want cluster-a", got)
|
|
}
|
|
}
|
|
|
|
func TestVidStampMonotonic(t *testing.T) {
|
|
dir := t.TempDir()
|
|
store := newVidStampStore(t, dir)
|
|
defer store.Close()
|
|
|
|
store.UpdateMaxVolumeIdSeen(needle.VolumeId(10))
|
|
store.UpdateMaxVolumeIdSeen(needle.VolumeId(5))
|
|
if got := store.MaxVolumeIdSeen(); got != needle.VolumeId(10) {
|
|
t.Fatalf("bound regressed to %d, want 10", got)
|
|
}
|
|
}
|
|
|
|
func TestVidStampBootstrapFromVolumes(t *testing.T) {
|
|
dir := t.TempDir()
|
|
|
|
store := newVidStampStore(t, dir)
|
|
if err := store.AddVolume(17, "", NeedleMapInMemory, "000", "", 0, needle.GetCurrentVersion(), 0, types.HardDriveType, 0); err != nil {
|
|
t.Fatalf("add volume: %v", err)
|
|
}
|
|
store.Close()
|
|
|
|
// upgrading a pre-stamp dir: volumes exist but no stamp yet
|
|
if err := os.Remove(filepath.Join(dir, vidStampFileName)); err != nil {
|
|
t.Fatalf("remove stamp: %v", err)
|
|
}
|
|
|
|
restarted := newVidStampStore(t, dir)
|
|
defer restarted.Close()
|
|
if got := restarted.MaxVolumeIdSeen(); got != needle.VolumeId(17) {
|
|
t.Fatalf("bootstrapped bound = %d, want 17", got)
|
|
}
|
|
}
|