From 02dac231197b83e74ecd6c23af43ddb303d11c11 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 9 Feb 2026 18:00:09 -0800 Subject: [PATCH 1/4] Fix master leader detection when grpc ports change --- weed/command/master.go | 4 ++-- weed/command/master_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 weed/command/master_test.go diff --git a/weed/command/master.go b/weed/command/master.go index 2a7511fbf..36e687250 100644 --- a/weed/command/master.go +++ b/weed/command/master.go @@ -360,12 +360,12 @@ func checkPeers(masterIp string, masterPort int, masterGrpcPort int, peers strin func isTheFirstOne(self pb.ServerAddress, peers []pb.ServerAddress) bool { slices.SortFunc(peers, func(a, b pb.ServerAddress) int { - return strings.Compare(string(a), string(b)) + return strings.Compare(a.ToHttpAddress(), b.ToHttpAddress()) }) if len(peers) <= 0 { return true } - return self == peers[0] + return self.ToHttpAddress() == peers[0].ToHttpAddress() } func (m *MasterOptions) toMasterOption(whiteList []string) *weed_server.MasterOption { diff --git a/weed/command/master_test.go b/weed/command/master_test.go new file mode 100644 index 000000000..c485a4aaa --- /dev/null +++ b/weed/command/master_test.go @@ -0,0 +1,35 @@ +package command + +import ( + "testing" + + "github.com/seaweedfs/seaweedfs/weed/pb" +) + +func TestIsTheFirstOneIgnoresGrpcPort(t *testing.T) { + self := pb.ServerAddress("127.0.0.1:9000.19000") + peers := []pb.ServerAddress{ + "127.0.0.1:9000", + "127.0.0.1:9002.19002", + "127.0.0.1:9003.19003", + } + + if !isTheFirstOne(self, peers) { + t.Fatalf("expected first peer match by HTTP address between %q and %+v", self, peers) + } +} + +func TestCheckPeersAddsSelfWhenGrpcPortMismatches(t *testing.T) { + self, peers := checkPeers("127.0.0.1", 9000, 19000, "127.0.0.1:9002,127.0.0.1:9003") + + found := false + for _, peer := range peers { + if peer.ToHttpAddress() == self.ToHttpAddress() { + found = true + break + } + } + if !found { + t.Fatalf("expected peers %+v to contain self %s by HTTP address", peers, self) + } +} From ae27e17e6fb229e6044033a9accc2c3b0f98e8b3 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 9 Feb 2026 18:07:01 -0800 Subject: [PATCH 2/4] Canonicalize self peer entry to avoid raft self-alias panic --- weed/command/master.go | 6 ++++-- weed/command/master_test.go | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/weed/command/master.go b/weed/command/master.go index 36e687250..3389fabc0 100644 --- a/weed/command/master.go +++ b/weed/command/master.go @@ -342,10 +342,12 @@ func checkPeers(masterIp string, masterPort int, masterGrpcPort int, peers strin cleanedPeers = pb.ServerAddresses(peers).ToAddresses() hasSelf := false - for _, peer := range cleanedPeers { + for i, peer := range cleanedPeers { if peer.ToHttpAddress() == masterAddress.ToHttpAddress() { + // Canonicalize self to avoid adding a second logical self peer + // when -peers uses host:port and local name is host:port.grpcPort. + cleanedPeers[i] = masterAddress hasSelf = true - break } } diff --git a/weed/command/master_test.go b/weed/command/master_test.go index c485a4aaa..947af988b 100644 --- a/weed/command/master_test.go +++ b/weed/command/master_test.go @@ -33,3 +33,13 @@ func TestCheckPeersAddsSelfWhenGrpcPortMismatches(t *testing.T) { t.Fatalf("expected peers %+v to contain self %s by HTTP address", peers, self) } } + +func TestCheckPeersCanonicalizesSelfEntry(t *testing.T) { + self, peers := checkPeers("127.0.0.1", 9000, 19000, "127.0.0.1:9000,127.0.0.1:9002,127.0.0.1:9003") + + for _, peer := range peers { + if peer.ToHttpAddress() == self.ToHttpAddress() && peer != self { + t.Fatalf("expected self peer to be canonicalized to %q, got %q", self, peer) + } + } +} From 15d0a46679c7b330154680449eedfe63e2abdc4b Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 9 Feb 2026 18:10:54 -0800 Subject: [PATCH 3/4] Normalize and deduplicate master peer addresses --- weed/command/master.go | 37 +++++++++++++++++++++++++++++++------ weed/command/master_test.go | 21 +++++++++++++++++++++ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/weed/command/master.go b/weed/command/master.go index 3389fabc0..565c9cc58 100644 --- a/weed/command/master.go +++ b/weed/command/master.go @@ -4,9 +4,11 @@ import ( "context" "crypto/tls" "fmt" + "net" "net/http" "os" "path" + "strconv" "strings" "time" @@ -338,16 +340,22 @@ func checkPeers(masterIp string, masterPort int, masterGrpcPort int, peers strin } peers = strings.TrimSpace(peers) - - cleanedPeers = pb.ServerAddresses(peers).ToAddresses() + seenPeers := make(map[string]struct{}) + for _, peer := range pb.ServerAddresses(peers).ToAddresses() { + normalizedPeer := normalizeMasterPeerAddress(peer, masterAddress) + key := string(normalizedPeer) + if _, found := seenPeers[key]; found { + continue + } + seenPeers[key] = struct{}{} + cleanedPeers = append(cleanedPeers, normalizedPeer) + } hasSelf := false - for i, peer := range cleanedPeers { + for _, peer := range cleanedPeers { if peer.ToHttpAddress() == masterAddress.ToHttpAddress() { - // Canonicalize self to avoid adding a second logical self peer - // when -peers uses host:port and local name is host:port.grpcPort. - cleanedPeers[i] = masterAddress hasSelf = true + break } } @@ -360,6 +368,23 @@ func checkPeers(masterIp string, masterPort int, masterGrpcPort int, peers strin return } +func normalizeMasterPeerAddress(peer pb.ServerAddress, self pb.ServerAddress) pb.ServerAddress { + if peer.ToHttpAddress() == self.ToHttpAddress() { + return self + } + + _, grpcPort, err := net.SplitHostPort(peer.ToGrpcAddress()) + if err != nil { + return peer + } + grpcPortValue, err := strconv.Atoi(grpcPort) + if err != nil { + return peer + } + + return pb.NewServerAddressWithGrpcPort(peer.ToHttpAddress(), grpcPortValue) +} + func isTheFirstOne(self pb.ServerAddress, peers []pb.ServerAddress) bool { slices.SortFunc(peers, func(a, b pb.ServerAddress) int { return strings.Compare(a.ToHttpAddress(), b.ToHttpAddress()) diff --git a/weed/command/master_test.go b/weed/command/master_test.go index 947af988b..b92254606 100644 --- a/weed/command/master_test.go +++ b/weed/command/master_test.go @@ -43,3 +43,24 @@ func TestCheckPeersCanonicalizesSelfEntry(t *testing.T) { } } } + +func TestCheckPeersDeduplicatesAliasPeers(t *testing.T) { + _, peers := checkPeers("127.0.0.1", 9000, 19000, "127.0.0.1:9002,127.0.0.1:9002.19002,127.0.0.1:9003") + + if len(peers) != 3 { + t.Fatalf("expected 3 unique peers after normalization, got %d: %+v", len(peers), peers) + } + + count9002 := 0 + for _, peer := range peers { + if peer.ToHttpAddress() == "127.0.0.1:9002" { + count9002++ + if string(peer) != "127.0.0.1:9002.19002" { + t.Fatalf("expected canonical peer 127.0.0.1:9002.19002, got %q", peer) + } + } + } + if count9002 != 1 { + t.Fatalf("expected one peer for 127.0.0.1:9002, got %d in %+v", count9002, peers) + } +} From af8273386dce51cab7862f9c8f825f82be1e56ef Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 9 Feb 2026 18:15:19 -0800 Subject: [PATCH 4/4] 4.12 --- k8s/charts/seaweedfs/Chart.yaml | 4 ++-- weed/util/version/constants.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/k8s/charts/seaweedfs/Chart.yaml b/k8s/charts/seaweedfs/Chart.yaml index 643a021c0..2b1105c9a 100644 --- a/k8s/charts/seaweedfs/Chart.yaml +++ b/k8s/charts/seaweedfs/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v1 description: SeaweedFS name: seaweedfs -appVersion: "4.11" +appVersion: "4.12" # Dev note: Trigger a helm chart release by `git tag -a helm-` -version: 4.0.411 +version: 4.0.412 diff --git a/weed/util/version/constants.go b/weed/util/version/constants.go index 0bb04e437..7d7cdd97b 100644 --- a/weed/util/version/constants.go +++ b/weed/util/version/constants.go @@ -9,7 +9,7 @@ import ( var ( MAJOR_VERSION = int32(4) - MINOR_VERSION = int32(11) + MINOR_VERSION = int32(12) VERSION_NUMBER = fmt.Sprintf("%d.%02d", MAJOR_VERSION, MINOR_VERSION) VERSION = util.SizeLimit + " " + VERSION_NUMBER COMMIT = ""