Files
seaweedfs/weed/wdclient/vid_map_remote_transition_test.go
T
bruce-zzz 116982595b wdclient: propagate DataInRemote across tier transitions on existing replicas
When a volume is tiered to remote storage or a remote-backed replica is
restored locally, the cached DataInRemote on the same volume-server URL
stayed at its old value because two pieces of state never updated:

* master_grpc_server.go only split newVolumes and (already-tracked) volumes
  into NewVids vs RemoteVids. ChangedVolumes went straight to NewVids, so
  the broadcast announced the re-classified volume as a fresh arrival and
  the client had no way to tell whether its existing cache was stale.

* vid_map.addLocationToMap early-returned when an entry already had the
  same URL. A tier transition reports the same URL with DataInRemote
  flipped, so the cached entry stayed at the old classification.

Wire both sides together: ChangedVolumes now go through the same IsRemote
split as newVolumes, and addLocationToMap replaces the existing entry in
place when the URL matches but DataInRemote has changed. The server
reference key only depends on URL/grpc port, so the refcount does not
move across the flip.

Adds vid_map_remote_transition_test.go covering the local->remote and
remote->local paths so the in-place update and the cache-key stability
are pinned by tests.
2026-09-02 20:17:48 +08:00

92 lines
2.9 KiB
Go

package wdclient
import (
"testing"
"github.com/seaweedfs/seaweedfs/weed/pb"
)
// When the master reports a tier transition (local ↔ remote) for an existing
// replica on the same server URL, the cached DataInRemote must update. Otherwise
// reads keep preferring a remote-backed replica or skip a newly-restored local
// one. The server refcount must stay stable because the server key only depends
// on the URL/grpc port.
func TestAddLocationUpdatesDataInRemoteOnTransition(t *testing.T) {
vm := newVidMap("", DefaultVidMapCacheSize)
vid := uint32(11)
server := Location{Url: "10.0.0.1:8080", DataCenter: "dc1", GrpcPort: 18080}
// First seen as local.
vm.addLocation(vid, server)
locs, found := vm.GetLocations(vid)
if !found || len(locs) != 1 || locs[0].DataInRemote {
t.Fatalf("expected single local replica, got %+v", locs)
}
// Same URL flips to remote.
tiered := server
tiered.DataInRemote = true
vm.addLocation(vid, tiered)
locs, found = vm.GetLocations(vid)
if !found {
t.Fatalf("tier transition dropped the replica")
}
if len(locs) != 1 {
t.Fatalf("tier transition should replace in place, got %d entries: %+v", len(locs), locs)
}
if !locs[0].DataInRemote {
t.Errorf("DataInRemote did not flip to remote on tier-out: %+v", locs[0])
}
if locs[0].Url != server.Url || locs[0].DataCenter != server.DataCenter {
t.Errorf("replaced entry lost non-DataInRemote fields: %+v", locs[0])
}
if !vm.hasVolumeServer(pb.ServerAddress(server.Url)) {
t.Errorf("server ref should remain stable across DataInRemote flip")
}
// Restored locally: same URL, DataInRemote back to false.
restored := server
vm.addLocation(vid, restored)
locs, found = vm.GetLocations(vid)
if !found {
t.Fatalf("restore transition dropped the replica")
}
if len(locs) != 1 {
t.Fatalf("restore should replace in place, got %d entries: %+v", len(locs), locs)
}
if locs[0].DataInRemote {
t.Errorf("DataInRemote did not flip back to local on restore: %+v", locs[0])
}
}
// LookupVolumeServerUrl must reflect the latest DataInRemote so the local-first
// ordering picks up newly-restored local replicas on the next read.
func TestLookupVolumeServerUrlReflectsRemoteTransition(t *testing.T) {
vm := newVidMap("dc1", DefaultVidMapCacheSize)
vid := uint32(12)
remote := Location{Url: "10.0.0.1:8080", DataCenter: "dc1", DataInRemote: true}
vm.addLocation(vid, remote)
urls, err := vm.LookupVolumeServerUrl("12")
if err != nil {
t.Fatalf("lookup failed: %v", err)
}
if len(urls) != 1 || urls[0] != "10.0.0.1:8080" {
t.Fatalf("expected only the remote replica, got %v", urls)
}
// Tier restored: same URL, DataInRemote=false.
local := Location{Url: "10.0.0.1:8080", DataCenter: "dc1"}
vm.addLocation(vid, local)
urls, err = vm.LookupVolumeServerUrl("12")
if err != nil {
t.Fatalf("lookup after restore failed: %v", err)
}
if len(urls) != 1 || urls[0] != "10.0.0.1:8080" {
t.Fatalf("expected only the restored replica, got %v", urls)
}
}