[M4] mxaccess: Session::recover_connection + RecoveryEvent broadcast
Wires the recovery API surface and event channel. Recovery is currently a no-op (validates policy + emits Started/Recovered events); the real teardown + re-bind + re-advise loop is wave-3 work tracked as F16. New - Session::recover_connection(policy) — port of MxNativeSession.RecoverConnectionAsync (cs:399-440). Validates policy.max_attempts >= 1 (mirrors cs:33-36 via RecoveryPolicy::validate). Emits RecoveryEvent::Started + Recovered through the broadcast channel. Returns Ok(()) immediately — actual reconnect work is F16. - Session::recovery_events() -> broadcast::Receiver<Arc<RecoveryEvent>> — typed observable for consumers that want to wire monitoring or state-machine handling. Same Arc-broadcast pattern as Session::callbacks(). Multi-subscriber safe (Arc::ptr_eq verified in tests). - SessionInner.recovery_tx: broadcast::Sender<Arc<RecoveryEvent>> initialized in connect_nmx + connect_test_session. Removed lib.rs stub (was Err(Unsupported)). design/followups.md: F16 added (P1) covering the actual reconnect loop. Resolves when R15's long-lived connection task lands and SessionInner gains a subscription registry — at that point the recover loop becomes ~50 lines slotting RecoverConnectionCore-style work between the Started and Recovered events. Tests (4 new in mxaccess; total 48) - recover_connection emits Started + Recovered for the default single-attempt policy. - recover_connection rejects max_attempts == 0 with InvalidArgument. - recover_connection after shutdown returns EngineNotRegistered. - recovery_events supports multiple subscribers (Arc::ptr_eq verifies the same allocation reaches both). Test count delta: 520 -> 524 (+4). All four DoD gates green. Open followups: 9 -> 10 (added F16). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,12 @@ move to `## Resolved` with a date + commit hash.
|
||||
**Why deferred:** `ManagedNmxService2Client.Create()` (`ManagedNmxService2Client.cs:30-64`) auto-discovers `(host, port, service_ipid)` by activating the `NmxSvc.NmxService` COM ProgID, marshalling the resulting `IUnknown` to an OBJREF, calling `IObjectExporter::ResolveOxid` against the OXID inside, then `IRemUnknown::RemQueryInterface` to get the `INmxService2` IPID. This requires `windows-rs` for `CoCreateInstance` / `CLSIDFromProgID` (the same gating dep as F6), plus the `ComObjRefProvider.MarshalIUnknownObjRef` port (also F6).
|
||||
**Resolves when:** F6 lands (windows-rs wired in + `ComObjRefProvider` port). At that point `NmxClient::create()` becomes ~30 lines that chain the existing primitives: COM activation → `MarshalIUnknownObjRef` → `ComObjRef::parse` → `object_exporter_client::resolve_oxid_with_managed_ntlm_packet_integrity` → `rem_unknown::encode_rem_query_interface_request` over a temporary transport → `NmxClient::connect`.
|
||||
|
||||
### F16 — Real `Session::recover_connection` reconnect loop (re-bind + re-advise)
|
||||
**Severity:** P1
|
||||
**Source:** M4 wave 2/3 boundary, `crates/mxaccess/src/session.rs`
|
||||
**Why deferred:** Wave-2 `Session::recover_connection` validates the policy and emits `RecoveryEvent::Started` + `RecoveryEvent::Recovered` on each call but does **NOT** actually tear down + re-establish the NMX transport / re-advise active subscriptions. The .NET reference's `RecoverConnectionCore` (`MxNativeSession.cs:442-474`) does all three: builds a replacement `ManagedNmxService2Client` via `CreateRegisteredService`, re-`Connect`s every `_publisherEndpoints` entry, re-`AdviseSupervisory`s every entry in `_subscriptions`, then atomically swaps the old service for the new one. Porting this to Rust requires (a) tracking the active subscriptions inside `SessionInner` (currently they're owned by the consumer's `Subscription` handles, with no central registry); (b) the long-lived connection task per R15 in `design/70-risks-and-open-questions.md` so swap-in-place is safe under concurrent operations; (c) a way to re-create the `CallbackExporter` (or keep the existing one bound while the underlying transport is replaced — needs design work).
|
||||
**Resolves when:** R15's long-lived connection task lands and `SessionInner` gains a subscription registry. At that point the recover loop becomes ~50 lines: for `attempt in 1..=max_attempts`, emit Started → drop+rebuild NmxClient → `register_engine_2` with the existing OBJREF → re-advise every registered correlation_id → emit Recovered (or Failed + sleep delay + continue, mirroring the `cs:407-440` shape exactly).
|
||||
|
||||
### F14 — `tiberius`-backed SQL implementation of `Resolver` + `UserResolver`
|
||||
**Severity:** P2
|
||||
**Source:** M3 stream A, `crates/mxaccess-galaxy/src/sql.rs` (constants present, no client wiring yet)
|
||||
|
||||
Reference in New Issue
Block a user