feat(site-runtime): ConnectionQualityChanged(Good) resets tag-subscribe backoff to the floor (Lever 1); lock in superseded-reply drop
Task 4 review fix-ups: RepeatedFailures gaps[1] lower bound 600→500 (two compounding jitter draws bottom out at 512 ms); SubscribeToDcl also clears _tagPathToAttributes and its prune is documented as defensive (PreStart is the only caller).
This commit is contained in:
@@ -186,8 +186,8 @@ public class InstanceActor : ReceiveActor
|
||||
// (a tick while a young request is outstanding re-arms instead of re-sending) and
|
||||
// exponential backoff (see NextTagSubscribeInterval) so a slow gateway is never
|
||||
// handed a fresh N-tag subscribe on top of the one it is still processing.
|
||||
// A re-entry into SubscribeToDcl rebuilds all three together: the retry state is
|
||||
// pruned (timers cancelled, pending rows dropped) before the new grouping is built.
|
||||
// SubscribeToDcl has a single caller today (PreStart); the prune at its top is defensive, so a
|
||||
// future second call site rebuilds the retry state and both tag maps together instead of stranding them.
|
||||
private readonly Dictionary<string, List<string>> _tagsByConnection = new();
|
||||
private readonly Dictionary<string, string> _pendingTagSubscribes = new();
|
||||
private readonly Dictionary<string, TagSubscribeState> _tagSubscribeStates = new();
|
||||
@@ -1171,6 +1171,20 @@ public class InstanceActor : ReceiveActor
|
||||
_logger.LogWarning("Connection {Connection} quality changed to {Quality} for instance {Instance}",
|
||||
qualityChanged.ConnectionName, qualityChanged.Quality, _instanceUniqueName);
|
||||
|
||||
// Lever 1 (2026-08-28): a connection returning to Good is the Instance Actor's
|
||||
// only restore signal (the DCL re-subscribes transparently). If our own subscribe
|
||||
// on it is still unconfirmed, retry at the floor now rather than in up to
|
||||
// TagSubscribeRetryMaxIntervalMs — recovery must be prompt. Timer != null ⇔ the
|
||||
// subscribe is unconfirmed (it is only nulled by a Success); if a request is in
|
||||
// flight and young the re-armed tick just hits the guard and re-arms at the floor.
|
||||
if (qualityChanged.Quality == QualityCode.Good
|
||||
&& _tagSubscribeStates.TryGetValue(qualityChanged.ConnectionName, out var subState)
|
||||
&& subState.Timer != null)
|
||||
{
|
||||
ResetTagSubscribeBackoff(subState);
|
||||
ArmTagSubscribeWatchdog(qualityChanged.ConnectionName, subState);
|
||||
}
|
||||
|
||||
if (_configuration == null) return;
|
||||
|
||||
// Mark all attributes bound to this connection with the new quality
|
||||
@@ -1208,6 +1222,7 @@ public class InstanceActor : ReceiveActor
|
||||
{
|
||||
if (_dclManager == null || _configuration == null) return;
|
||||
|
||||
// Defensive — PreStart is the only caller today.
|
||||
// Lever 1: prune retry state from a previous build — cancels any armed watchdog and
|
||||
// drops its pending row so a re-entry can never strand an in-flight slot.
|
||||
foreach (var state in _tagSubscribeStates.Values)
|
||||
@@ -1221,6 +1236,7 @@ public class InstanceActor : ReceiveActor
|
||||
// Group attributes by their bound connection name into the persistent
|
||||
// _tagsByConnection field so a retry can re-send the exact same request.
|
||||
_tagsByConnection.Clear();
|
||||
_tagPathToAttributes.Clear();
|
||||
foreach (var attr in _configuration.Attributes)
|
||||
{
|
||||
if (string.IsNullOrEmpty(attr.DataSourceReference) ||
|
||||
|
||||
Reference in New Issue
Block a user