docs: write-completion correlation covers all four unary write kinds
ci / nightly-windev (push) Has been skipped
ci / windows-x86 (push) Failing after 1m51s
ci / java (push) Successful in 2m16s
ci / portable (push) Successful in 8m26s

This commit is contained in:
Joseph Doherty
2026-08-09 19:47:04 -04:00
parent 794c44246a
commit 91d8715c74
3 changed files with 16 additions and 10 deletions
+10 -5
View File
@@ -534,7 +534,7 @@ against the live MXAccess attribute set.
- [Alarm Client Discovery — Subtag provider](./AlarmClientDiscovery.md) - [Alarm Client Discovery — Subtag provider](./AlarmClientDiscovery.md)
- [gRPC Contract — provider_status and degraded fields](./Grpc.md) - [gRPC Contract — provider_status and degraded fields](./Grpc.md)
## Secured-Write Completion Correlation ## Write Completion Correlation
MXAccess writes are fire-and-forget: the toolkit call returns before the MXAccess writes are fire-and-forget: the toolkit call returns before the
Galaxy commit, and the per-item outcome only exists in the later Galaxy commit, and the per-item outcome only exists in the later
@@ -542,7 +542,8 @@ Galaxy commit, and the per-item outcome only exists in the later
proved worker-side command acceptance only, forcing consumers (OtOpcUa's proved worker-side command acceptance only, forcing consumers (OtOpcUa's
GalaxyDriver) to report every write as provisionally good. GalaxyDriver) to report every write as provisionally good.
For `WriteSecured`/`WriteSecured2` the worker now holds the unary reply for a For the unary write kinds (`Write`/`Write2`/`WriteSecured`/`WriteSecured2`)
the worker now holds the unary reply for a
bounded window (`MxGateway:Worker:WriteCompletionWaitMilliseconds`, default bounded window (`MxGateway:Worker:WriteCompletionWaitMilliseconds`, default
1.5 s, `0` disables; conveyed to the worker via 1.5 s, `0` disables; conveyed to the worker via
`MXGATEWAY_WORKER_WRITE_COMPLETION_WAIT_MS`) and copies the matching `MXGATEWAY_WORKER_WRITE_COMPLETION_WAIT_MS`) and copies the matching
@@ -565,9 +566,13 @@ callback's status rows onto `MxCommandReply.statuses`. Key choices, argued in
acceptance; the MXAccess outcome (success or failure) rides only in acceptance; the MXAccess outcome (success or failure) rides only in
`statuses[0]`; the `OnWriteComplete` event still streams unchanged (nothing `statuses[0]`; the `OnWriteComplete` event still streams unchanged (nothing
swallowed, nothing synthesized). swallowed, nothing synthesized).
- **Scope: secured writes only.** Plain `Write`/`Write2` and bulk writes stay - **Scope: all four unary write kinds; bulk writes stay fire-and-forget.**
fire-and-forget — the wait would add a device round-trip per write to The first cut correlated `WriteSecured`/`WriteSecured2` only, but OtOpcUa's
high-rate supervisory loops. dominant FreeAccess write path goes out as plain `Write` (2026-08-09 live
verification, 06/S-1) — a refused plain write was invisible on the reply.
Plain `Write`/`Write2` now correlate identically. Bulk writes keep
fire-and-forget replies: waiting per entry would add a device round-trip per
item to high-rate supervisory loops.
- **Best-effort correlation.** The callback carries only - **Best-effort correlation.** The callback carries only
`(hItem, statuses)` — no transaction id — so concurrent writes to the same `(hItem, statuses)` — no transaction id — so concurrent writes to the same
item within the window can swap rows; benign for the serialized single-write item within the window can swap rows; benign for the serialized single-write
+1 -1
View File
@@ -114,7 +114,7 @@ launch CWD (SEC-01, SEC-33).
| `MxGateway:Worker:StartupProbeRetryAttempts` | `3` | Number of retry attempts for transient worker startup probe failures before pipe connection and handshake continue. | | `MxGateway:Worker:StartupProbeRetryAttempts` | `3` | Number of retry attempts for transient worker startup probe failures before pipe connection and handshake continue. |
| `MxGateway:Worker:StartupProbeRetryDelayMilliseconds` | `250` | Delay between transient startup probe retry attempts. | | `MxGateway:Worker:StartupProbeRetryDelayMilliseconds` | `250` | Delay between transient startup probe retry attempts. |
| `MxGateway:Worker:PipeConnectAttemptTimeoutMilliseconds` | `2000` | Per-attempt timeout used by the worker named-pipe connect retry path. The overall pipe connection still stays under the startup budget. | | `MxGateway:Worker:PipeConnectAttemptTimeoutMilliseconds` | `2000` | Per-attempt timeout used by the worker named-pipe connect retry path. The overall pipe connection still stays under the startup budget. |
| `MxGateway:Worker:WriteCompletionWaitMilliseconds` | `1500` | Bounded wait the worker holds a `WriteSecured`/`WriteSecured2` reply for the matching MXAccess `OnWriteComplete` callback, so the reply's `statuses` carry the real commit outcome. `0` disables the wait (pure fire-and-forget replies). Must be `>= 0`. The gateway conveys the value to the worker via the `MXGATEWAY_WORKER_WRITE_COMPLETION_WAIT_MS` environment variable. Consumers that time their own writes must budget above this wait: OtOpcUa's GalaxyDriver wraps gateway writes in a 2 s Tier A resilience timeout, so a deployment raising this option past ~2000 must raise that driver `ResilienceConfig` write timeout in step or slow-but-successful commits surface as consumer-side failures. | | `MxGateway:Worker:WriteCompletionWaitMilliseconds` | `1500` | Bounded wait the worker holds a unary write reply (`Write`/`Write2`/`WriteSecured`/`WriteSecured2`; bulk writes excluded) for the matching MXAccess `OnWriteComplete` callback, so the reply's `statuses` carry the real commit outcome. `0` disables the wait (pure fire-and-forget replies). Must be `>= 0`. The gateway conveys the value to the worker via the `MXGATEWAY_WORKER_WRITE_COMPLETION_WAIT_MS` environment variable. Consumers that time their own writes must budget above this wait: OtOpcUa's GalaxyDriver wraps gateway writes in a 2 s Tier A resilience timeout, so a deployment raising this option past ~2000 must raise that driver `ResilienceConfig` write timeout in step or slow-but-successful commits surface as consumer-side failures. |
| `MxGateway:Worker:ShutdownTimeoutSeconds` | `10` | Grace period for worker shutdown before the gateway treats shutdown as failed and may kill the worker process tree. | | `MxGateway:Worker:ShutdownTimeoutSeconds` | `10` | Grace period for worker shutdown before the gateway treats shutdown as failed and may kill the worker process tree. |
| `MxGateway:Worker:HeartbeatIntervalSeconds` | `5` | Worker heartbeat send interval and gateway heartbeat check cadence input. | | `MxGateway:Worker:HeartbeatIntervalSeconds` | `5` | Worker heartbeat send interval and gateway heartbeat check cadence input. |
| `MxGateway:Worker:HeartbeatGraceSeconds` | `15` | Maximum age of the last worker heartbeat before the gateway faults the worker. This must be greater than or equal to `HeartbeatIntervalSeconds`. | | `MxGateway:Worker:HeartbeatGraceSeconds` | `15` | Maximum age of the last worker heartbeat before the gateway faults the worker. This must be greater than or equal to `HeartbeatIntervalSeconds`. |
+5 -4
View File
@@ -431,9 +431,10 @@ Core commands:
- `AuthenticateUser` - `AuthenticateUser`
- `ArchestrAUserToId` - `ArchestrAUserToId`
**Secured-write completion correlation.** MXAccess writes are fire-and-forget **Write completion correlation.** MXAccess writes are fire-and-forget
at the toolkit level — the per-item outcome only exists in the later at the toolkit level — the per-item outcome only exists in the later
`OnWriteComplete` callback. For `WriteSecured` and `WriteSecured2` the worker `OnWriteComplete` callback. For the unary write kinds — `Write`, `Write2`,
`WriteSecured`, `WriteSecured2` — the worker
therefore holds the unary reply for a bounded window therefore holds the unary reply for a bounded window
(`MxGateway:Worker:WriteCompletionWaitMilliseconds`, default 1.5 s, `0` (`MxGateway:Worker:WriteCompletionWaitMilliseconds`, default 1.5 s, `0`
disables) and, when the matching callback arrives, copies its status rows onto disables) and, when the matching callback arrives, copies its status rows onto
@@ -444,8 +445,8 @@ empty `statuses` means unconfirmed (the callback missed the window), never
failed. The `OnWriteComplete` event still flows on the event stream unchanged. failed. The `OnWriteComplete` event still flows on the event stream unchanged.
Correlation is best-effort per `(server_handle, item_handle)` — the callback Correlation is best-effort per `(server_handle, item_handle)` — the callback
carries no transaction id, so concurrent writes to the same item within the carries no transaction id, so concurrent writes to the same item within the
window can swap rows. Plain `Write`/`Write2` and the bulk write commands stay window can swap rows. The bulk write commands stay
fire-and-forget: waiting there would add a device round-trip of latency to fire-and-forget: waiting per entry would add a device round-trip of latency to
high-rate supervisory write loops. high-rate supervisory write loops.
Bulk variants (single gRPC round-trip carries the full list, the worker Bulk variants (single gRPC round-trip carries the full list, the worker