fix(inbound-api): API-method recompile/invalidate reaches the peer central node; stale doc; locked-override name rule (ScadaBridge#103)
(a) Inbound API compile state (#75) is per central node: a recompile cleared the known-bad record only on the node that answered, and the peer kept refusing the unchanged script with SCRIPT_COMPILE_FAILED (the per-request revision check only notices a changed script). Create / update / delete (ManagementActor post-commit), RecompileApiMethod, and API methods written by a Transport bundle import now publish InboundApiMethodInvalidated on the central cache-invalidation topic (#100 item 3). The new InboundApiCacheInvalidationHandler drops that method's compiled handler, known-bad record and cached ApiMethod row on the peer, which compiles the current text on its next request. (b) docs/components/InboundAPI.md no longer claims a direct DB edit stays stale until restart: the per-request revision check picks it up once the node's cached row expires (ApiMethodCacheTtl, 5 s). Component-InboundAPI, Component-Host and the #85 cache audit describe the peer invalidation. (c) Decision: the import's locked-override warnings keep EXACT (ordinal) name matching, because FlatteningService matches override names ordinally. An override differing from a locked member only by case never reaches that member's lock check (the flattener drops it as unresolved), so a "targets a LOCKED member" warning for it would be false. The comparer is now named (LockedOverrideNameComparer) and documented, and pinned by tests on both sides (flattener + import). Tests: the peer's known-bad record is cleared by a recompile (and validate leaves it alone); create/update/delete and the import publish the invalidation; the handler drops only the named method; composition root registers the handler; case-only override gets no lock warning; the flattener matches override names exactly. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -223,7 +223,7 @@ The `Route.To().Call()` inherits the method-level timeout automatically. A scrip
|
||||
|
||||
### Startup compilation and hot-reload
|
||||
|
||||
At startup the Host loads all `ApiMethod` rows from the configuration database and calls `CompileAndRegister` on each. After a method is updated via the Management API or CLI, the Management Service calls `CompileAndRegister` again — the updated script takes effect on the next request, with no node restart. Methods created after startup compile lazily on first invocation. Scripts modified directly in the database do not take effect until the next node restart; always use the Management API, CLI, or Central UI.
|
||||
At startup the Host loads all `ApiMethod` rows from the configuration database and calls `CompileAndRegister` on each. After a method is updated via the Management API or CLI, the Management Service calls `CompileAndRegister` again — the updated script takes effect on the next request, with no node restart. Methods created after startup compile lazily on first invocation. Each request resolves the method's stored row (through `ApiMethodCache`, `InboundApi:ApiMethodCacheTtl`, default 5 s) and revision-checks the cached handler against its script text, recompiling on a mismatch — so a script edited directly in the database takes effect on each node within that TTL, without a restart. Management API, CLI, Central UI and bundle-import changes also drop the method's cached row and handler on **both** central nodes at once: the node that made the change directly, the peer through `InboundApiMethodInvalidated` on the central cache-invalidation topic (#103, see [Host](./Host.md)).
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -252,11 +252,11 @@ The inbound body-capture cap for audit is configured separately under `AuditLog:
|
||||
|
||||
### A method always returns 500 after a script update
|
||||
|
||||
The in-memory handler cache still holds the previous compiled delegate. If the update went through the Management API or CLI, `CompileAndRegister` should have been called automatically and the new script should be active on the next request. If the script was edited directly in the database, the cached delegate is stale until the next node restart. Check the `ScadaBridge.InboundAPI` log category for a `"script compilation failed"` or `"trust model violation"` warning to distinguish a compile error from a routing failure.
|
||||
The in-memory handler cache still holds the previous compiled delegate. If the update went through the Management API or CLI, `CompileAndRegister` should have been called automatically and the new script should be active on the next request. If the script was edited directly in the database, the per-request revision check recompiles it once the node's cached `ApiMethod` row expires (`ApiMethodCacheTtl`, default 5 s) — no restart is needed. Check the `ScadaBridge.InboundAPI` log category for a `"script compilation failed"` or `"trust model violation"` warning to distinguish a compile error from a routing failure.
|
||||
|
||||
### A method is stuck in the known-bad-methods cache
|
||||
|
||||
If a previously broken script is fixed but `ExecuteAsync` still returns `"Script compilation failed for this method"`, the method name is in `_knownBadMethods`. A record only short-circuits while the stored script text is the one that failed, so a changed script always recompiles; a stale record therefore means the script text is unchanged and something outside it made the compile fail. `api-method get --id <N>` shows the node's `isKnownBad` / `lastCompileError` (the real diagnostics; the data plane keeps its generic message), `api-method validate --id <N>` compiles the stored script and returns the diagnostics without touching the cache, and `api-method recompile --id <N>` re-runs `CompileAndRegister` — clearing the entry on success — without re-sending the entity (#75). All three are per central node; the Central UI's API-method edit page exposes the same state and actions.
|
||||
If a previously broken script is fixed but `ExecuteAsync` still returns `"Script compilation failed for this method"`, the method name is in `_knownBadMethods`. A record only short-circuits while the stored script text is the one that failed, so a changed script always recompiles; a stale record therefore means the script text is unchanged and something outside it made the compile fail. `api-method get --id <N>` shows the node's `isKnownBad` / `lastCompileError` (the real diagnostics; the data plane keeps its generic message), `api-method validate --id <N>` compiles the stored script and returns the diagnostics without touching the cache, and `api-method recompile --id <N>` re-runs `CompileAndRegister` — clearing the entry on success — without re-sending the entity (#75). `get` and `validate` report the answering central node; a `recompile` also drops the method's handler and known-bad record on the peer central node (#103), which compiles the current script on its next request. The Central UI's API-method edit page exposes the same state and actions.
|
||||
|
||||
### Routed calls time out but the site is reachable
|
||||
|
||||
|
||||
Reference in New Issue
Block a user