fix(cluster): gossiped-role predicates compare case-insensitively — a miscased peer no longer un-elects the Primary
This commit is contained in:
+4
-2
@@ -181,10 +181,12 @@ public sealed class ClusterNodeAddressReconcilerActor : ReceiveActor, IWithTimer
|
||||
public static IReadOnlyList<(string Host, int Port)> FilterOwnClusterDriverMembers(
|
||||
IEnumerable<Member> members, string? ownClusterRole)
|
||||
{
|
||||
// R3-04 (01/C-9): GossipedRolesHave, not ordinal Contains — a peer gossiping a miscased
|
||||
// 'Driver' (rolling-upgrade window) must not read as a phantom RunningNodeHasNoRow finding.
|
||||
var driverMembers = members
|
||||
.Where(m => m.Status == MemberStatus.Up && m.Roles.Contains(DriverRole));
|
||||
.Where(m => m.Status == MemberStatus.Up && RoleParser.GossipedRolesHave(m.Roles, DriverRole));
|
||||
if (!string.IsNullOrEmpty(ownClusterRole))
|
||||
driverMembers = driverMembers.Where(m => m.Roles.Contains(ownClusterRole));
|
||||
driverMembers = driverMembers.Where(m => RoleParser.GossipedRolesHave(m.Roles, ownClusterRole));
|
||||
return driverMembers
|
||||
.Select(m => (Host: m.Address.Host ?? string.Empty, Port: m.Address.Port ?? 0))
|
||||
.Where(a => !string.IsNullOrWhiteSpace(a.Host) && a.Port > 0)
|
||||
|
||||
@@ -3,6 +3,7 @@ using Akka.Actor;
|
||||
using Akka.Cluster;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ZB.MOM.WW.Audit;
|
||||
using ZB.MOM.WW.OtOpcUa.Cluster;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.ControlPlane.Redundancy;
|
||||
|
||||
@@ -122,7 +123,9 @@ public sealed class ManualFailoverService : IManualFailoverService
|
||||
}
|
||||
|
||||
private static List<Member> OrderedDrivers(IEnumerable<Member> members) => members
|
||||
.Where(m => m.Status == MemberStatus.Up && m.Roles.Contains(RedundancyStateActor.DriverRole))
|
||||
// R3-04 (01/C-9): GossipedRolesHave, not ordinal Contains — a peer gossiping a miscased
|
||||
// 'Driver' (rolling-upgrade window) must still be a manual-failover candidate.
|
||||
.Where(m => m.Status == MemberStatus.Up && RoleParser.GossipedRolesHave(m.Roles, RedundancyStateActor.DriverRole))
|
||||
.OrderBy(m => m, Member.AgeOrdering)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
@@ -190,7 +190,15 @@ public sealed class RedundancyStateActor : ReceiveActor, IWithTimers
|
||||
if (string.IsNullOrWhiteSpace(host)) continue;
|
||||
|
||||
var isPrimary = driverPrimary is not null && driverPrimary == member.Address;
|
||||
var role = member.Roles.Contains(DriverRole)
|
||||
// R3-04 (01/C-9): GossipedRolesHave, not ordinal Contains — a peer running an older build
|
||||
// can still gossip a miscased 'Driver' during a rolling upgrade, and this list is what
|
||||
// /hosts and /fleet render, so a miscased-but-genuinely-driver member must not read as
|
||||
// Detached. NOTE: this does NOT reach SelectDriverPrimary/driverPrimary above — that
|
||||
// delegates to ClusterActiveNode.OldestUpMember (ZB.MOM.WW.Health.Akka), which matches via
|
||||
// Akka's own ordinal Member.HasRole and is not this repo's to change (see R3-04 execution
|
||||
// notes). So a member gossiping only 'Driver' now classifies Secondary rather than
|
||||
// Detached, but a PAIR that BOTH gossip 'Driver' still elects no Primary.
|
||||
var role = RoleParser.GossipedRolesHave(member.Roles, DriverRole)
|
||||
? (isPrimary ? CommonsRedundancyRole.Primary : CommonsRedundancyRole.Secondary)
|
||||
: CommonsRedundancyRole.Detached;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user