fix(mtconnect): Unsupported latch inside the checked section; live-index reads do not clear a write degradation (review r1 N-4, N-5)
- N-4: the /sample Unsupported latch is set inside TryDegradeStream's generation-checked critical section (latchUnsupported), so a superseded pump can no longer latch it on the session that replaced it. - N-5: a read served from the live pump index clears only the read degradation - it is stream evidence, like a chunk, which by the I-3 ruling does not clear an unanswered write. The 'cannot stick' wording in the XML doc and docs/drivers/MTConnect.md now states the latch-until-next-answered-/current-read- or-write behaviour, incl. the subscription-only consequence. Tested (chunk + live-index read keep Degraded; an answered write clears it). Refs #743 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
@@ -371,8 +371,9 @@ public sealed partial class MTConnectDriver
|
||||
|
||||
/// <summary>
|
||||
/// Whether the last Agent write got no reply (review I-3 / coordinator ruling: an unanswered write degrades).
|
||||
/// Cleared by the next answered read OR write (<see cref="RecordReadAnswered"/> / <see cref="RecordWriteAnswered"/>),
|
||||
/// so it cannot latch on a driver that is still talking to the Agent.
|
||||
/// Latched until the next answered <c>/current</c> read or answered write (<see cref="RecordReadAnswered"/> /
|
||||
/// <see cref="RecordWriteAnswered"/>). <b>Neither a <c>/sample</c> chunk nor a live-index read clears it</b>, so on
|
||||
/// a subscription-only driver one timed-out write keeps the driver Degraded until the next write (r1 N-5).
|
||||
/// </summary>
|
||||
private volatile bool _writeDegraded;
|
||||
|
||||
@@ -855,9 +856,13 @@ public sealed partial class MTConnectDriver
|
||||
|
||||
// The read path is answering (from a stream that is delivering), so a degradation a failed /current left
|
||||
// behind is no longer true of it. The stamp stays the pump's — this read heard nothing from the Agent itself.
|
||||
if (_readDegraded || _writeDegraded)
|
||||
// Clears only the READ degradation (r1 N-5): an index read is the stream's evidence, not an Agent answer to a
|
||||
// request — the same evidence a /sample chunk carries, which by the I-3 ruling does not clear an unanswered
|
||||
// write either.
|
||||
if (_readDegraded)
|
||||
{
|
||||
RecordReadAnswered(ReadHealth().LastSuccessfulRead ?? DateTime.UtcNow);
|
||||
_readDegraded = false;
|
||||
PublishHealthy(ReadHealth().LastSuccessfulRead ?? DateTime.UtcNow);
|
||||
}
|
||||
|
||||
return results;
|
||||
@@ -2255,12 +2260,9 @@ public sealed partial class MTConnectDriver
|
||||
// forever, so retrying would be an unbounded hot loop against an endpoint
|
||||
// that will never stream until an operator changes something. Latch it, say
|
||||
// so loudly, and stop. A re-initialize clears the latch.
|
||||
lock (_subscriptionLock)
|
||||
{
|
||||
_streamUnsupported = true;
|
||||
}
|
||||
|
||||
if (!TryDegradeStream(generation, outcome.Error!.Message))
|
||||
// r1 N-4: the latch is set inside the same generation-checked section as the degrade, so a
|
||||
// superseded pump can never latch Unsupported on the session that replaced it.
|
||||
if (!TryDegradeStream(generation, outcome.Error!.Message, latchUnsupported: true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -2707,7 +2709,8 @@ public sealed partial class MTConnectDriver
|
||||
/// <param name="generation">The pump's session generation.</param>
|
||||
/// <param name="message">The failure (redacted text only).</param>
|
||||
/// <returns><see langword="false"/> when the pump's session has been replaced (nothing written).</returns>
|
||||
private bool TryDegradeStream(long generation, string message)
|
||||
/// <param name="latchUnsupported">Also latch the "endpoint cannot stream" verdict (r1 N-4).</param>
|
||||
private bool TryDegradeStream(long generation, string message, bool latchUnsupported = false)
|
||||
{
|
||||
// The mirror of TryMarkStreamDelivered: a superseded pump's failure is not the new session's.
|
||||
lock (_subscriptionLock)
|
||||
@@ -2717,6 +2720,11 @@ public sealed partial class MTConnectDriver
|
||||
return false;
|
||||
}
|
||||
|
||||
if (latchUnsupported)
|
||||
{
|
||||
_streamUnsupported = true;
|
||||
}
|
||||
|
||||
_streamDegraded = true;
|
||||
Degrade(message);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user