Add dual call modes for external systems: synchronous Call() and cached CachedCall()

Scripts now choose per invocation whether an external system call is synchronous
(all failures return to script) or cached (transient failures go to store-and-forward).
Mirrors the existing Database.Connection/CachedWrite pattern. Updated ESG, Site
Runtime script API, high-level requirements, and design doc.
This commit is contained in:
Joseph Doherty
2026-03-16 08:00:20 -04:00
parent 5fff1712a8
commit 1ef316f32c
4 changed files with 35 additions and 12 deletions

View File

@@ -73,12 +73,29 @@ All external system calls are **HTTP/REST** with **JSON** serialization:
- Response bodies are deserialized from JSON into the method's defined return type.
- Credentials (API key header or Basic Auth header) are attached to every request per the system's authentication configuration.
## External System Call Modes
Scripts choose between two call modes per invocation, mirroring the dual-mode database access pattern:
### Synchronous (Real-time)
- Script calls `ExternalSystem.Call("systemName", "methodName", params)`.
- The HTTP request is executed immediately. The script blocks until the response is received or the timeout elapses.
- **All failures** (transient and permanent) return an error to the calling script. No store-and-forward buffering.
- Use for request/response interactions where the script needs the result (e.g., fetching a recipe, querying inventory).
### Cached (Store-and-Forward)
- Script calls `ExternalSystem.CachedCall("systemName", "methodName", params)`.
- The call is attempted immediately. If it succeeds, the response is discarded (fire-and-forget).
- On **transient failure** (connection refused, timeout, HTTP 5xx), the call is routed to the Store-and-Forward Engine for retry per the system's retry settings. The script does **not** block — the call is buffered and the script continues.
- On **permanent failure** (HTTP 4xx), the error is returned **synchronously** to the calling script. No retry — the request itself is wrong.
- Use for outbound data pushes where deferred delivery is acceptable (e.g., posting production data, sending quality reports).
## Call Timeout & Error Handling
- Each external system definition specifies a **timeout** that applies to all method calls on that system.
- Error classification determines whether the Store-and-Forward Engine retries the call:
- **Transient failures** (connection refused, timeout, HTTP 5xx): The call is routed to the Store-and-Forward Engine for retry per the system's retry settings. The script does **not** block waiting for eventual delivery — the call is buffered and the script continues.
- **Permanent failures** (HTTP 4xx): No retry. The error is returned **synchronously** to the calling script for handling (log, notify, try different parameters, etc.). The failure is logged to Site Event Logging.
- Error classification by HTTP response:
- **Transient failures** (connection refused, timeout, HTTP 5xx): Behavior depends on call mode — `CachedCall` buffers for retry; `Call` returns error to script.
- **Permanent failures** (HTTP 4xx): Always returned to the calling script regardless of call mode. Logged to Site Event Logging.
- This classification ensures the S&F buffer is not polluted with requests that will never succeed.
## Database Connection Management

View File

@@ -205,7 +205,8 @@ Available to all Script Execution Actors and Alarm Execution Actors:
- `Scripts.CallShared("scriptName", parameters)` — Execute shared script code inline (direct method invocation). The call includes the current recursion depth.
### External Systems
- Access to predefined external system API methods (see External System Gateway component).
- `ExternalSystem.Call("systemName", "methodName", params)` — Synchronous HTTP call. Blocks until response or timeout. All failures return to script. Use when the script needs the result.
- `ExternalSystem.CachedCall("systemName", "methodName", params)` — Fire-and-forget with store-and-forward on transient failure. Use for outbound data pushes where deferred delivery is acceptable.
### Notifications
- `Notify.To("listName").Send("subject", "message")` — Send an email notification via a named notification list.

View File

