docs(delmia): document at-least-once failover semantics + DelmiaRecipeDownload idempotency requirement
Manual verification (task step 3) skipped: this worktree cannot reach a live environment to fetch the deployed DelmiaRecipeDownload script via `scadabridge api-method get`. Documents only the mandated contract per PLAN-06 Task 23; verifying the deployed script against it is deferred to whoever has live environment access. Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
@@ -105,6 +105,30 @@ response came back). A node that responds at all is authoritative — its answer
|
|||||||
> to the next node — failover is strictly for unreachable nodes. (Revisit only if operations want
|
> to the next node — failover is strictly for unreachable nodes. (Revisit only if operations want
|
||||||
> `5xx`/`503` to also fail over.)
|
> `5xx`/`503` to also fail over.)
|
||||||
|
|
||||||
|
## Delivery semantics: at-least-once
|
||||||
|
|
||||||
|
The connect-failure-only failover loop above is by design (no code change here) — but its
|
||||||
|
consequence is at-least-once delivery of the `DelmiaRecipeDownload` POST, not exactly-once. Two
|
||||||
|
cases both count as "no response" from the client's point of view, trigger failover to the next
|
||||||
|
URL, and produce a duplicate POST even though the first node may have already processed the
|
||||||
|
request:
|
||||||
|
|
||||||
|
1. **Connection reset after the server processed the POST** — the request reaches the site and is
|
||||||
|
applied, but the response never makes it back to the client (dropped connection, reset). The
|
||||||
|
client sees a connect failure (row 1 of the table above), rolls over, and re-POSTs to the next
|
||||||
|
`BaseUrls` entry.
|
||||||
|
2. **Slow response exceeding `TimeoutSeconds`** — the server is still processing (or has already
|
||||||
|
finished) when the client's `HttpClient.Timeout` fires. The client treats the timeout the same
|
||||||
|
as an unreachable node, rolls over, and re-POSTs.
|
||||||
|
|
||||||
|
**Requirement:** the deployed `DelmiaRecipeDownload` inbound method **MUST be idempotent** on the
|
||||||
|
natural key `(MachineCode, DownloadPath, WorkOrderNumber)`. A duplicate POST for the same key must
|
||||||
|
return the same `Result`/`ResultText` as the original call, not double-apply side effects (e.g. not
|
||||||
|
queue the recipe download twice). This is a contract on the inbound method's script, not on the
|
||||||
|
DelmiaNotifier client — the client cannot distinguish a "duplicate delivered" retry from a
|
||||||
|
"first delivery" attempt, so idempotency must live on the receiving side. See the timeout row in
|
||||||
|
the failover table above for the triggering condition.
|
||||||
|
|
||||||
## Error handling & logging
|
## Error handling & logging
|
||||||
|
|
||||||
- **stdout is reserved** for the `YES`/`NO` contract. All diagnostics — per-URL attempt, status code,
|
- **stdout is reserved** for the `YES`/`NO` contract. All diagnostics — per-URL attempt, status code,
|
||||||
|
|||||||
@@ -47,6 +47,13 @@ Each API method definition includes:
|
|||||||
- **Implementation Script**: C# script that executes when the method is called. Stored **inline** in the method definition. Follows standard C# authoring patterns but has no template inheritance — it is a standalone script tied to this method.
|
- **Implementation Script**: C# script that executes when the method is called. Stored **inline** in the method definition. Follows standard C# authoring patterns but has no template inheritance — it is a standalone script tied to this method.
|
||||||
- **Timeout**: Configurable per method. Defines the maximum time the method is allowed to execute (including any routed calls to sites) before returning a timeout error to the caller.
|
- **Timeout**: Configurable per method. Defines the maximum time the method is allowed to execute (including any routed calls to sites) before returning a timeout error to the caller.
|
||||||
|
|
||||||
|
> **Authoring note — idempotent methods for at-least-once callers**: some external callers retry
|
||||||
|
> on their own connect-failure/timeout failover loop and cannot tell "the first attempt actually
|
||||||
|
> succeeded" from "the first attempt was lost" — e.g. the `DelmiaRecipeDownload` method backing
|
||||||
|
> `DelmiaNotifier` (see `docs/plans/2026-06-26-delmia-recipe-notifier-design.md`, "Delivery
|
||||||
|
> semantics: at-least-once"). Implementation scripts for such methods must be idempotent on their
|
||||||
|
> natural key so a duplicate delivery returns the same result without double-applying side effects.
|
||||||
|
|
||||||
### Management
|
### Management
|
||||||
- Managed by users with the **Designer** role via the Central UI.
|
- Managed by users with the **Designer** role via the Central UI.
|
||||||
- All method definition changes are audit logged.
|
- All method definition changes are audit logged.
|
||||||
|
|||||||
@@ -52,6 +52,15 @@ WWNotifier.exe -m Z28061 -d "C:\recipes\job.nc" -w WO12345 -p PN-7788 -s 0100 -u
|
|||||||
- All diagnostics (per-URL attempt, status code, which URL answered, exceptions) go to **stderr** and,
|
- All diagnostics (per-URL attempt, status code, which URL answered, exceptions) go to **stderr** and,
|
||||||
if `LogPath` is set, an appended log file — never to stdout.
|
if `LogPath` is set, an appended log file — never to stdout.
|
||||||
|
|
||||||
|
## Delivery semantics
|
||||||
|
|
||||||
|
The `BaseUrls` failover loop is at-least-once, not exactly-once: a connection reset *after* the
|
||||||
|
target already processed the POST, or a slow response that exceeds `TimeoutSeconds`, both look like
|
||||||
|
an unreachable node to this client and cause a re-POST to the next URL — even though the first
|
||||||
|
attempt may already have succeeded. The deployed `DelmiaRecipeDownload` inbound method must be
|
||||||
|
idempotent on `(MachineCode, DownloadPath, WorkOrderNumber)` so a duplicate delivery is harmless;
|
||||||
|
see the design doc's "Delivery semantics: at-least-once" section for details.
|
||||||
|
|
||||||
## Configuration — `appsettings.json`
|
## Configuration — `appsettings.json`
|
||||||
|
|
||||||
Placed next to the exe (copied to the output on build/publish):
|
Placed next to the exe (copied to the output on build/publish):
|
||||||
|
|||||||
Reference in New Issue
Block a user