refactor(drivers): delete the inert driver-tier recycle machinery (#522)
The tier system was documented, operator-authorable, and inert. Deleted rather than activated,
because its premise is gone rather than merely unused.
"Tier C" meant a driver running out-of-process behind an IDriverSupervisor that could restart its
Host without tearing down the OPC UA session. No such process exists anywhere: Galaxy reaches
MXAccess over gRPC to the external mxaccessgw sidecar (PR 7.2 retired the in-process
Galaxy.Host/Proxy/Shared projects) and FOCAS has run in-process since its managed wire client
landed 2026-04-24. Consistently, IDriverSupervisor had ZERO implementations and there was nothing
for one to implement against.
The issue understated the inertness. It says the Tier-C-only protections never engaged, implying
the Tier A/B parts did. They did not: nothing constructs MemoryTracking, MemoryRecycle or
ScheduledRecycleScheduler outside their own unit tests, so the whole Core/Stability recycle layer
was dead — meaning option (a), "pass real tiers", was never a flag flip. It would have meant
writing the wiring that never existed AND arming it.
Deleted: MemoryTracking, MemoryRecycle, ScheduledRecycleScheduler, IDriverSupervisor, the
vestigial DriverTypeRegistry (referenced only by its own tests), and the RecycleIntervalSeconds
knob — which the AdminUI let an operator author and the parser validated while it configured
nothing.
DriverTier itself SURVIVES and is load-bearing: DriverResilienceOptions.GetTierDefaults supplies
the real per-capability timeout/retry/breaker policies via DriverFactoryRegistry.GetTier and
DriverCapabilityInvokerFactory. Only the isolation-and-recycle layer above it is gone.
Deliberately NOT deleted:
- WedgeDetector came along in the same directory and is equally dead in production, but it is
tier-agnostic and is not recycle machinery — it only shares the folder. Restored rather than
swept up in a decision that was not about it.
- IDriver.GetMemoryFootprint() and FlushOptionalCachesAsync() lose their only consumer here.
Removing them touches all 12 drivers and every test stub, so they are documented as
consumerless and filed as #525 instead of buried in this diff.
Compatibility: a deployed ResilienceConfig blob still carrying "recycleIntervalSeconds" parses
cleanly (unknown keys are ignored — guarded by a new test, because a blob that suddenly failed to
parse would fall back to tier defaults and silently discard the operator's real overrides), and
the AdminUI's preserve-unknown-keys bag keeps the key rather than rewriting stored config on an
unrelated edit.
The 01/U-6 knob-inertness guard carried an explicit carve-out admitting RecycleIntervalSeconds was
dormant and out of scope; that carve-out is now gone, so the expected set is literally what the
test's name claims.
Note: Host.IntegrationTests has 2 failures (DriverProbeRegistrationTests.is_idempotent,
PrimaryGateFailoverTests) — verified pre-existing by reproducing both on clean master dc9d947b.
Claude-Session: https://claude.ai/code/session_015p7wGqy3YpZNCpDzTpGMKo
This commit is contained in:
+6
-4
@@ -20,10 +20,12 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4"><label class="form-label">Recycle interval (s, Tier C only)</label>
|
||||
<input type="number" class="form-control form-control-sm" @bind="_m.RecycleIntervalSeconds" @bind:after="EmitAsync" placeholder="none" disabled="@_m.ParseFailed" /></div>
|
||||
</div>
|
||||
@* The "Recycle interval (s, Tier C only)" input was removed in Gitea #522. It authored a knob
|
||||
with no effect: the scheduled-recycle machinery it fed was constructed only in tests, and
|
||||
the IDriverSupervisor that would have performed the recycle had no implementations. The
|
||||
tier model it belonged to assumed an out-of-process driver Host that no longer exists
|
||||
anywhere. Offering an operator a control that silently does nothing is worse than not
|
||||
offering it. *@
|
||||
|
||||
<div class="table-wrap mt-3">
|
||||
<table class="data-table">
|
||||
|
||||
+6
-5
@@ -23,8 +23,12 @@ public sealed class ResilienceFormModel
|
||||
public static readonly string[] Capabilities =
|
||||
["Read", "Write", "Discover", "Subscribe", "Probe", "AlarmSubscribe", "AlarmAcknowledge", "HistoryRead"];
|
||||
|
||||
/// <summary>Gets or sets the driver recycle-interval override, in seconds; null = use the tier default.</summary>
|
||||
public int? RecycleIntervalSeconds { get; set; }
|
||||
// RecycleIntervalSeconds is GONE (Gitea #522) — the scheduled-recycle machinery it authored was
|
||||
// deleted, so the field configured nothing. It is deliberately NOT explicitly stripped from stored
|
||||
// JSON: the model's preserve-unknown-keys contract now covers it, so an existing blob's
|
||||
// "recycleIntervalSeconds" survives an unrelated edit in _bag rather than being silently dropped,
|
||||
// and the parser ignores it. Removing the key is a migration decision, not a side effect of
|
||||
// opening the form.
|
||||
|
||||
// capability name -> (timeout, retry, breaker), each nullable.
|
||||
/// <summary>Gets or sets the per-capability resilience overrides, keyed by capability name.</summary>
|
||||
@@ -112,7 +116,6 @@ public sealed class ResilienceFormModel
|
||||
}
|
||||
|
||||
model._bag = bag;
|
||||
model.RecycleIntervalSeconds = GetIntOrNull(bag, "recycleIntervalSeconds");
|
||||
|
||||
if (bag.TryGetPropertyValue("capabilityPolicies", out var cpNode) && cpNode is JsonObject cp)
|
||||
{
|
||||
@@ -141,8 +144,6 @@ public sealed class ResilienceFormModel
|
||||
{
|
||||
if (ParseFailed) return RawStoredJson;
|
||||
|
||||
SetOrRemoveInt(_bag, "recycleIntervalSeconds", RecycleIntervalSeconds);
|
||||
|
||||
var cp = _bag.TryGetPropertyValue("capabilityPolicies", out var cpNode) && cpNode is JsonObject existing
|
||||
? existing
|
||||
: null;
|
||||
|
||||
Reference in New Issue
Block a user