diff --git a/weed/server/master_grpc_server_admin.go b/weed/server/master_grpc_server_admin.go index a374c3530..194ff5708 100644 --- a/weed/server/master_grpc_server_admin.go +++ b/weed/server/master_grpc_server_admin.go @@ -170,10 +170,17 @@ func (ms *MasterServer) isKnownPingTarget(ctx context.Context, target string, ta case cluster.FilerType, cluster.BrokerType, cluster.S3Type: return ms.Cluster.IsKnownNode(targetType, addr) case cluster.VolumeServerType: - if ms.Topo == nil { - return false + if ms.Topo != nil && ms.Topo.LookupDataNodeByAddress(addr) != nil { + return true } - return ms.Topo.LookupDataNodeByAddress(addr) != nil + // Only the leader receives volume-server heartbeats, so a follower's + // topology is empty. Fall back to the volume-server set the master + // learns over its own MasterClient subscription to the leader, the + // same source the filer gate trusts. + if ms.MasterClient != nil { + return ms.MasterClient.HasVolumeServer(addr) + } + return false case cluster.MasterType: if ms.option != nil && ms.option.Master.Equals(addr) { return true diff --git a/weed/server/master_grpc_server_admin_ping_test.go b/weed/server/master_grpc_server_admin_ping_test.go index 5c8de4325..dd31f0b0d 100644 --- a/weed/server/master_grpc_server_admin_ping_test.go +++ b/weed/server/master_grpc_server_admin_ping_test.go @@ -8,6 +8,8 @@ import ( "github.com/seaweedfs/seaweedfs/weed/pb" "github.com/seaweedfs/seaweedfs/weed/sequence" "github.com/seaweedfs/seaweedfs/weed/topology" + "github.com/seaweedfs/seaweedfs/weed/wdclient" + "google.golang.org/grpc" ) func TestMasterIsKnownPingTarget(t *testing.T) { @@ -62,6 +64,36 @@ func TestMasterIsKnownPingTarget(t *testing.T) { } } +// On a follower master the topology is empty because only the leader receives +// volume-server heartbeats. The gate must then fall back to the volume-server +// set learned over the MasterClient subscription instead of rejecting every +// volume-server target. The positive MasterClient.HasVolumeServer lookup is +// covered by wdclient.TestHasVolumeServer; here we lock in that an empty +// topology no longer short-circuits to false and that the fallback is +// nil-safe. +func TestMasterIsKnownPingTargetFollowerEmptyTopology(t *testing.T) { + ctx := context.Background() + vs := "10.0.0.10:8080.18080" + + // No topology and no MasterClient: nothing to consult, so reject. + bare := &MasterServer{option: &MasterOption{Master: pb.ServerAddress("10.0.0.1:9333")}} + if bare.isKnownPingTarget(ctx, vs, cluster.VolumeServerType) { + t.Fatalf("volume server must be unknown without topology or MasterClient") + } + + // Empty topology plus a MasterClient that has not learned this volume + // server yet: still reject, but exercise the MasterClient fallback path. + mc := wdclient.NewMasterClient(grpc.EmptyDialOption{}, "", cluster.MasterType, "", "", "", pb.ServerDiscovery{}) + follower := &MasterServer{ + option: &MasterOption{Master: pb.ServerAddress("10.0.0.1:9333")}, + Topo: topology.NewTopology("test", sequence.NewMemorySequencer(), 32*1024, 5, false), + MasterClient: mc, + } + if follower.isKnownPingTarget(ctx, vs, cluster.VolumeServerType) { + t.Fatalf("volume server must be unknown until the MasterClient learns it") + } +} + func TestVolumeServerIsKnownPingTarget(t *testing.T) { seed := pb.ServerAddress("10.0.0.1:9333") vs := &VolumeServer{