@@ -221,7 +221,7 @@ Scripts executing on a site for a given instance can:
- **Write** attribute values on that instance. For attributes with a data source reference, the write goes to the Data Connection Layer which writes to the physical device; the in-memory value updates when the device confirms the new value via the existing subscription. For static attributes, the write updates the in-memory value directly.
- **Call other scripts** on that instance via `Instance.CallScript("scriptName", params)`. Calls use the Akka ask pattern and return the called script's return value. Script-to-script calls support concurrent execution.
- **Call shared scripts** via `Scripts.CallShared("scriptName", params)`. Shared scripts execute **inline** in the calling Script Actor's context — they are compiled code libraries, not separate actors.
- **Call external system API methods** (see Section 5).
- **Call external system API methods** in two modes: `ExternalSystem.Call()` for synchronous request/response, or `ExternalSystem.CachedCall()` for fire-and-forget with store-and-forward on transient failure (see Section 5).
- **Send notifications** (see Section 6).
- **Access databases** by requesting an MS SQL client connection by name (see Section 5.5).

View File

@@ -24,13 +24,15 @@ The External System Gateway doc lacked specification for the invocation protocol
- **Per-system timeout** — one timeout value applies to all methods on a given external system.
- Defined in the external system definition.
### Error Classification
- **Transient failures** (connection errors, timeouts, HTTP 5xx): Routed to Store-and-Forward for retry. Script does not block.
- **Permanent failures** (HTTP 4xx): No retry. Error returned synchronously to the calling script. Logged to Site Event Logging.
- S&F buffer only accepts transient failures to avoid accumulating unrecoverable requests.
### Dual Call Modes
- **`ExternalSystem.Call()`**: Synchronous request/response. All failures (transient and permanent) return to the script. No S&F buffering. Use when the script needs the result.
- **`ExternalSystem.CachedCall()`**: Fire-and-forget with S&F on transient failure. Use for outbound data pushes where deferred delivery is acceptable.
- Mirrors the existing `Database.Connection()` / `Database.CachedWrite()` pattern.
### Permanent Failure Behavior
- Synchronous error back to script, consistent with DCL write failure handling.
### Error Classification
- **Transient failures** (connection errors, timeouts, HTTP 5xx): `CachedCall` buffers for retry; `Call` returns error to script.
- **Permanent failures** (HTTP 4xx): Always returned to the calling script regardless of call mode. Logged to Site Event Logging.
- S&F buffer only accepts transient failures to avoid accumulating unrecoverable requests.
### Database Connection Pooling
- Standard ADO.NET connection pooling per named connection. No custom pool logic.
@@ -43,8 +45,10 @@ The External System Gateway doc lacked specification for the invocation protocol
| Document | Change |
|----------|--------|
| `Component-ExternalSystemGateway.md` | Updated External System Definition fields (base URL, auth modes, timeout, HTTP method/path per method). Added 3 new sections: Invocation Protocol, Call Timeout & Error Handling, Database Connection Management. |
| `Component-ExternalSystemGateway.md` | Updated External System Definition fields. Added sections: External System Call Modes (dual-mode API), Invocation Protocol, Call Timeout & Error Handling, Database Connection Management. |
| `Component-StoreAndForward.md` | Clarified that only transient failures are buffered; 4xx errors are not queued. |
| `Component-SiteRuntime.md` | Updated Script Runtime API with `ExternalSystem.Call()` and `ExternalSystem.CachedCall()`. |
| `HighLevelReqs.md` | Updated script capabilities (Section 4.4) to reflect dual call modes. |
## Alternatives Considered
@@ -54,3 +58,4 @@ The External System Gateway doc lacked specification for the invocation protocol
- **All failures retryable**: Rejected — retrying 4xx errors pollutes the S&F buffer with requests that will never succeed.
- **Custom connection pooling**: Rejected — ADO.NET pooling is battle-tested and handles this scenario natively.
- **XML serialization option**: Rejected — JSON-only is consistent with REST-only; XML systems can use a wrapper.
- **Per-method sync/cached flag**: Rejected — the same method may need synchronous calls in one script and cached in another. Script author chooses per invocation.