Skip to content

feat(events): retain frontend notices and reset expired cursors - #331

Merged
Xuwznln merged 1 commit into
devfrom
feat/events-retention-adr0006
Nov 4, 2026
Merged

Xuwznln merged 1 commit into
devfrom
feat/events-retention-adr0006

Conversation

@Xuwznln

@Xuwznln Xuwznln commented Nov 4, 2026

Copy link
Copy Markdown
Collaborator

Second step of bringing the v1.0 line into dev; it finishes the ADR 0006 port started in #330. frontend_event only carries REST invalidation notices but grew without bound. A background thread owned by the local Workflow Authority now trims it even when nobody is subscribed. Defaults: last 24 hours, at most 10,000 notices, a check every 60 s and at most 1,000 rows per transaction. They are set by BasicConfig.frontend_event_* and can be overridden through UNILABOS_ environment variables. Each pass deletes only a contiguous prefix: it stops at the first notice that must stay, so out-of-order times never leave holes, and the latest notice is always kept as the watermark. The thread stops before the shared runtime.db connection closes. /api/v1/events reads the retention bounds and the page in one SQL snapshot on every poll. A cursor behind the window gets stream.reset {reason: cursor_expired} with the latest sequence as its id. A cursor exactly at the window edge resumes normally, and an explicit Last-Event-ID: 0 still replays the retained window. Clients need no change: OpenLabSite (Xuwznln/OpenLabSite#2) and the SDK watch from #330 already treat any stream.reset as a REST reload. Tests: new tests/server/test_workflow_events_retention.py covers contiguous-prefix deletion, the count and age limits, watermark retention, bounded batches and the worker lifecycle. test_workflow_events_subscription.py gains expired, edge and zero-replay cases. Locally tests/server tests/protocol tests/app give 664 passed; the 3 test_driver_package_graphs failures also fail on current dev, caused by demo packages installed into the shared venv. Checked against a two-process dev backend with UNILABOS_BASICCONFIG_FRONTEND_EVENT_MAX_COUNT=5 and a 1 s interval: the table settles at 5 notices, SSE watch keeps working and SIGTERM shuts down cleanly.

🤖 Generated with Claude Code

Second half of the v1.0 ADR 0006 port. frontend_event only carries REST
invalidation notices, but it grew without bound. A background thread
owned by the local Workflow Authority now trims it even when nobody is
subscribed. It keeps the last 24 hours and at most 10,000 notices by
default (BasicConfig.frontend_event_*, overridable through UNILABOS_
environment variables), checks every 60 s and deletes at most 1,000 rows
per transaction. Each pass deletes a contiguous prefix only: it stops at
the first notice that must stay, so out-of-order times never leave holes,
and it always keeps the latest notice as the watermark. The thread stops
before the shared runtime.db connection closes.

/api/v1/events reads the retention bounds and the page in one SQL
snapshot on every poll. A cursor behind the window gets stream.reset
{reason: cursor_expired} with the latest sequence as its id. A cursor
exactly at the window edge resumes normally. An explicit Last-Event-ID: 0
still replays the retained window on its first page.

Checked against a two-process dev backend started with
UNILABOS_BASICCONFIG_FRONTEND_EVENT_MAX_COUNT=5 and a 1 s interval: the
table settles at 5 notices, SSE watch keeps working, and SIGTERM shuts
down cleanly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Xuwznln
Xuwznln merged commit b7ac51a into dev Nov 4, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant