diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ClusterNode.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ClusterNode.cs index f86fbb4..1057b1f 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ClusterNode.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ClusterNode.cs @@ -1,5 +1,3 @@ -using ZB.MOM.WW.OtOpcUa.Configuration.Enums; - namespace ZB.MOM.WW.OtOpcUa.Configuration.Entities; /// Physical OPC UA server node within a . @@ -10,8 +8,6 @@ public sealed class ClusterNode public required string ClusterId { get; set; } - public required RedundancyRole RedundancyRole { get; set; } - /// Machine hostname / IP. public required string Host { get; set; } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Enums/RedundancyRole.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Enums/RedundancyRole.cs deleted file mode 100644 index e0e9ece..0000000 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Enums/RedundancyRole.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace ZB.MOM.WW.OtOpcUa.Configuration.Enums; - -/// Per-node redundancy role within a cluster. Per decision #84. -public enum RedundancyRole -{ - Primary, - Secondary, - Standalone, -} diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/OtOpcUaConfigDbContext.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/OtOpcUaConfigDbContext.cs index 334cd8f..54ba0f6 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/OtOpcUaConfigDbContext.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/OtOpcUaConfigDbContext.cs @@ -115,7 +115,6 @@ public sealed class OtOpcUaConfigDbContext(DbContextOptions x.NodeId); e.Property(x => x.NodeId).HasMaxLength(64); e.Property(x => x.ClusterId).HasMaxLength(64); - e.Property(x => x.RedundancyRole).HasConversion().HasMaxLength(16); e.Property(x => x.Host).HasMaxLength(255); e.Property(x => x.ApplicationUri).HasMaxLength(256); e.Property(x => x.DriverConfigOverridesJson).HasColumnType("nvarchar(max)"); @@ -130,10 +129,10 @@ public sealed class OtOpcUaConfigDbContext(DbContextOptions x.ApplicationUri).IsUnique().HasDatabaseName("UX_ClusterNode_ApplicationUri"); e.HasIndex(x => x.ClusterId).HasDatabaseName("IX_ClusterNode_ClusterId"); - // At most one Primary per cluster - e.HasIndex(x => x.ClusterId).IsUnique() - .HasFilter("[RedundancyRole] = 'Primary'") - .HasDatabaseName("UX_ClusterNode_Primary_Per_Cluster"); + // v2: the "one Primary per cluster" filtered unique index (and the RedundancyRole + // column it filtered on) are gone. Akka cluster leader-of-driver-role is the + // authoritative primary signal (see RedundancyStateActor + ServiceLevelCalculator, + // Task 35). }); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Validation/DraftValidator.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Validation/DraftValidator.cs index 3be8186..8feaad5 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Validation/DraftValidator.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Validation/DraftValidator.cs @@ -228,14 +228,9 @@ public static class DraftValidator $"Toggle the missing node(s) back on or change RedundancyMode/NodeCount to match.", cluster.ClusterId)); - // Primary uniqueness — decision #84. Two Primary nodes is always an invariant violation - // regardless of mode; catch it here so publish fails loud rather than the runtime - // demoting both to ServiceLevelBand.InvalidTopology at boot. - var primaryCount = clusterNodes.Count(n => n.Enabled && n.RedundancyRole == RedundancyRole.Primary); - if (primaryCount > 1) - errors.Add(new("ClusterMultiplePrimary", - $"Cluster '{cluster.ClusterId}' has {primaryCount} Enabled Primary nodes. At most one Primary per cluster.", - cluster.ClusterId)); + // v2: the v1 "exactly one Primary per cluster" invariant is gone. RedundancyRole was + // dropped in Task 14d; in v2 the Akka cluster's role-leader-of-"driver" elects the + // primary at runtime, so there is no static configuration to validate here. return errors; } diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/DraftValidatorTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/DraftValidatorTests.cs index 839c1a1..b467d89 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/DraftValidatorTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/DraftValidatorTests.cs @@ -161,7 +161,7 @@ public sealed class DraftValidatorTests { var cluster = BuildCluster(nodeCount: nodeCount, mode: mode); var nodes = Enumerable.Range(0, enabledNodes) - .Select(i => BuildNode($"n-{i}", enabled: true, role: i == 0 ? RedundancyRole.Primary : RedundancyRole.Secondary)) + .Select(i => BuildNode($"n-{i}", enabled: true)) .ToList(); var errors = DraftValidator.ValidateClusterTopology(cluster, nodes); @@ -175,33 +175,24 @@ public sealed class DraftValidatorTests var cluster = BuildCluster(nodeCount: 2, mode: RedundancyMode.Hot); var nodes = new[] { - BuildNode("primary", enabled: true, role: RedundancyRole.Primary), - BuildNode("backup", enabled: false, role: RedundancyRole.Secondary), + BuildNode("primary", enabled: true), + BuildNode("backup", enabled: false), }; var errors = DraftValidator.ValidateClusterTopology(cluster, nodes); errors.ShouldContain(e => e.Code == "ClusterEnabledNodeCountMismatch"); } - [Fact] - public void ValidateClusterTopology_flags_multiple_Primary() - { - var cluster = BuildCluster(nodeCount: 2, mode: RedundancyMode.Hot); - var nodes = new[] - { - BuildNode("a", enabled: true, role: RedundancyRole.Primary), - BuildNode("b", enabled: true, role: RedundancyRole.Primary), - }; - - var errors = DraftValidator.ValidateClusterTopology(cluster, nodes); - errors.ShouldContain(e => e.Code == "ClusterMultiplePrimary"); - } + // v2: the "exactly one Primary per cluster" check is gone — Akka cluster's + // role-leader-of-"driver" elects the primary at runtime. The corresponding + // ValidateClusterTopology_flags_multiple_Primary test (and the + // ClusterMultiplePrimary error code it asserted) were removed alongside Task 14d. [Fact] public void ValidateClusterTopology_returns_no_errors_on_valid_standalone() { var cluster = BuildCluster(nodeCount: 1, mode: RedundancyMode.None); - var nodes = new[] { BuildNode("only", enabled: true, role: RedundancyRole.Primary) }; + var nodes = new[] { BuildNode("only", enabled: true) }; var errors = DraftValidator.ValidateClusterTopology(cluster, nodes); errors.ShouldBeEmpty(); @@ -219,11 +210,10 @@ public sealed class DraftValidatorTests CreatedBy = "t", }; - private static ClusterNode BuildNode(string id, bool enabled, RedundancyRole role) => new() + private static ClusterNode BuildNode(string id, bool enabled) => new() { NodeId = id, ClusterId = "c-test", - RedundancyRole = role, Host = "localhost", OpcUaPort = 4840, DashboardPort = 5001,