feat(cpl-267): document sovereign-mode cache staleness + listener-lag banner#566
feat(cpl-267): document sovereign-mode cache staleness + listener-lag banner#566GTC6244 wants to merge 7 commits into
Conversation
… banner Each API-server instance serves reads from a per-instance cache refreshed by an event listener polling the chain ~every 10s, so after an on-chain write an instance didn't originate (notably ChainSecured wallet writes) its reads can be stale for up to one poll interval. Document this read-after-write window in the SDK sovereign-mode reference, the core_sdk.js header, and the dashboard user docs. Expose the listener's lag via a new account_event_listener_lag_seconds field on GET /health, and add a dashboard banner that warns when a connected instance reports more than 30s of lag. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds end-user and SDK documentation for the API server’s read-after-write cache staleness in sovereign mode, and introduces a listener-lag signal surfaced via /health that the dashboard uses to warn when an instance’s cached reads may be meaningfully out of date.
Changes:
- Add
account_event_listener_lag_secondsto the unauthenticated health response, derived from the account-event listener’s catch-up tracking. - Add a dashboard poller + UI banner that warns when the connected instance reports >30s listener lag.
- Document cache staleness semantics and the new lag field in management docs and the SDK header.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| lit-static/dapps/dashboard/styles.css | Adds --warning theme color and styles for the new listener-lag banner. |
| lit-static/dapps/dashboard/listener_lag.js | New module that polls health and toggles the listener-lag staleness banner. |
| lit-static/dapps/dashboard/index.html | Adds the banner container with appropriate live-region attributes. |
| lit-static/dapps/dashboard/app.js | Starts the listener-lag monitor on auth-ready. |
| lit-static/core_sdk.js | Documents read-after-write cache staleness and points to /health lag field. |
| lit-api-server/src/core/v1/health.rs | Adds account_event_listener_lag_seconds to /health JSON and tests for key presence. |
| lit-api-server/src/account_events.rs | Introduces lag tracking via a process-global timestamp updated by the listener. |
| docs/management/dashboard.mdx | Documents dashboard-visible staleness scenarios and the amber banner meaning. |
| docs/management/account_modes.mdx | Documents the read-after-write staleness window and the /health lag field. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * `GET /health` reports `account_event_listener_lag_seconds`. On a healthy | ||
| * instance this stays at or below the poll interval; when it climbs, the | ||
| * listener is stalled or dead and reads may be meaningfully out of date. We poll | ||
| * /health and show a banner once the lag crosses LAG_THRESHOLD_SECS. |
| // Only advance once logs are successfully processed; on error we | ||
| // retry the same range on the next tick. | ||
| last_checked_block = latest_block; | ||
| // Processed everything up to the head we observed this tick. | ||
| mark_caught_up(); |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
# Conflicts: # lit-static/dapps/dashboard/app.js
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
cargo-deny 0.20 moved `--config` from a `check` subcommand flag to a top-level option, so the unpinned `taiki-e/install-action` (now resolving to 0.20.2) rejects the existing `cargo deny check --config ...` step with "unexpected argument '--config'", failing every rust-ci matrix job. Pin to the last 0.19.x, which accepts the current invocation, until the command is migrated to the new CLI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts: # .github/workflows/rust-ci.yml
🤖 Code review (Claude)Summary: This PR documents a read-after-write staleness window for sovereign-mode writes (in Findings1. The central documented claim is factually wrong: list reads are not cached. ( The docs state that API-mode reads (
The per-instance cache ( 2. Wrong endpoint path in docs: Health routes are mounted under 3. Staleness when the listener dies is bounded by TTL, not open-ended. ( Cache entries expire on their own: 300s for positive results, 30s for negative ( 4. Lag metric reports "caught up" after a restart that skipped events. ( On (re)start, Nits
VerdictNeeds changes. The Rust lag-tracking and the dashboard banner are solid and correctly wired, but the documentation — the stated point of the PR — misattributes the staleness window to list reads, which are uncached and read the chain live. The docs should be rewritten around the actual cached surface (permission checks and wallet-derivation on the execute path), plus the |
Address the Claude PR review: the docs incorrectly claimed list reads (listApiKeys/listGroups/listWallets/listActions) go through the per-instance cache. They don't — every list_* handler does a live contract call, so list/read endpoints (and the dashboard's tables) always reflect chain head. The cache sits on the action-execution path (authorization decisions + wallet-derivation lookups). Rewrite account_modes.mdx, dashboard.mdx, the core_sdk.js header, the dashboard banner copy, and the Rust doc comments to describe the actual cached surface: after an unobserved on-chain permission change, execute-path authorization can be stale for up to one poll interval (~10s) while the listener is healthy, or up to the cache TTL (30s denials / 5min grants) if it is stalled — never open-ended. Also: use the canonical /core/v1/health path in client-facing docs (the root /health mount still exists for NLB probes); note the lag metric resets on listener restart (which skips events); revert EVENT_POLL_INTERVAL to private; and document the process-global test-race constraint on the lag static. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks for the review — the central finding was correct and I've pushed AddressedFinding 1 — docs misattributed the staleness to list reads (the important one). Confirmed: every Finding 3 — dead-listener staleness is TTL-bounded, not open-ended. Fixed. Docs now state the window is at most ~one poll interval (~10s) while the listener is healthy, falling back to the cache TTL (30s denials / 5min grants) if it stalls — explicitly "never open-ended." Also switched "refreshes" → "invalidates" throughout. Finding 4 — lag resets on restart, which skips events. Added a caveat to the Finding 2 — health path. Partial correction: Nits: reverted Intentionally not changed
Verified after the changes: |
# Conflicts: # lit-api-server/src/core/v1/health.rs
pool_warm_hit and pool_memory_limit_bypass intermittently failed in CI with "no pre-warmed worker became ready within 15s". Root cause: every TestServer::start() warms LIT_ACTIONS_POOL_SIZE workers (production default 10), and ~a dozen server-spawning integration tests run in parallel — a ~120-way concurrent V8-snapshot bootstrap storm that, on a loaded/constrained runner, starves the warmup gate so not even one worker becomes ready in time. Reproduced locally by amplifying the storm (LIT_ACTIONS_POOL_SIZE=64 → identical panic on exactly those two warmup-gated tests, while pool_concurrent_exhaustion, which tolerates a cold pool, passes). Cap the pool to 2 for the test process via the existing #[ctor] (respecting an explicit LIT_ACTIONS_POOL_SIZE override, incl. 0 to disable), cutting the storm ~5x while still exercising pool hits/refills/fallbacks. Also bump the warmup deadline 15s→30s for extra CI margin. Production DEFAULT_POOL_SIZE is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Documents the read-after-write cache staleness window for CPL-267: each API-server instance serves reads from a per-instance cache refreshed by an event listener polling the chain ~every 10s, so after an on-chain write an instance didn't originate (notably ChainSecured/sovereign wallet writes) its reads can be stale for up to one poll interval — this is now spelled out in the SDK sovereign-mode reference (
account_modes.mdx), thecore_sdk.jsheader, and the dashboard user docs. The listener now tracks how far behind the chain head it is and exposes it as a newaccount_event_listener_lag_secondsfield on the unauthenticatedGET /health(informational; does not affect health status). The dashboard polls/healthand shows an amber staleness banner whenever a connected instance reports more than 30s of lag. New unit tests cover the lag semantics (Nonebefore first poll → small → large) and the always-present/healthfield.🤖 Generated with Claude Code