fix(controlplane): case-insensitive NodeId equality for deploy ack-set
Aligns ConfigPublishCoordinator's _acks/_expectedAcks with the case-insensitive ClusterId/NodeId scoping in DeploymentArtifact.ResolveClusterScope, so an ack from a node whose host:port differs only in case still matches its expected-ack entry (SQL collation + DNS are case-insensitive).
This commit is contained in:
@@ -30,10 +30,15 @@ public sealed class ConfigPublishCoordinator : ReceiveActor, IWithTimers
|
||||
private readonly IDbContextFactory<OtOpcUaConfigDbContext> _dbFactory;
|
||||
private readonly TimeSpan _applyDeadline;
|
||||
private readonly ILoggingAdapter _log = Context.GetLogger();
|
||||
private readonly Dictionary<NodeId, ApplyAckOutcome> _acks = new();
|
||||
// NodeId equality here is case-insensitive (by Value) to match the case-insensitive ClusterId/
|
||||
// NodeId scoping in DeploymentArtifact.ResolveClusterScope — so an ack from a node whose address
|
||||
// differs only in case still matches its expected-ack entry (SQL collation + DNS are
|
||||
// case-insensitive, so the same node can surface with different casing).
|
||||
private static readonly IEqualityComparer<NodeId> NodeIdComparer = new CaseInsensitiveNodeIdComparer();
|
||||
private readonly Dictionary<NodeId, ApplyAckOutcome> _acks = new(NodeIdComparer);
|
||||
|
||||
private DeploymentId? _current;
|
||||
private HashSet<NodeId> _expectedAcks = new();
|
||||
private HashSet<NodeId> _expectedAcks = new(NodeIdComparer);
|
||||
|
||||
/// <summary>Gets the timer scheduler for managing apply deadlines.</summary>
|
||||
public ITimerScheduler Timers { get; set; } = null!;
|
||||
@@ -88,7 +93,7 @@ public sealed class ConfigPublishCoordinator : ReceiveActor, IWithTimers
|
||||
.AsNoTracking()
|
||||
.ToList();
|
||||
|
||||
_expectedAcks = nodeStates.Select(s => NodeId.Parse(s.NodeId)).ToHashSet();
|
||||
_expectedAcks = nodeStates.Select(s => NodeId.Parse(s.NodeId)).ToHashSet(NodeIdComparer);
|
||||
foreach (var s in nodeStates.Where(s => s.Status != NodeDeploymentStatus.Applying))
|
||||
_acks[NodeId.Parse(s.NodeId)] = s.Status == NodeDeploymentStatus.Applied
|
||||
? ApplyAckOutcome.Applied
|
||||
@@ -248,7 +253,7 @@ public sealed class ConfigPublishCoordinator : ReceiveActor, IWithTimers
|
||||
private HashSet<NodeId> DiscoverDriverNodes()
|
||||
{
|
||||
var cluster = Akka.Cluster.Cluster.Get(Context.System);
|
||||
var nodes = new HashSet<NodeId>();
|
||||
var nodes = new HashSet<NodeId>(NodeIdComparer);
|
||||
foreach (var member in cluster.State.Members)
|
||||
{
|
||||
if (member.Status is not (MemberStatus.Up or MemberStatus.Joining)) continue;
|
||||
@@ -261,4 +266,18 @@ public sealed class ConfigPublishCoordinator : ReceiveActor, IWithTimers
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/// <summary>Case-insensitive <see cref="NodeId"/> equality (by <see cref="NodeId.Value"/>),
|
||||
/// matching the case-insensitive scoping in <c>DeploymentArtifact.ResolveClusterScope</c> so the
|
||||
/// expected-ack set and incoming acks agree regardless of host-name casing.</summary>
|
||||
private sealed class CaseInsensitiveNodeIdComparer : IEqualityComparer<NodeId>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool Equals(NodeId x, NodeId y) =>
|
||||
string.Equals(x.Value, y.Value, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <inheritdoc />
|
||||
public int GetHashCode(NodeId obj) =>
|
||||
StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Value ?? string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user