From 56854139c992119b803d51f896b62e3330a32730 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 13 Jul 2026 10:08:27 -0400 Subject: [PATCH] =?UTF-8?q?refactor(r2-02):=20delete=20the=20dead=20bulkhe?= =?UTF-8?q?ad=20knob=20=E2=80=94=20options=20+=20parser=20(01/U-6);=20stor?= =?UTF-8?q?ed=20JSON=20keys=20become=20ignored=20unknowns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...lience-config-hardening-plan.md.tasks.json | 2 +- .../Resilience/DriverResilienceOptions.cs | 9 ------- .../DriverResilienceOptionsParser.cs | 8 ------- .../DriverResilienceOptionsParserTests.cs | 24 ++++++++++++------- 4 files changed, 16 insertions(+), 27 deletions(-) diff --git a/archreview/plans/R2-02-resilience-config-hardening-plan.md.tasks.json b/archreview/plans/R2-02-resilience-config-hardening-plan.md.tasks.json index 16887f2e..7a9d626a 100644 --- a/archreview/plans/R2-02-resilience-config-hardening-plan.md.tasks.json +++ b/archreview/plans/R2-02-resilience-config-hardening-plan.md.tasks.json @@ -11,7 +11,7 @@ { "id": 7, "subject": "S-8 dispatch layer: flip HandleWriteAsync to isIdempotent:false — RED the DriverInstanceActorResilienceWiringTests expectation first, then flip; full Runtime suite green", "status": "completed", "blockedBy": [6] }, { "id": 8, "subject": "S-8/P-4 invoker: cache the no-retry options snapshot once per invoker + add RecordCallStart/Complete tracker accounting to the non-idempotent arm + 2 CapabilityInvokerTests", "status": "completed", "blockedBy": [7] }, { "id": 9, "subject": "S-8 docs + form: fix WriteIdempotentAttribute's false 'reads via reflection' claim; note the non-idempotent production default; disable Write/Ack retry cells in the razor", "status": "completed", "blockedBy": [7, 5] }, - { "id": 10, "subject": "U-6 delete (Core): remove BulkheadMaxConcurrent/MaxQueue from DriverResilienceOptions + parser shape/merge/doc-example; add BulkheadKeys_InStoredJson_AreIgnored (migration-free contract)", "status": "pending", "blockedBy": [2, 6] }, + { "id": 10, "subject": "U-6 delete (Core): remove BulkheadMaxConcurrent/MaxQueue from DriverResilienceOptions + parser shape/merge/doc-example; add BulkheadKeys_InStoredJson_AreIgnored (migration-free contract)", "status": "completed", "blockedBy": [2, 6] }, { "id": 11, "subject": "U-6 delete (AdminUI): strip bulkhead fields from ResilienceFormModel + the two razor inputs; update ResilienceFormModelTests", "status": "pending", "blockedBy": [5, 10] }, { "id": 12, "subject": "U-6 doc sweep (seam xmldocs, OTOPCUA0001 message + analyzer tests, operator docs; leave migrations/WedgeDetector/historical plans) + Options_properties_are_exactly_the_pipeline_wired_set knob-inertness guard", "status": "pending", "blockedBy": [10, 11] }, { "id": 13, "subject": "C-7 RED tests: unknown top-level key / unknown capability / unknown per-policy field survive round-trip; malformed JSON sets ParseFailed and ToJson returns the original text", "status": "pending", "blockedBy": [11] }, diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceOptions.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceOptions.cs index 62629cf7..242cb44d 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceOptions.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceOptions.cs @@ -19,15 +19,6 @@ public sealed record DriverResilienceOptions public IReadOnlyDictionary CapabilityPolicies { get; init; } = new Dictionary(); - /// Bulkhead (max concurrent in-flight calls) for every capability. Default 32. - public int BulkheadMaxConcurrent { get; init; } = 32; - - /// - /// Bulkhead queue depth. Zero = no queueing; overflow fails fast with - /// BulkheadRejectedException. Default 64. - /// - public int BulkheadMaxQueue { get; init; } = 64; - /// /// Periodic scheduled recycle interval for Tier C out-of-process hosts, in seconds. /// Null (the default) = no scheduled recycle; the driver's Host process keeps running diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceOptionsParser.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceOptionsParser.cs index e48e6211..658193ba 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceOptionsParser.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceOptionsParser.cs @@ -13,8 +13,6 @@ namespace ZB.MOM.WW.OtOpcUa.Core.Resilience; /// Example JSON shape per Phase 6.1 Stream A.2: /// /// { -/// "bulkheadMaxConcurrent": 16, -/// "bulkheadMaxQueue": 64, /// "capabilityPolicies": { /// "Read": { "timeoutSeconds": 5, "retryCount": 5, "breakerFailureThreshold": 3 }, /// "Write": { "timeoutSeconds": 5, "retryCount": 0, "breakerFailureThreshold": 5 } @@ -114,8 +112,6 @@ public static class DriverResilienceOptionsParser { Tier = tier, CapabilityPolicies = merged, - BulkheadMaxConcurrent = shape.BulkheadMaxConcurrent ?? baseOptions.BulkheadMaxConcurrent, - BulkheadMaxQueue = shape.BulkheadMaxQueue ?? baseOptions.BulkheadMaxQueue, RecycleIntervalSeconds = recycleIntervalSeconds, }; } @@ -200,10 +196,6 @@ public static class DriverResilienceOptionsParser private sealed class ResilienceConfigShape { - /// Gets or sets the maximum concurrent bulkhead requests. - public int? BulkheadMaxConcurrent { get; set; } - /// Gets or sets the maximum bulkhead queue size. - public int? BulkheadMaxQueue { get; set; } /// Gets or sets the scheduled recycle interval in seconds. public int? RecycleIntervalSeconds { get; set; } /// Gets or sets the per-capability resilience policies. diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/DriverResilienceOptionsParserTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/DriverResilienceOptionsParserTests.cs index 65f10beb..7031ba0d 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/DriverResilienceOptionsParserTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/DriverResilienceOptionsParserTests.cs @@ -98,18 +98,23 @@ public sealed class DriverResilienceOptionsParserTests read.BreakerFailureThreshold.ShouldBe(tierDefault.BreakerFailureThreshold); } - /// Verifies that bulkhead overrides are honored. + /// + /// 01/U-6: the deleted bulkhead knob's keys in stored JSON become ignored unknown keys — a + /// config carrying them parses clean with no diagnostic (migration-free contract). + /// [Fact] - public void BulkheadOverrides_AreHonored() + public void BulkheadKeys_InStoredJson_AreIgnored() { var json = """ { "bulkheadMaxConcurrent": 100, "bulkheadMaxQueue": 500 } """; - var options = DriverResilienceOptionsParser.ParseOrDefaults(DriverTier.B, json, out _); + var options = DriverResilienceOptionsParser.ParseOrDefaults(DriverTier.B, json, out var diag); - options.BulkheadMaxConcurrent.ShouldBe(100); - options.BulkheadMaxQueue.ShouldBe(500); + diag.ShouldBeNull(); + // Known capabilities untouched — the stale bulkhead keys are simply ignored. + options.Resolve(DriverCapability.Read).ShouldBe( + DriverResilienceOptions.GetTierDefaults(DriverTier.B)[DriverCapability.Read]); } /// Verifies that unknown capability surfaces in diagnostic but does not fail. @@ -133,17 +138,18 @@ public sealed class DriverResilienceOptionsParserTests DriverResilienceOptions.GetTierDefaults(DriverTier.A)[DriverCapability.Read]); } - /// Verifies that property names are case insensitive. + /// Verifies that top-level property names are case insensitive. [Fact] public void PropertyNames_AreCaseInsensitive() { var json = """ - { "BULKHEADMAXCONCURRENT": 42 } + { "RECYCLEINTERVALSECONDS": 3600 } """; - var options = DriverResilienceOptionsParser.ParseOrDefaults(DriverTier.A, json, out _); + var options = DriverResilienceOptionsParser.ParseOrDefaults(DriverTier.C, json, out var diag); - options.BulkheadMaxConcurrent.ShouldBe(42); + diag.ShouldBeNull(); + options.RecycleIntervalSeconds.ShouldBe(3600); } /// Verifies that capability name is case insensitive.