Merge review R1 fixes: removed driver's resilience tracker released; SDK timed-unshelve no-op (engine owns expiry); commented re-ack stored as AddComment; native-ack Primary verdict published from start; drain gate SharesRowsWithPeer; malformed cache bytes don't adopt the revision; HostAbortedException passthrough; ClusterRoleInfo seed filtered
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -76,7 +76,7 @@ public sealed class ClusterRoleInfo : IClusterRoleInfo, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
SeedFromCurrentState();
|
||||
SeedFrom(_cluster.State);
|
||||
_subscriber = system.ActorOf(Props.Create(() => new SubscriberActor(this)), "clusterroleinfo-subscriber");
|
||||
}
|
||||
|
||||
@@ -121,12 +121,19 @@ public sealed class ClusterRoleInfo : IClusterRoleInfo, IDisposable
|
||||
/// <summary>Occurs when the leader for a role changes.</summary>
|
||||
public event EventHandler<RoleLeaderChangedEventArgs>? RoleLeaderChanged;
|
||||
|
||||
private void SeedFromCurrentState()
|
||||
/// <summary>Seeds the member/leader maps from a cluster-state snapshot (the constructor passes the live
|
||||
/// <c>Cluster.State</c>; exposed for tests).</summary>
|
||||
/// <param name="snapshot">The cluster-state snapshot.</param>
|
||||
internal void SeedFrom(ClusterEvent.CurrentClusterState snapshot)
|
||||
{
|
||||
var snapshot = _cluster.State;
|
||||
lock (_lock)
|
||||
{
|
||||
foreach (var member in snapshot.Members)
|
||||
// Cluster-002 (review R1 m6): seed only USABLE members. A member already Leaving / Exiting / Down
|
||||
// (or still Joining) at seed time is not one, and a departing member never fires a later
|
||||
// MemberLeft / MemberExited / MemberDowned that HandleMemberEvent would drop it on — it would
|
||||
// linger until MemberRemoved, the very delay the member-event handling exists to avoid. A
|
||||
// Joining member is added by its MemberUp.
|
||||
foreach (var member in snapshot.Members.Where(m => m.Status is MemberStatus.Up or MemberStatus.WeaklyUp))
|
||||
foreach (var role in member.Roles)
|
||||
{
|
||||
if (!_membersByRole.TryGetValue(role, out var set))
|
||||
|
||||
@@ -28,6 +28,16 @@ public interface IDriverCapabilityInvokerFactory
|
||||
/// </param>
|
||||
/// <returns>A per-instance <see cref="IDriverCapabilityInvoker"/>.</returns>
|
||||
IDriverCapabilityInvoker Create(string driverInstanceId, string driverType, string? resilienceConfigJson = null);
|
||||
|
||||
/// <summary>
|
||||
/// Release every piece of shared resilience state held for <paramref name="driverInstanceId"/> — called
|
||||
/// by the host when the instance leaves the deployed plan (#609). A concrete factory drops the
|
||||
/// instance's cached pipelines and its status-tracker snapshots, so the periodic resilience publisher
|
||||
/// stops re-emitting (and re-priming new telemetry subscribers with) a decommissioned driver's last
|
||||
/// breaker/failure counters. Default: no-op (a factory that holds no state).
|
||||
/// </summary>
|
||||
/// <param name="driverInstanceId">The driver instance that was removed.</param>
|
||||
void Release(string driverInstanceId) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -105,7 +105,17 @@ public static class Part9StateMachine
|
||||
throw new ArgumentException("User identity required for audit.", nameof(user));
|
||||
|
||||
if (current.Acked == AlarmAckedState.Acknowledged)
|
||||
return TransitionResult.NoOp(current, "already acknowledged");
|
||||
{
|
||||
// A COMMENTED re-ack carries a real comment even though nothing is acknowledged (Gitea #597 review
|
||||
// m1): the OPC UA node reports it — the SDK writes the comment on a re-ack — so the system of
|
||||
// record must hold it too, or the event advertises a comment that exists only on the node that
|
||||
// served the call (never persisted, never replicated to the pair peer). Recorded exactly like
|
||||
// AddComment; the ack fields (LastAck*) stay those of the real acknowledge. An uncommented re-ack
|
||||
// is still a no-op.
|
||||
return string.IsNullOrWhiteSpace(comment)
|
||||
? TransitionResult.NoOp(current, "already acknowledged")
|
||||
: ApplyAddComment(current, user, comment, nowUtc);
|
||||
}
|
||||
|
||||
var audit = AppendComment(current.Comments, nowUtc, user, "Acknowledge", comment);
|
||||
var next = current with
|
||||
|
||||
@@ -90,4 +90,16 @@ public sealed class DriverCapabilityInvokerFactory : IDriverCapabilityInvokerFac
|
||||
statusTracker: _statusTracker,
|
||||
optionsGeneration: generation);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// #609: without this the tracker kept a removed instance's snapshots forever (only <see cref="Create"/>
|
||||
/// invalidated, and a removed instance is never re-created), and the resilience publisher re-emitted
|
||||
/// them every tick — undoing the host's telemetry-hub eviction within one interval.
|
||||
/// </remarks>
|
||||
public void Release(string driverInstanceId)
|
||||
{
|
||||
_builder.Invalidate(driverInstanceId);
|
||||
_statusTracker.Invalidate(driverInstanceId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user