From b815446940f4c859deae66f9dfdd9c8bf00b42a8 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 31 Jul 2026 09:07:11 -0400 Subject: [PATCH] refactor(drivers): delete GetMemoryFootprint / FlushOptionalCachesAsync (#525) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both `IDriver` members had no production call site. Their only consumer was `MemoryTracking`, deleted with the driver-tier recycle machinery in #522, and `GetMemoryFootprint`'s doc-comment claimed "polled every 30s by Core" — which had not been true since v3, even before #522 removed the poller. Owner decision on #525 was (a) delete rather than (b) build the process-wide watchdog that would consume them. The driver-side implementations are in git history and were mostly reusable; what was always missing is the trigger, and a member nobody calls is how the misleading doc-comment survived this long. Removed from `IDriver`, all 12 driver implementations, and every test stub — plus the support code that existed only to feed them, which is the part a member-only deletion would have left behind as dead weight: - MTConnect's three per-entry byte-weight constants, its cached `ProbeModelDataItemCount`, and `CountDataItems` — a full device-tree walk run on every cache fill to populate a number with no remaining reader. - OpcUaClient's `_discoveredNodeCount` field and the discovery-pass write to it. - MQTT's `ApproxBytesPerAuthoredTag`. Two behavioural notes, neither a change in what the software does: - `AbCipTemplateCache` now has no production reset path. Its only caller was AB CIP's flush, which itself had no caller — so nothing changes except how visible the fact is. Recorded on the type, along with the correction that its comment's claim about a device `ReinitializeAsync` also flushing it was never true. A UDT redefined on a live controller keeps its stale shape until the driver instance is rebuilt. - MTConnect's `/probe` cache is still dropped, by `InvalidateProbeModel()` on an Agent restart. That is now `internal` rather than `private` so the tests which used the flush purely to empty the cache drive the real path instead — they now cover a case that actually occurs in production. Several unrelated doc comments cited `FlushOptionalCachesAsync` as the canonical CAS example and are repointed to it. Tests: the ones that existed only to assert on the removed members are deleted (3 OpcUaClient, 3 Galaxy, 2 MQTT, 1 MTConnect, 1 AbCip). Worth noting one was vacuous — `FlushOptionalCachesAsync_resets_footprint_counter` asserted a footprint of 0 before the flush and 0 after with no discovery in between, so it would have passed against a flush that did nothing at all. Full suite green except 7 failures verified identical on master before and after (4 AbLegacy integration with the compose profile down, 3 Host.IntegrationTests), checked by name rather than assumed. Docs corrected where they described this as live: `DriverLifecycle.md` (which carried the #525 placeholder), `drivers/AbCip.md`, `drivers/AbLegacy.md`, and `v2/driver-stability.md` — whose "polls this every 30 s" + per-driver `DriverConfig` memory budget were never built as written and are now banner-ed as design intent rather than description. Claude-Session: https://claude.ai/code/session_015p7wGqy3YpZNCpDzTpGMKo --- docs/DriverLifecycle.md | 30 +++- docs/drivers/AbCip.md | 2 +- docs/drivers/AbLegacy.md | 2 +- docs/v2/driver-stability.md | 2 + .../IDriver.cs | 23 +-- .../AbCipDriver.cs | 9 -- .../AbCipTemplateCache.cs | 13 +- .../AbLegacyDriver.cs | 5 - .../CalculationDriver.cs | 10 -- .../FocasDriver.cs | 4 - .../GalaxyDriver.cs | 16 -- .../MTConnectDriver.cs | 143 ++++-------------- .../ModbusDriver.cs | 4 - .../MqttDriver.cs | 27 ---- .../OpcUaClientDriver.cs | 26 ---- .../ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs | 8 - .../ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlDriver.cs | 8 - .../TwinCATDriver.cs | 12 -- .../Drivers/DriverHostActor.cs | 4 - .../Browsing/DiscoveryDriverBrowserFakes.cs | 4 - .../DriverHostTests.cs | 13 -- .../GenericDriverNodeManagerTests.cs | 10 -- .../AbCipDriverDiscoveryTests.cs | 12 -- .../AbCipFetchUdtShapeTests.cs | 9 +- .../GalaxyDriverFactoryTests.cs | 3 - .../GalaxyDriverInfrastructureTests.cs | 51 +------ .../MTConnectDiscoverTests.cs | 22 +-- .../MTConnectDriverLifecycleTests.cs | 58 +++---- .../MTConnectHostAndRediscoverTests.cs | 9 +- .../MqttDriverDiscoveryTests.cs | 26 ---- ...pcUaClientMediumFindingsRegressionTests.cs | 39 +---- .../Fakes/FakeReconnectDriverFactory.cs | 10 -- ...iverHostActorNativeAlarmAckRoutingTests.cs | 4 - .../DriverHostActorPrimaryGateTests.cs | 2 - .../Drivers/DriverHostActorReconcileTests.cs | 4 - .../DriverHostActorWriteRoutingTests.cs | 4 - .../DriverInstanceActorHostStatusTests.cs | 5 - .../DriverInstanceActorNativeAlarmTests.cs | 4 - ...iverInstanceActorRediscoverySignalTests.cs | 5 - ...riverInstanceActorResilienceWiringTests.cs | 2 - ...iverInstanceActorWriteAndSubscribeTests.cs | 4 - .../Drivers/StubDrivers.cs | 5 - 42 files changed, 130 insertions(+), 523 deletions(-) diff --git a/docs/DriverLifecycle.md b/docs/DriverLifecycle.md index 16e9c496..5bfd6186 100644 --- a/docs/DriverLifecycle.md +++ b/docs/DriverLifecycle.md @@ -136,15 +136,31 @@ the real per-capability timeout / retry / breaker policies, resolved via Every driver runs Tier A because no factory passes a tier, so today that means one uniform policy set. -⚠️ Two `IDriver` members are now **consumerless**: `GetMemoryFootprint()` and -`FlushOptionalCachesAsync()` existed to feed `MemoryTracking`. All 12 drivers -still implement them and nothing calls them. They were left in place rather -than swept, since removing them touches every driver and every test stub — -tracked as Gitea **#525**. +`IDriver` **no longer carries `GetMemoryFootprint()` or +`FlushOptionalCachesAsync()`** (Gitea **#525**). They existed to feed +`MemoryTracking`, deleted with the rest of the driver-tier recycle machinery in +**#522**; after that nothing called them, and their doc-comments still claimed +Core polled the footprint every 30 s, which had not been true since v3. The +sweep removed them from the interface, all 12 drivers, and every test stub, +along with the support code that existed only to feed them — MTConnect's +per-entry byte weights and its cached data-item count, OpcUaClient's +`_discoveredNodeCount`, and MQTT's per-tag byte estimate. + +Two consequences worth knowing: + +- `AbCipTemplateCache` now has **no production reset path**. Its only caller was + AB CIP's flush — which itself had no caller, so nothing has actually changed + in behaviour, only in how visible the fact is. A UDT redefined on a live + controller keeps its stale shape until the driver instance is rebuilt. +- MTConnect's `/probe` cache is still dropped, by `InvalidateProbeModel()` on an + Agent restart. The tests that used to reach it through the flush now drive + that path instead, so they cover a case that actually occurs. If per-driver memory protection is wanted again, build it for the architecture -as it is now: a process-wide watchdog, not a per-driver tier whose only remedy -requires a supervisor that cannot exist in-process. +as it is now: a process-wide watchdog that can ask drivers to flush, not a +per-driver tier whose only remedy requires a supervisor that cannot exist +in-process. The deleted per-driver implementations are in git history and were +mostly reusable — what was always missing is the trigger. --- diff --git a/docs/drivers/AbCip.md b/docs/drivers/AbCip.md index 0a62c406..2f9594a2 100644 --- a/docs/drivers/AbCip.md +++ b/docs/drivers/AbCip.md @@ -168,6 +168,6 @@ UDT-typed tags found in the walk. ## Operational Notes -- **Native heap is invisible to the GC.** `GetMemoryFootprint()` reports CLR allocations only; libplctag's native `Tag` heap does not show up there. Watch whole-process RSS, and use `ReinitializeAsync` (tears down + re-creates every device's libplctag handles) as the remediation for native-heap growth. +- **Native heap is invisible to the GC.** libplctag's native `Tag` heap does not show up in CLR allocation figures. Watch whole-process RSS, and use `ReinitializeAsync` (tears down + re-creates every device's libplctag handles) as the remediation for native-heap growth. (There is no per-driver footprint number to consult: `IDriver.GetMemoryFootprint()` was deleted as unconsumed in Gitea #525.) - **Handle eviction on failure** — a non-zero libplctag status or a transport exception evicts the cached tag runtime so the next read/write re-creates a fresh handle, mirroring the probe loop's recreate-on-failure behaviour. - **Declaration-only UDT grouping is a footgun unless verified** — only enable `EnableDeclarationOnlyUdtGrouping` when every UDT's member declaration order has been hand-verified against the controller's compiled layout. diff --git a/docs/drivers/AbLegacy.md b/docs/drivers/AbLegacy.md index 1241876d..7cbcdfe6 100644 --- a/docs/drivers/AbLegacy.md +++ b/docs/drivers/AbLegacy.md @@ -132,6 +132,6 @@ and the UI authoring flow. ## Operational Notes -- **Native heap is invisible to the GC.** As with AB CIP, `GetMemoryFootprint()` reports CLR allocations only; watch whole-process RSS and use `ReinitializeAsync` to recycle libplctag handles. +- **Native heap is invisible to the GC.** As with AB CIP, libplctag's native heap does not show up in CLR allocation figures; watch whole-process RSS and use `ReinitializeAsync` to recycle libplctag handles. (`IDriver.GetMemoryFootprint()` was deleted as unconsumed in Gitea #525.) - **PCCC reconnect is more expensive than CIP** — legacy PLCs have no connection multiplexing, so the resilience pipeline should use longer backoff than for AB CIP (see [docs/v2/driver-specs.md §4](../v2/driver-specs.md)). - **Single-element addressing today** — a PCCC file is inherently an array (an N7 file is up to 256 words), but the current tag surface addresses one element per tag; range-spanning tags must be enumerated element-by-element until multi-element addressing lands. diff --git a/docs/v2/driver-stability.md b/docs/v2/driver-stability.md index b18c185e..c5be713d 100644 --- a/docs/v2/driver-stability.md +++ b/docs/v2/driver-stability.md @@ -120,6 +120,8 @@ For Tier A/B (in-process) drivers, the "crash" being counted is a driver-instanc In-process drivers cannot be recycled by killing the server process — that would take down every other driver, every session, and the OPC UA endpoint. RSS watchdogs and scheduled recycle therefore do **not** apply to Tier A/B. Instead, each driver instance is monitored at a finer grain: +> ⚠️ **The two bullets below were never built as described, and the surface they name is now gone.** `GetMemoryFootprint()` / `FlushOptionalCachesAsync()` did exist on `IDriver`, but nothing ever polled the footprint on a 30 s cadence in v3, no driver ever declared a cache budget in `DriverConfig`, and the recycle machinery their only consumer belonged to was deleted in Gitea #522. Both members were removed from `IDriver` in Gitea #525. Retained here as the design intent a future process-wide watchdog could revive — not as a description of the system. + - **Per-instance allocation tracking**: drivers expose a `GetMemoryFootprint()` capability returning bytes attributable to their own caches (symbol cache, subscription items, queued operations). The Core polls this every 30 s and logs growth slope per driver instance. - **Soft-limit on cached state**: each driver declares a memory budget for its caches in `DriverConfig`. On breach, the Core asks the driver to flush optional caches (e.g. discard symbol cache, force re-discovery). No process action. - **Escalation rule**: if a driver instance's footprint cannot be bounded by cache flushing — or if growth is in opaque allocations the driver can't account for — that driver is a candidate for **promotion to Tier C**. Process recycle is the only safe leak remediation, and the only way to apply process recycle to a single driver is to give it its own process. diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriver.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriver.cs index 193b2381..02c353fc 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriver.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriver.cs @@ -64,26 +64,5 @@ public interface IDriver /// has a running / not-running distinction worth reporting. /// The driver's current health snapshot. DriverHealth GetHealth(); - - /// - /// Approximate driver-attributable footprint in bytes (caches, queues, symbol tables). - /// ⚠️ Nothing calls this (Gitea #525). Its only consumer was MemoryTracking, - /// deleted with the rest of the driver-tier recycle machinery in Gitea #522 — the doc-comment here - /// used to claim Core polled it every 30 s, which was never true in v3. All 12 drivers still - /// implement it; most return a real number, and returning 0 is equally correct today. Do not - /// read a value from it and act on it without first building a consumer that is actually wired. - /// - /// The approximate memory footprint in bytes. - long GetMemoryFootprint(); - - /// - /// Drop optional caches (symbol cache, browse cache, etc.) to bring footprint back below budget. - /// Required-for-correctness state must NOT be flushed. - /// ⚠️ Nothing calls this (Gitea #525) — same reason as - /// : the memory-pressure path that would have invoked it is - /// gone. - /// - /// Cancellation token for the operation. - /// A task that represents the asynchronous operation. - Task FlushOptionalCachesAsync(CancellationToken cancellationToken); } + diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDriver.cs index 05ad2904..703579d6 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDriver.cs @@ -1017,15 +1017,6 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery, /// public DriverHealth GetHealth() => _health; - /// - public long GetMemoryFootprint() => 0; - - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) - { - _templateCache.Clear(); - return Task.CompletedTask; - } // ---- ITagDiscovery ---- diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipTemplateCache.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipTemplateCache.cs index 719cd87f..81cda88d 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipTemplateCache.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipTemplateCache.cs @@ -4,9 +4,13 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip; /// /// Cache of UDT shape descriptors keyed by (deviceHostAddress, templateInstanceId). -/// Populated on demand during discovery + whole-UDT reads; flushed via -/// and on device -/// ReinitializeAsync. +/// Populated on demand during discovery + whole-UDT reads. +/// Nothing in production clears it. Its only caller was +/// AbCipDriver.FlushOptionalCachesAsync, deleted in Gitea #525 as an unconsumed +/// IDriver member — but that member never had a caller either, so this is not a change in +/// behaviour, only in how visible the fact is. The comment here used to claim a device +/// ReinitializeAsync also flushed it; no such call ever existed. A UDT redefined on a live +/// controller therefore keeps its stale shape until the driver instance is rebuilt. /// /// /// Templates are decoded by (CIP Template Object @@ -34,7 +38,8 @@ public sealed class AbCipTemplateCache public void Put(string deviceHostAddress, uint templateInstanceId, AbCipUdtShape shape) => _shapes[(deviceHostAddress, templateInstanceId)] = shape; - /// Drop every cached shape — called on . + /// Drop every cached shape. Retained as ordinary cache API + the tests' reset seam; + /// see the type remarks for why no production path calls it. public void Clear() => _shapes.Clear(); /// Count of cached shapes — exposed for diagnostics + tests. diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/AbLegacyDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/AbLegacyDriver.cs index faadaafc..65b2d068 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/AbLegacyDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/AbLegacyDriver.cs @@ -229,11 +229,6 @@ public sealed class AbLegacyDriver : IDriver, IReadable, IWritable, ITagDiscover /// public DriverHealth GetHealth() => _health; - /// - public long GetMemoryFootprint() => 0; - - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// /// Gets the device count. diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Calculation/CalculationDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Calculation/CalculationDriver.cs index 2d94ea25..61e505d1 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Calculation/CalculationDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Calculation/CalculationDriver.cs @@ -279,16 +279,6 @@ public sealed class CalculationDriver : IDriver, IDependencyConsumer, ISubscriba ? new DriverHealth(DriverState.Healthy, _lastComputeUtc, null) : new DriverHealth(DriverState.Unknown, _lastComputeUtc, null); - /// - public long GetMemoryFootprint() => 0; - - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) - { - // The compile cache is the only optional cache; dropping it is safe (recompiled on next eval). - _evaluator.ClearCompiledScripts(); - return Task.CompletedTask; - } // ---- IDependencyConsumer ---- diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/FocasDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/FocasDriver.cs index b01ef4a4..ebd68e38 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/FocasDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/FocasDriver.cs @@ -311,10 +311,6 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery, /// public DriverHealth GetHealth() => Volatile.Read(ref _health); - /// - public long GetMemoryFootprint() => 0; - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// Gets the number of configured devices. internal int DeviceCount => _devices.Count; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs index f77d55f6..dd9e7225 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs @@ -680,22 +680,6 @@ public sealed class GalaxyDriver /// public IReadOnlyList GetHostStatuses() => _hostStatuses.Snapshot(); - /// - public long GetMemoryFootprint() - { - // Estimated footprint: 64 bytes × tracked item handles (one gw subscription entry - // per bound tag) + 256 bytes × tracked driver subscriptions (registry overhead per - // OPC UA monitored item). Returns 0 when no subscriptions are active. These - // constants are conservative — a 50k-tag set occupies ~3 MB and registers clearly - // with the server's cache-flush heuristic. - const long BytesPerItemHandle = 64L; // TagBinding + reverse-map entry - const long BytesPerSubscription = 256L; // SubscriptionEntry overhead - return (_subscriptions.TrackedItemHandleCount * BytesPerItemHandle) - + (_subscriptions.TrackedSubscriptionCount * BytesPerSubscription); - } - - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; // ===== ITagDiscovery (PR 4.1) ===== diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriver.cs index 53df61f5..e811179c 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDriver.cs @@ -62,21 +62,6 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.MTConnect; /// public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDiscovery, IHostConnectivityProbe, IRediscoverable { - /// - /// Coarse per-entry byte weights behind . The contract asks - /// for an approximate driver-attributable footprint that Core polls every 30 s to - /// decide whether to ask for a flush, so an order-of-magnitude estimate from the two things - /// that actually scale with a plant (declared data items, live observations) is the right - /// precision. Measuring real retained size would need a heap walk per poll. - /// - private const int ProbeModelBytesPerDataItem = 512; - - /// Estimated retained bytes per live dataItemId → snapshot entry. - private const int IndexBytesPerEntry = 256; - - /// Estimated retained bytes per authored . - private const int TagBytesPerEntry = 256; - /// /// The Agent could not be reached, answered unusably, or blew the per-call deadline. Distinct /// from the index's BadNoCommunication, which means the Agent answered and said the @@ -323,15 +308,14 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis /// /// /// - /// Why one field instead of three. These three values are read outside - /// — by , by - /// (Task 12's browse), and by - /// — while every write happens under it. As three - /// separate fields they could be observed mid-update: a reader could see a dropped - /// ProbeModel beside a stale non-zero data-item count and report a footprint for - /// a cache that no longer exists, or pair one initialize's client with the previous - /// one's model. Packing them makes every observation internally consistent by - /// construction, with no lock on the read path. + /// Why one field instead of several. These values are read outside + /// — by and by + /// (Task 12's browse) — while every write + /// happens under it. As separate fields they could be observed mid-update, pairing one + /// initialize's client with the previous one's model. Packing them makes every + /// observation internally consistent by construction, with no lock on the read path. + /// (A third value, a cached data-item count, lived here until Gitea #525 deleted its + /// only reader — GetMemoryFootprint — along with the rest of that dead surface.) /// /// /// Why not simply take the lock in those readers. Both readers await the @@ -423,8 +407,8 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis /// /// The cached /probe device model, or null when it has not been fetched or was - /// dropped by . Prefer - /// , which cannot observe the flushed hole. + /// dropped by on an Agent restart. Prefer + /// , which cannot observe the dropped hole. /// internal MTConnectProbeModel? CachedProbeModel => Volatile.Read(ref _session)?.ProbeModel; @@ -596,58 +580,6 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis /// public DriverHealth GetHealth() => ReadHealth(); - /// - /// - /// Sums the three caches that scale with plant size: the /probe device model (by - /// declared data item), the live observation index (by indexed data item), and the authored - /// tag table. See for why the weights are coarse - /// constants. - /// - public long GetMemoryFootprint() => - ((long)(Volatile.Read(ref _session)?.ProbeModelDataItemCount ?? 0) * ProbeModelBytesPerDataItem) - + ((long)ObservationIndex.Count * IndexBytesPerEntry) - + ((long)Volatile.Read(ref _binding).IndexTags.Count * TagBytesPerEntry); - - /// - /// - /// Drops the /probe device model — the browse cache, which is re-derivable from the - /// Agent at any time — and keeps the observation index, which is required for correctness - /// (it is the read surface, and re-deriving it would mean a full re-prime the caller did not - /// ask for). Flushing cannot wedge the driver: every consumer of the model goes through - /// , which re-fetches and re-caches on a miss. - /// - /// The drop is a single of a whole new - /// , so the model and its data-item count can never be seen - /// out of step (which would skew ), and a flush that - /// races a concurrent teardown or re-initialize is discarded rather than resurrecting - /// the session it was reading. - /// - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) - { - var session = Volatile.Read(ref _session); - if (session?.ProbeModel is null) - { - return Task.CompletedTask; - } - - var flushed = session with { ProbeModel = null, ProbeModelDataItemCount = 0 }; - if (!ReferenceEquals(Interlocked.CompareExchange(ref _session, flushed, session), session)) - { - // Someone swapped the session while we were building the flushed copy — their state is - // newer than ours, and publishing would undo it. Whatever they installed is either a - // teardown (no cache to flush) or a fresh initialize (a cache the operator's budget - // breach never saw), so dropping this flush is correct, not merely safe. - return Task.CompletedTask; - } - - _logger.LogInformation( - "MTConnect driver {DriverInstanceId} flushed its cached /probe device model ({DataItemCount} data items); it is re-fetched on the next browse.", - _driverInstanceId, session.ProbeModelDataItemCount); - - return Task.CompletedTask; - } - /// /// /// @@ -963,9 +895,9 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis /// /// /// The model comes from , never from - /// . may have - /// dropped the cache under memory pressure; reading the field directly would make a - /// flushed driver browse as an Agent with no data items, permanently. + /// . may have + /// dropped the cache after an Agent restart; reading the field directly would make such + /// a driver browse as an Agent with no data items, permanently. /// /// /// This throws, and that is the opposite of 's posture on @@ -1250,8 +1182,7 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis /// /// Publishing the answer into is the opposite /// mistake: it would put a second writer on a field the lifecycle owns under a - /// compare-and-swap, and would silently re-warm a cache that - /// — or an Agent restart, see + /// compare-and-swap, and would silently re-warm a cache that an Agent restart — see /// — deliberately dropped. The parsed model is /// discarded; only the fact that a request completed is used. /// @@ -1473,9 +1404,9 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis /// , which reads /// ; with the cache still warm that call answers /// from a model describing a process that no longer exists, and the "rediscovery" would - /// diff the stale tree against itself and change nothing. Dropped through the same - /// compare-and-swap uses, so a lifecycle change - /// landing mid-drop wins rather than being undone. + /// diff the stale tree against itself and change nothing. Dropped through a + /// compare-and-swap, so a lifecycle change landing mid-drop wins rather than being + /// undone. /// /// /// Once per change, not once per chunk. Every chunk after a restart carries the new @@ -1514,12 +1445,16 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis } /// - /// Drops the cached /probe device model so the next consumer re-fetches it. Same - /// compare-and-swap discipline as — retried, because - /// unlike a flush this drop is a correctness requirement rather than a courtesy: losing the - /// race must not leave a model from a dead Agent installed. + /// Drops the cached /probe device model so the next consumer re-fetches it. + /// internal rather than private so the discovery tests can exercise the real + /// drop path; production calls it from . + /// Compare-and-swap, and retried — this drop is a correctness requirement rather + /// than a courtesy: losing the race must not leave a model from a dead Agent installed. + /// (The courtesy version, FlushOptionalCachesAsync, took the same lock-free + /// approach but gave up on a lost race; it went with the rest of the unconsumed + /// memory-pressure surface in Gitea #525.) /// - private void InvalidateProbeModel() + internal void InvalidateProbeModel() { while (true) { @@ -1529,7 +1464,7 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis return; } - var dropped = session with { ProbeModel = null, ProbeModelDataItemCount = 0 }; + var dropped = session with { ProbeModel = null }; if (ReferenceEquals(Interlocked.CompareExchange(ref _session, dropped, session), session)) { return; @@ -2191,7 +2126,7 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis /// sequence that had just been rejected. /// /// - /// Compare-and-swap for the same reason uses one: + /// Compare-and-swap for the same reason uses one: /// a lifecycle change landing mid-update has newer state, so this write is dropped /// rather than replayed over it. /// @@ -2221,7 +2156,7 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis /// The /probe device model: the cached copy when warm, otherwise a fresh /// deadline-bounded /probe that re-populates the cache. This is the accessor every /// model consumer (Task 12's DiscoverAsync) must use, so that - /// can never leave browse permanently disabled. + /// can never leave browse permanently disabled. /// /// /// Lock-free by design — see the remarks for why this does not take @@ -2261,7 +2196,7 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis } // Publish only onto the session we actually fetched against. - var recached = session with { ProbeModel = model, ProbeModelDataItemCount = CountDataItems(model) }; + var recached = session with { ProbeModel = model }; _ = Interlocked.CompareExchange(ref _session, recached, session); return model; @@ -2416,7 +2351,7 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis // cache, and the pump's opening cursor together or none of them. Volatile.Write( ref _session, - new AgentSession(client, probe, CountDataItems(probe), current.InstanceId, current.NextSequence)); + new AgentSession(client, probe, current.InstanceId, current.NextSequence)); var index = new MTConnectObservationIndex(binding.IndexTags); index.Apply(current); @@ -2738,13 +2673,6 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis || current.SampleIntervalMs != incoming.SampleIntervalMs || current.SampleCount != incoming.SampleCount; - /// - /// Counts every data item the device model declares. Done once, when the model is cached, - /// rather than re-walking the tree on every 30 s poll. - /// - private static int CountDataItems(MTConnectProbeModel model) => - model.Devices.Sum(d => d.AllDataItems().Count()); - /// /// Runs one unary agent call under a deadline linked to the caller's token. Belt-and-braces /// over the production client's own per-call deadline: the seam does not oblige an @@ -3196,17 +3124,13 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis /// /// The live agent client and the /probe cache derived from it, as one immutable /// unit. Immutable so that publishing a change is a single reference swap — see the - /// remarks for why the three values must move together. + /// remarks for why these values must move together. /// /// The agent client this session owns and will dispose on teardown. /// - /// The cached device model, or null once has + /// The cached device model, or null once has /// dropped it. Never null at the moment a session is first published. /// - /// - /// Data items declared by , counted once at cache time and - /// always 0 when it is null. - /// /// /// The Agent's instanceId as last observed. Updated in place (by /// ) when the pump sees the Agent restart, so the @@ -3220,7 +3144,6 @@ public sealed class MTConnectDriver : IDriver, IReadable, ISubscribable, ITagDis private sealed record AgentSession( IMTConnectAgentClient Client, MTConnectProbeModel? ProbeModel, - int ProbeModelDataItemCount, long InstanceId, long NextSequence); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs index 3d972795..08b845cd 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs @@ -282,10 +282,6 @@ public sealed class ModbusDriver /// Barrier-protected publish of a new _health snapshot. /// private void WriteHealth(DriverHealth value) => Volatile.Write(ref _health, value); - /// - public long GetMemoryFootprint() => 0; - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; // ---- ITagDiscovery ---- diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Mqtt/MqttDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Mqtt/MqttDriver.cs index e8826724..7c17e2fc 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Mqtt/MqttDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Mqtt/MqttDriver.cs @@ -88,15 +88,6 @@ public sealed class MqttDriver /// The publish failed, or the driver has no live broker connection to publish through. private const uint StatusBadCommunicationError = 0x80050000u; - /// - /// Rough per-tag driver-attributable cost: the parsed (five - /// strings — RawPath, topic, JSONPath and the record header) plus its cached - /// and the two dictionary entries that index it. An estimate - /// by construction — GetMemoryFootprint exists to drive a cache-budget decision, not - /// to be exact. - /// - private const long ApproxBytesPerAuthoredTag = 512; - /// /// The single folder streams every variable under — and therefore /// the only subtree path a rediscovery ScopeHint can name truthfully. See @@ -429,24 +420,6 @@ public sealed class MqttDriver }; } - /// - /// Approximate driver-attributable footprint: the authored definition table and the - /// last-value cache that indexes it. Both are sized by the authored tag count, which is the - /// only thing a deployment can grow. - /// - /// The approximate memory footprint in bytes. - public long GetMemoryFootprint() => _authoredRawPaths.Count * ApproxBytesPerAuthoredTag; - - /// - /// No-op, deliberately. This driver holds no optional cache: the authored table is the - /// address space itself, and the last-value cache is — MQTT is - /// subscribe-first, so a dropped value is not re-fetchable, and flushing it would turn every - /// node Bad until its topic next published, which for a slow-changing tag can be never. - /// Birth/alias state (Sparkplug, P2) is correctness state for the same reason. - /// - /// Unused; present for the shape. - /// A completed task. - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; // ---- ITagDiscovery ---- diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs index 26dece00..10730d77 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs @@ -129,12 +129,6 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// The that owns . private CertificateValidator? _certValidatorRef; /// - /// Approximate count of discovered nodes (folders + variables). Updated by - /// and used to report a non-zero - /// to the Core allocation-slope detector. - /// - private volatile int _discoveredNodeCount; - /// /// SDK-provided reconnect handler that owns the retry loop + session-transfer machinery /// when the session's keep-alive channel reports a bad status. Null outside the /// reconnecting window; constructed lazily inside the keep-alive handler. Guarded by @@ -644,23 +638,6 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// public DriverHealth GetHealth() => _health; - /// - // Each discovered node (folder or variable) contributes ~512 bytes to cover the - // DriverAttributeInfo record, the browse-name string, and the stable nsu= reference string - // stored in the address-space builder. The real number depends on string length + box sizes; - // the constant is conservative enough that a 10k-node remote server reports ~5 MB — well - // within the budget and detectable by the Core slope alarm. - public long GetMemoryFootprint() => _discoveredNodeCount * 512L; - - /// - // The OPC UA Client driver holds no independently-flushable cache beyond what the - // address-space builder retains — a flush here resets the discovered-node footprint - // counter and signals Core that re-discovery will rebuild it cleanly from the remote server. - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) - { - _discoveredNodeCount = 0; - return Task.CompletedTask; - } // ---- IReadable ---- @@ -926,9 +903,6 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit await EnrichAndRegisterVariablesAsync(session, pendingVariables, cancellationToken) .ConfigureAwait(false); - // Update the footprint counter so GetMemoryFootprint() returns a real estimate - // after each discovery pass. - _discoveredNodeCount = discovered; } finally { _gate.Release(); } } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs index c7949bb4..02c3e8d7 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs @@ -328,9 +328,6 @@ public sealed class S7Driver /// public DriverHealth GetHealth() => _health; - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; - /// /// True when carries a real config body. The /// bootstrapper always passes a populated document; some unit tests pass "{}" or @@ -481,11 +478,6 @@ public sealed class S7Driver /// private static readonly HashSet UnimplementedDataTypes = new(); - /// - // The Plc instance + one 240-960 byte PDU buffer is under 4 KB; returns 0 because the - // IDriver contract asks for a driver-attributable growth number and S7.Net doesn't expose one. - public long GetMemoryFootprint() => 0; - // ---- IReadable ---- /// diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlDriver.cs index c69de1ec..f837285d 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlDriver.cs @@ -397,14 +397,6 @@ public sealed class SqlDriver /// public DriverHealth GetHealth() => ReadHealth(); - /// No caches, no symbol table, no connection between polls — the footprint is the tag table. - /// Always zero. - public long GetMemoryFootprint() => 0; - - /// Nothing optional to flush: the authored table is required for correctness. - /// Cancellation token for the operation. - /// A completed task. - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; // ---- ITagDiscovery ---- diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs index 4acdbdf2..e041066f 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs @@ -208,18 +208,6 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery /// public DriverHealth GetHealth() => _health; - /// - // This driver holds no flushable symbol cache — BrowseSymbolsAsync streams and discards; the - // footprint reflects live allocations only: ~256 bytes per pre-declared tag (tag-definition - // record + dictionary overhead) and ~512 bytes per active native subscription. - public long GetMemoryFootprint() => - (_tagsByRawPath.Count * 256L) + (_nativeSubs.Count * 512L); - - /// - // No flushable cache exists in this driver — the symbol table is streamed fresh on every - // DiscoverAsync call. This is a no-op but is deliberately present so Core's cache-budget - // enforcement sees a compliant Tier-A driver. - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// Gets the count of configured devices. internal int DeviceCount => _devices.Count; diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs index 6d257dbd..87cd2fdc 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs @@ -2238,10 +2238,6 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers public Task ShutdownAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// public DriverHealth GetHealth() => new(DriverState.Healthy, DateTime.UtcNow, LastError: null); - /// - public long GetMemoryFootprint() => 0; - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; } private void HandleRestartDriver(RestartDriver msg) diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Commons.Tests/Browsing/DiscoveryDriverBrowserFakes.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Commons.Tests/Browsing/DiscoveryDriverBrowserFakes.cs index 78f45228..d8fba909 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Commons.Tests/Browsing/DiscoveryDriverBrowserFakes.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Commons.Tests/Browsing/DiscoveryDriverBrowserFakes.cs @@ -72,8 +72,6 @@ internal sealed class FakeDiscoveryDriver : IDriver, ITagDiscovery public Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken) => Task.CompletedTask; public DriverHealth GetHealth() => new(DriverState.Healthy, null, null); - public long GetMemoryFootprint() => 0; - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; } /// An IDriver that is NOT ITagDiscovery — exercises the not-discovery-capable gate. @@ -92,8 +90,6 @@ internal sealed class FakeNonDiscoveryDriver(string driverType = "Flat", string return Task.CompletedTask; } public DriverHealth GetHealth() => new(DriverState.Healthy, null, null); - public long GetMemoryFootprint() => 0; - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; } /// Configurable IDriverFactory: delegates construction to a supplied func and records what it made. diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/DriverHostTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/DriverHostTests.cs index 1f6ff319..98586ed2 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/DriverHostTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/DriverHostTests.cs @@ -45,12 +45,6 @@ public sealed class DriverHostTests public DriverHealth GetHealth() => new(Initialized ? DriverState.Healthy : DriverState.Unknown, null, null); - /// Gets the memory footprint of the driver. - public long GetMemoryFootprint() => 0; - - /// Flushes optional caches asynchronously. - /// The cancellation token. - public Task FlushOptionalCachesAsync(CancellationToken ct) => Task.CompletedTask; } /// Verifies that registering a driver initializes it and tracks its health. @@ -248,13 +242,6 @@ public sealed class DriverHostTests /// Gets the current health status of the driver. public DriverHealth GetHealth() => new(DriverState.Healthy, null, null); - /// Gets the memory footprint of the driver. - public long GetMemoryFootprint() => 0; - - /// Flushes optional caches asynchronously. - /// The cancellation token. - public Task FlushOptionalCachesAsync(CancellationToken ct) => Task.CompletedTask; - private static readonly TaskCompletionSource CompletedTcs = MakeCompleted(); private static TaskCompletionSource MakeCompleted() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/GenericDriverNodeManagerTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/GenericDriverNodeManagerTests.cs index f2d71510..3ea21a63 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/GenericDriverNodeManagerTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/GenericDriverNodeManagerTests.cs @@ -187,11 +187,6 @@ public sealed class GenericDriverNodeManagerTests public Task ShutdownAsync(CancellationToken _) => Task.CompletedTask; /// Gets the current health status of the driver. public DriverHealth GetHealth() => new(DriverState.Healthy, null, null); - /// Gets the memory footprint of the driver. - public long GetMemoryFootprint() => 0; - /// Flushes optional caches in the driver. - /// Cancellation token (unused in test double). - public Task FlushOptionalCachesAsync(CancellationToken _) => Task.CompletedTask; /// Discovers the address space by throwing an exception. /// The builder used to construct the address space. @@ -224,11 +219,6 @@ public sealed class GenericDriverNodeManagerTests public Task ShutdownAsync(CancellationToken ct) => Task.CompletedTask; /// Gets the current health status of the driver. public DriverHealth GetHealth() => new(DriverState.Healthy, DateTime.UtcNow, null); - /// Gets the memory footprint of the driver. - public long GetMemoryFootprint() => 0; - /// Flushes optional caches in the driver. - /// Cancellation token. - public Task FlushOptionalCachesAsync(CancellationToken ct) => Task.CompletedTask; /// Discovers the address space and registers alarm conditions. /// The builder used to construct the address space. diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverDiscoveryTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverDiscoveryTests.cs index 8e860f9e..5be7525d 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverDiscoveryTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverDiscoveryTests.cs @@ -573,18 +573,6 @@ public sealed class AbCipDriverDiscoveryTests cache.Count.ShouldBe(0); } - /// Verifies that FlushOptionalCachesAsync clears the template cache. - [Fact] - public async Task FlushOptionalCachesAsync_clears_template_cache() - { - var drv = new AbCipDriver(new AbCipDriverOptions(), "drv-1"); - drv.TemplateCache.Put("dev", 1, new AbCipUdtShape("T", 4, [])); - drv.TemplateCache.Count.ShouldBe(1); - - await drv.FlushOptionalCachesAsync(CancellationToken.None); - drv.TemplateCache.Count.ShouldBe(0); - } - // ---- helpers ---- /// Test implementation of IAddressSpaceBuilder that records calls. diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipFetchUdtShapeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipFetchUdtShapeTests.cs index 2d0554a8..f56e5893 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipFetchUdtShapeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipFetchUdtShapeTests.cs @@ -210,9 +210,9 @@ public sealed class AbCipFetchUdtShapeTests shape.ShouldBeNull(); } - /// Verifies that FlushOptionalCachesAsync empties the template cache. + /// Verifies that a cleared template cache is re-populated from the network. [Fact] - public async Task FlushOptionalCachesAsync_empties_template_cache() + public async Task A_cleared_template_cache_refetches_from_the_controller() { var factory = new FakeTemplateReaderFactory { @@ -230,7 +230,10 @@ public sealed class AbCipFetchUdtShapeTests _ = await InvokeFetch(drv, "ab://10.0.0.5/1,0", 99); drv.TemplateCache.Count.ShouldBe(1); - await drv.FlushOptionalCachesAsync(CancellationToken.None); + // Gitea #525 deleted AbCipDriver.FlushOptionalCachesAsync, which is what used to clear this; + // the cache's own Clear() is now the only reset, and nothing in production calls it (see the + // AbCipTemplateCache remarks). What this test still pins is the cache-miss path below. + drv.TemplateCache.Clear(); drv.TemplateCache.Count.ShouldBe(0); // Next fetch hits the network again. diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverFactoryTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverFactoryTests.cs index 2e3f6a3f..875dde8d 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverFactoryTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverFactoryTests.cs @@ -174,9 +174,6 @@ public sealed class GalaxyDriverFactoryTests await driver.ShutdownAsync(CancellationToken.None); driver.GetHealth().State.ShouldBe(DriverState.Unknown); - - driver.GetMemoryFootprint().ShouldBe(0); - await driver.FlushOptionalCachesAsync(CancellationToken.None); // no-op shouldn't throw } /// Verifies that reinitializing with equivalent config refreshes health. diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverInfrastructureTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverInfrastructureTests.cs index f135218c..b455ebe2 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverInfrastructureTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverInfrastructureTests.cs @@ -10,8 +10,9 @@ using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime; namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests; /// -/// Driver-level infrastructure tests for findings Driver.Galaxy-007 (dispose-CTS gate) -/// and Driver.Galaxy-011 (GetMemoryFootprint non-zero estimate). +/// Driver-level infrastructure tests for finding Driver.Galaxy-007 (dispose-CTS gate). +/// Driver.Galaxy-011 (a non-zero GetMemoryFootprint estimate) was also covered here until +/// Gitea #525 deleted that member from IDriver as unconsumed surface. /// public sealed class GalaxyDriverInfrastructureTests { @@ -22,47 +23,11 @@ public sealed class GalaxyDriverInfrastructureTests new GalaxyReconnectOptions()); // ===== Driver.Galaxy-011 regression: GetMemoryFootprint reflects registry size ===== - - /// Verifies that memory footprint is zero when there are no subscriptions. - [Fact] - public void GetMemoryFootprint_IsZeroWhenNoSubscriptions() - { - var sub = new NoOpSubscriber(); - using var driver = new GalaxyDriver("drv-1", Opts(), null, null, null, sub); - - driver.GetMemoryFootprint().ShouldBe(0L); - } - - /// Verifies that memory footprint is nonzero after subscriptions are active. - [Fact] - public async Task GetMemoryFootprint_IsNonZeroAfterSubscribe() - { - // When subscriptions are active the footprint estimate must be > 0 so the - // server's memory-pressure mechanism sees the Galaxy driver as a participant. - var sub = new NoOpSubscriber(); - using var driver = new GalaxyDriver("drv-1", Opts(), null, null, null, sub); - - await driver.SubscribeAsync(["Tag.A", "Tag.B"], TimeSpan.Zero, CancellationToken.None); - - driver.GetMemoryFootprint().ShouldBeGreaterThan(0L); - } - - /// Verifies that memory footprint decreases after unsubscribing. - [Fact] - public async Task GetMemoryFootprint_DecreasesAfterUnsubscribe() - { - var sub = new NoOpSubscriber(); - using var driver = new GalaxyDriver("drv-1", Opts(), null, null, null, sub); - - var handle = await driver.SubscribeAsync(["Tag.A", "Tag.B"], TimeSpan.Zero, CancellationToken.None); - var afterSubscribe = driver.GetMemoryFootprint(); - afterSubscribe.ShouldBeGreaterThan(0L); - - await driver.UnsubscribeAsync(handle, CancellationToken.None); - var afterUnsubscribe = driver.GetMemoryFootprint(); - afterUnsubscribe.ShouldBeLessThan(afterSubscribe, - "footprint must decrease when subscriptions are removed"); - } + // + // Three tests lived here asserting the footprint tracked the subscription registry. The member + // was deleted from IDriver in Gitea #525 as unconsumed surface — nothing ever read the number — + // and they went with it. The registry sizes they leaned on are still covered directly by the + // subscribe/unsubscribe tests below. // ===== Driver.Galaxy-007 regression: Dispose cancels the dispose CTS ===== diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectDiscoverTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectDiscoverTests.cs index 6e02e552..ea6eef6a 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectDiscoverTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectDiscoverTests.cs @@ -585,17 +585,21 @@ public sealed class MTConnectDiscoverTests } /// - /// …and after has dropped that cache, - /// discovery re-fetches and still streams the full tree. This is the whole reason discovery - /// must go through GetOrFetchProbeModelAsync rather than reading the cached field: a - /// memory-budget flush must never leave browse permanently answering "this agent has no data - /// items". + /// …and after the cache has been dropped, discovery re-fetches and still streams the full + /// tree. This is the whole reason discovery must go through GetOrFetchProbeModelAsync + /// rather than reading the cached field: a drop must never leave browse permanently + /// answering "this agent has no data items". + /// Drives — the Agent-restart drop, + /// which is the only thing that empties this cache in production. It used to drive + /// FlushOptionalCachesAsync, deleted with the rest of the unconsumed memory-pressure + /// surface in Gitea #525; that path had no production caller, so this now tests a case that + /// actually occurs. /// [Fact] - public async Task Discover_refetches_the_probe_model_after_a_cache_flush() + public async Task Discover_refetches_the_probe_model_after_the_cache_is_dropped() { var (driver, client) = await InitializedDriverAsync(); - await driver.FlushOptionalCachesAsync(TestContext.Current.CancellationToken); + driver.InvalidateProbeModel(); driver.CachedProbeModel.ShouldBeNull(); var cap = await DiscoverAsync(driver); @@ -689,7 +693,7 @@ public sealed class MTConnectDiscoverTests public async Task Discover_propagates_an_agent_probe_failure_rather_than_streaming_an_empty_tree() { var (driver, client) = await InitializedDriverAsync(); - await driver.FlushOptionalCachesAsync(TestContext.Current.CancellationToken); + driver.InvalidateProbeModel(); client.ProbeFailure = new HttpRequestException("agent down"); var builder = new CapturingBuilder(); @@ -705,7 +709,7 @@ public sealed class MTConnectDiscoverTests public async Task Discover_propagates_caller_cancellation() { var (driver, _) = await InitializedDriverAsync(); - await driver.FlushOptionalCachesAsync(TestContext.Current.CancellationToken); + driver.InvalidateProbeModel(); using var cts = new CancellationTokenSource(); await cts.CancelAsync(); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectDriverLifecycleTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectDriverLifecycleTests.cs index b413261d..ef3dddfe 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectDriverLifecycleTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectDriverLifecycleTests.cs @@ -590,31 +590,21 @@ public sealed class MTConnectDriverLifecycleTests driver.GetHealth().State.ShouldBe(DriverState.Unknown); } - // ---- footprint + flush ---- + // ---- dropping the probe cache ---- + // + // These used to drive FlushOptionalCachesAsync. Gitea #525 deleted it as an unconsumed IDriver + // member, along with GetMemoryFootprint and the footprint test that only existed to assert on + // it. What survives is InvalidateProbeModel — the Agent-restart drop, which is the only thing + // that empties this cache in production, and which has the same retired-session guard and the + // same "browse must not be left permanently empty" obligation. [Fact] - public async Task Memory_footprint_grows_when_initialize_populates_the_caches() + public void Dropping_the_probe_cache_keeps_the_observation_index() { - var (driver, _) = NewDriver(); + var (driver, _) = InitializedDriverAsync().GetAwaiter().GetResult(); - var before = driver.GetMemoryFootprint(); - await driver.InitializeAsync("{}", CancellationToken.None); - var after = driver.GetMemoryFootprint(); + driver.InvalidateProbeModel(); - // Before init the only config-attributable state is the two authored tags. - before.ShouldBeLessThan(after); - after.ShouldBeGreaterThan(0); - } - - [Fact] - public async Task Flush_drops_the_probe_cache_and_keeps_the_observation_index() - { - var (driver, _) = await InitializedDriverAsync(); - var before = driver.GetMemoryFootprint(); - - await driver.FlushOptionalCachesAsync(CancellationToken.None); - - driver.GetMemoryFootprint().ShouldBeLessThan(before); driver.CachedProbeModel.ShouldBeNull(); // The index is required for correctness (it IS the read surface) — never flushable. @@ -624,12 +614,12 @@ public sealed class MTConnectDriverLifecycleTests } [Fact] - public async Task Flush_does_not_wedge_the_driver_the_probe_model_is_refetched_on_demand() + public async Task Dropping_the_cache_does_not_wedge_the_driver_the_probe_model_is_refetched_on_demand() { - // Task 12's DiscoverAsync reads the probe model. If flushing left no way back to it, a - // cache-budget breach would permanently disable browse on that driver instance. + // Task 12's DiscoverAsync reads the probe model. If dropping it left no way back, an Agent + // restart would permanently disable browse on that driver instance. var (driver, client) = await InitializedDriverAsync(); - await driver.FlushOptionalCachesAsync(CancellationToken.None); + driver.InvalidateProbeModel(); client.ProbeCallCount.ShouldBe(1); @@ -679,7 +669,7 @@ public sealed class MTConnectDriverLifecycleTests // it captured. What must not happen is a raw ObjectDisposedException escaping into browse — // the caller already handles "not connected" and should have exactly one failure mode. var (driver, client) = await InitializedDriverAsync(); - await driver.FlushOptionalCachesAsync(CancellationToken.None); + driver.InvalidateProbeModel(); // Held locally: the gate is one-shot, so the client has already cleared its own reference by // the time the request is parked on it. @@ -712,7 +702,7 @@ public sealed class MTConnectDriverLifecycleTests }); await driver.InitializeAsync("{}", CancellationToken.None); - await driver.FlushOptionalCachesAsync(CancellationToken.None); + driver.InvalidateProbeModel(); var client = built[0]; var staleModel = client.Probe; @@ -742,22 +732,22 @@ public sealed class MTConnectDriverLifecycleTests } [Fact] - public async Task Flush_after_shutdown_is_a_no_op_and_does_not_resurrect_the_session() + public async Task Dropping_the_cache_after_shutdown_is_a_no_op_and_does_not_resurrect_the_session() { - // Core polls the footprint and asks for a flush on its own schedule, so a flush arriving - // after the driver was torn down is ordinary, not exotic. It must observe the retired - // session and do nothing — publishing a "flushed" copy of it would reinstate a session - // holding an already-disposed client for every later caller. + // The /sample pump can see an Agent restart at any moment, so a drop arriving after the + // driver was torn down is ordinary, not exotic. It must observe the retired session and do + // nothing — publishing a "dropped" copy of it would reinstate a session holding an + // already-disposed client for every later caller. // - // NOTE: this pins the retired-session guard, NOT the CompareExchange beneath it. Flush has - // no await point, so nothing can be interleaved between its read and its publish from a + // NOTE: this pins the retired-session guard, NOT the CompareExchange beneath it. The drop + // has no await point, so nothing can be interleaved between its read and its publish from a // single-threaded test; the CAS is defence-in-depth against a genuinely concurrent // lifecycle call and is deliberately claimed as such rather than as tested behaviour. The // fetch-side CAS, which does have an await point, is covered by the test above. var (driver, client) = await InitializedDriverAsync(); await driver.ShutdownAsync(CancellationToken.None); - await driver.FlushOptionalCachesAsync(CancellationToken.None); + driver.InvalidateProbeModel(); driver.CachedProbeModel.ShouldBeNull(); driver.GetHealth().State.ShouldBe(DriverState.Unknown); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectHostAndRediscoverTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectHostAndRediscoverTests.cs index 0b9e3e1b..fc96b5c3 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectHostAndRediscoverTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectHostAndRediscoverTests.cs @@ -440,8 +440,8 @@ public sealed class MTConnectHostAndRediscoverTests /// /// The probe is a liveness check, never a cache writer. Publishing its answer into /// AgentSession.ProbeModel would put a second writer on the field the lifecycle owns - /// under a compare-and-swap, and would silently re-warm a cache that - /// FlushOptionalCachesAsync (or an Agent restart) deliberately dropped. + /// under a compare-and-swap, and would silently re-warm a cache that an Agent restart + /// deliberately dropped. /// [Fact] public async Task Probe_ticks_never_publish_into_the_session_probe_cache() @@ -457,10 +457,9 @@ public sealed class MTConnectHostAndRediscoverTests await TickAsync(ticker); driver.CachedProbeModel.ShouldBeSameAs(original); - driver.GetMemoryFootprint().ShouldBeGreaterThan(0); - // …and a flushed cache stays flushed until a real consumer asks for it. - await driver.FlushOptionalCachesAsync(Ct); + // …and a dropped cache stays dropped until a real consumer asks for it. + driver.InvalidateProbeModel(); await TickAsync(ticker); driver.CachedProbeModel.ShouldBeNull(); } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Tests/MqttDriverDiscoveryTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Tests/MqttDriverDiscoveryTests.cs index 7a5f28df..ba49a6c1 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Tests/MqttDriverDiscoveryTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Tests/MqttDriverDiscoveryTests.cs @@ -159,24 +159,6 @@ public sealed class MqttDriverDiscoveryTests results.ShouldBeEmpty(); } - /// - /// The falsifiability pin for the cache-flush contract: the last-value cache backs - /// IReadable, so flushing it would silently turn every subscribed node Bad under - /// memory pressure. FlushOptionalCachesAsync must leave it untouched. - /// - [Fact] - public async Task FlushOptionalCachesAsync_LeavesLastValueCacheIntact() - { - var driver = PlainDriver(Tag("Plant/Mqtt/dev1/Temp", "f/t")); - driver.Subscriptions.HandleMessage("f/t", Encoding.UTF8.GetBytes("hot"), retained: false); - - await driver.FlushOptionalCachesAsync(CancellationToken.None); - - var results = await driver.ReadAsync(["Plant/Mqtt/dev1/Temp"], CancellationToken.None); - results[0].StatusCode.ShouldBe(0u); - results[0].Value.ShouldBe("hot"); - } - /// A malformed reinitialize delta must never fault the driver, and must not lose the running config. [Fact] public async Task ReinitializeAsync_MalformedDelta_KeepsRunningConfig_AndDoesNotFault() @@ -334,14 +316,6 @@ public sealed class MqttDriverDiscoveryTests fired.ShouldBe(0); } - /// The footprint tracks the authored table; an empty driver reports no driver-attributable bytes. - [Fact] - public void GetMemoryFootprint_TracksAuthoredTable() - { - PlainDriver().GetMemoryFootprint().ShouldBe(0); - PlainDriver(Tag("Plant/Mqtt/dev1/Temp", "f/t")).GetMemoryFootprint().ShouldBeGreaterThan(0); - } - /// Shutdown on a never-initialized driver is a no-op, not a null-reference. [Fact] public async Task ShutdownAsync_BeforeInitialize_DoesNotThrow() diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientMediumFindingsRegressionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientMediumFindingsRegressionTests.cs index ea8f9221..6da77610 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientMediumFindingsRegressionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientMediumFindingsRegressionTests.cs @@ -13,7 +13,6 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests; /// Driver.OpcUaClient-009 — WriteAsync timeout maps to BadTimeout, not BadCommunicationError /// Driver.OpcUaClient-010 — OPC UA Byte type maps to UInt16, not Int16 /// Driver.OpcUaClient-012 — AutoAcceptCertificates emits a startup warning -/// Driver.OpcUaClient-013 — GetMemoryFootprint and FlushOptionalCachesAsync contract /// Driver.OpcUaClient-015 — key pure-logic paths exposed for future test expansion /// /// @@ -97,39 +96,11 @@ public sealed class OpcUaClientMediumFindingsRegressionTests } // ---- Driver.OpcUaClient-013 ---- - - /// Verifies that GetMemoryFootprint returns zero before discovery. - [Fact] - public void GetMemoryFootprint_returns_zero_before_first_discovery() - { - using var drv = new OpcUaClientDriver(new OpcUaClientDriverOptions(), "opcua-mem-013"); - // Pre-init: no nodes discovered yet, so footprint is 0. - drv.GetMemoryFootprint().ShouldBe(0L); - } - - /// Verifies that FlushOptionalCachesAsync completes without throwing. - [Fact] - public async Task FlushOptionalCachesAsync_completes_without_throwing() - { - // FlushOptionalCachesAsync is a cheap no-op before any discovery. Must not throw. - using var drv = new OpcUaClientDriver(new OpcUaClientDriverOptions(), "opcua-flush-013"); - await Should.NotThrowAsync(async () => - await drv.FlushOptionalCachesAsync(TestContext.Current.CancellationToken)); - } - - /// Verifies that FlushOptionalCachesAsync resets the footprint counter. - [Fact] - public async Task FlushOptionalCachesAsync_resets_footprint_counter() - { - // GetMemoryFootprint uses _discoveredNodeCount * 512. FlushOptionalCachesAsync resets - // _discoveredNodeCount to 0, so a subsequent call to GetMemoryFootprint returns 0. - // We drive this through a complete Discovery pass against a stub — instead, test the - // contract via FlushOptionalCachesAsync clearing a counter we can observe. - using var drv = new OpcUaClientDriver(new OpcUaClientDriverOptions(), "opcua-flush2-013"); - drv.GetMemoryFootprint().ShouldBe(0L, "pre-flush should be 0 (no discovery)"); - await drv.FlushOptionalCachesAsync(TestContext.Current.CancellationToken); - drv.GetMemoryFootprint().ShouldBe(0L, "post-flush should still be 0"); - } + // + // Three tests lived here covering GetMemoryFootprint + FlushOptionalCachesAsync. Both members + // were deleted from IDriver in Gitea #525 as unconsumed surface, and these went with them. Worth + // noting one was vacuous anyway: it asserted a footprint of 0 before the flush and 0 after, with + // no discovery in between, so it would have passed against a flush that did nothing at all. // ---- Driver.OpcUaClient-015: pure-logic paths ---- diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/Fakes/FakeReconnectDriverFactory.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/Fakes/FakeReconnectDriverFactory.cs index ceec98f6..a2ff9911 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/Fakes/FakeReconnectDriverFactory.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/Fakes/FakeReconnectDriverFactory.cs @@ -155,14 +155,4 @@ public sealed class FakeReconnectDriver : IDriver public DriverHealth GetHealth() => _reconnecting ? new DriverHealth(DriverState.Reconnecting, _lastSuccess, null) : new DriverHealth(DriverState.Healthy, _lastSuccess, null); - - /// Returns a zero memory footprint (the fake holds no driver-attributable caches). - /// Always 0. - public long GetMemoryFootprint() => 0; - - /// Flushes optional caches — a no-op (the fake holds none). - /// Cancellation token for the operation. - /// A completed task. - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) - => Task.CompletedTask; } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorNativeAlarmAckRoutingTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorNativeAlarmAckRoutingTests.cs index 65fc6e57..f3d75523 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorNativeAlarmAckRoutingTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorNativeAlarmAckRoutingTests.cs @@ -266,10 +266,6 @@ public sealed class DriverHostActorNativeAlarmAckRoutingTests : RuntimeActorTest public Task ShutdownAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// public DriverHealth GetHealth() => new(DriverState.Healthy, DateTime.UtcNow, LastError: null); - /// - public long GetMemoryFootprint() => 0; - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// public Task SubscribeAlarmsAsync( diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorPrimaryGateTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorPrimaryGateTests.cs index eb0e4e5c..e16e1b7c 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorPrimaryGateTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorPrimaryGateTests.cs @@ -395,8 +395,6 @@ public sealed class DriverHostActorPrimaryGateTests : RuntimeActorTestBase public Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken) => Task.CompletedTask; public Task ShutdownAsync(CancellationToken cancellationToken) => Task.CompletedTask; public DriverHealth GetHealth() => new(DriverState.Healthy, DateTime.UtcNow, LastError: null); - public long GetMemoryFootprint() => 0; - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; public Task> WriteAsync( IReadOnlyList writes, CancellationToken cancellationToken) { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorReconcileTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorReconcileTests.cs index 56a85fb8..c3c0d672 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorReconcileTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorReconcileTests.cs @@ -335,9 +335,5 @@ public sealed class DriverHostActorReconcileTests : RuntimeActorTestBase public Task ShutdownAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// public DriverHealth GetHealth() => new(DriverState.Healthy, DateTime.UtcNow, LastError: null); - /// - public long GetMemoryFootprint() => 0; - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; } } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorWriteRoutingTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorWriteRoutingTests.cs index baf11d1a..f94acd8f 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorWriteRoutingTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorWriteRoutingTests.cs @@ -426,10 +426,6 @@ public sealed class DriverHostActorWriteRoutingTests : RuntimeActorTestBase /// public DriverHealth GetHealth() => new(DriverState.Healthy, DateTime.UtcNow, LastError: null); /// - public long GetMemoryFootprint() => 0; - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; - /// public Task> WriteAsync( IReadOnlyList writes, CancellationToken cancellationToken) { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorHostStatusTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorHostStatusTests.cs index 31d0da91..e4b77e51 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorHostStatusTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorHostStatusTests.cs @@ -289,10 +289,5 @@ public sealed class DriverInstanceActorHostStatusTests : RuntimeActorTestBase /// public DriverHealth GetHealth() => new(DriverState.Healthy, FixedLastRead, null); - /// - public long GetMemoryFootprint() => 0; - - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; } } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorNativeAlarmTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorNativeAlarmTests.cs index 450fafee..e1f22d7c 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorNativeAlarmTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorNativeAlarmTests.cs @@ -314,10 +314,6 @@ public sealed class DriverInstanceActorNativeAlarmTests : RuntimeActorTestBase public Task ShutdownAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// Gets the health status of the driver. public DriverHealth GetHealth() => new(DriverState.Healthy, DateTime.UtcNow, null); - /// Gets the memory footprint of the driver. - public long GetMemoryFootprint() => 0; - /// Flushes optional caches in the driver. - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; } /// A driver that implements + and lets the diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorRediscoverySignalTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorRediscoverySignalTests.cs index 36b2651d..0154d205 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorRediscoverySignalTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorRediscoverySignalTests.cs @@ -241,10 +241,5 @@ public sealed class DriverInstanceActorRediscoverySignalTests : RuntimeActorTest /// public DriverHealth GetHealth() => new(DriverState.Healthy, FixedLastRead, _lastError); - /// - public long GetMemoryFootprint() => 0; - - /// - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; } } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorResilienceWiringTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorResilienceWiringTests.cs index 87aa6167..745f3f6b 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorResilienceWiringTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorResilienceWiringTests.cs @@ -129,8 +129,6 @@ public sealed class DriverInstanceActorResilienceWiringTests : RuntimeActorTestB public Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken) => Task.CompletedTask; public Task ShutdownAsync(CancellationToken cancellationToken) => Task.CompletedTask; public DriverHealth GetHealth() => new(DriverState.Healthy, DateTime.UtcNow, null); - public long GetMemoryFootprint() => 0; - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; public Task> WriteAsync( IReadOnlyList writes, CancellationToken cancellationToken) diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorWriteAndSubscribeTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorWriteAndSubscribeTests.cs index 4941eb45..38d27fea 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorWriteAndSubscribeTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorWriteAndSubscribeTests.cs @@ -205,10 +205,6 @@ public sealed class DriverInstanceActorWriteAndSubscribeTests : RuntimeActorTest public Task ShutdownAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// Gets the health status of the driver. public DriverHealth GetHealth() => new(DriverState.Healthy, DateTime.UtcNow, null); - /// Gets the memory footprint of the driver. - public long GetMemoryFootprint() => 0; - /// Flushes optional caches in the driver. - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// Writes the specified requests (never reached — actor fast-fails in Connecting). public Task> WriteAsync( diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/StubDrivers.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/StubDrivers.cs index b9780be4..ea8c5c53 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/StubDrivers.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/StubDrivers.cs @@ -63,11 +63,6 @@ internal class StubDriver : IDriver } /// Gets the health status of the driver. public DriverHealth GetHealth() => new(DriverState.Healthy, DateTime.UtcNow, null); - /// Gets the memory footprint of the driver. - public long GetMemoryFootprint() => 0; - /// Flushes optional caches in the driver. - /// Cancellation token for the operation. - public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; } ///