Repository navigation
factory: make the cockpit fast - #228
Merged
Merged
Conversation
Batch the snapshot's N+1 reads (viewer permissions, sub-agents per member, paused labels, agent rows, roster children), serve a stale snapshot and node projection while one rebuild runs, cache each viewer's cut with its ETag digest, render the snapshot off the event loop, read member costs side by side, and coalesce factory_changed frames in the web to one refresh per factory per 2 s. Adds a live-size benchmark (dev/benchmarks/factory_cockpit.py) and a query and latency budget test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
❌ UI Snapshot doesn't match the committed baseline. If this UI change is intentional, update the baseline — each path renders in the same pinned image, so the result matches this gate:
Diff PNGs ( |
5 of 13 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issue
N/A (perf: the live cockpit takes 3-22 s per snapshot and 15 s per node page when its cache runs out)
Summary
The live factory server runs one python process. Each cockpit read rebuilt the snapshot from about 360 to 450 SQL statements: permission checks one member at a time, sub-agents listed per member, paused labels and agent rows read once per project or child, and the roster listing children a second time. On Postgres each statement is a round trip, so a snapshot cost seconds. Every agent event also sent a
factory_changedframe (debounced 0.5 s), and each frame made every open tab refetch about 10 queries.Server:
readable_setresolves admin, sub-agent parents and grants with a fixed number of reads (get_conversations+list_for_sessions). The newConversationStore.list_sub_agents_by_parentreads every member's sub-agents with one id query plusget_conversations; the base class keeps the per-parent loop. The roster reuses those children and reads message previews with one batched call.FactoryDataprefetches root, PL and worker rows once per load. Agent rows are read once per build (contextvar memo,agent_rows_memo). Member costs are read in parallel when cold, in the request's context.forget(),nodes.invalidate()and recorded events still drop the cache hard, so the next read waits for a fresh build. A generation and epoch counter keeps a build that started before a drop from being stored.filtered_viewcachesfilter_snapshotand its ETag digest once per snapshot and readable set, so it no longer deep-copies 200-450 KB and hashes it on every request. The 304 path does no work. The 200 body is rendered off the event loop.Web:
factory_changedframes are coalesced per factory: the leading edge applies at once and the trailing edge applies once per 2 s with merged scopes. A person's own mutations still invalidate straight away.Benchmark (
dev/benchmarks/factory_cockpit.py, live size: 20 projects, ~50 members, 5k events, 600 decisions and proposals, 30 demos, 2k chat messages; SQLite with 2 ms added per SQL statement to stand in for Postgres round trips; 2 tabs polling while agents write 2 events/s):GET /factories/{fid}n/product/pingwhile loaded (event loop free?)The 186 cold snapshot statements include 90 one-time handle writes on a factory's first build. A steady-state rebuild is about 95. The worst cases left under load are each page's first read after a cold start, all under 0.5 s.
Test Plan
uv run pytest -n 8 tests/server/factory tests/stores: 5531 passed.test_databricks_volumes_artifact_storeneeds thedatabricksextra and was skipped locally.tests/server/factory/test_cockpit_perf.pyseeds a live-shaped factory and checks per-page cold SQL statement budgets, cold < 3 s, warm p95 < 300 ms and the 304.npx vitest run src/hooks src/components/factory: 2760 passed. New test for the frame throttle.OMNIGENT_SKIP_WEB_UI=true uv run python -m dev.benchmarks.factory_cockpit --db-latency-ms 2reproduces the table.Demo
N/A (server and push-handling change; numbers above)
Type of change
Test coverage
Coverage notes
The perf test budgets SQL statements, which is the deterministic signal, and keeps loose time budgets for CI. No new env or config.
🤖 Generated with Claude Code