Skip to content

perf(rivetkit): verify and optimize internal sqlite queries#5443

Merged
NathanFlurry merged 6 commits into
mainfrom
stack/test-rivetkit-verify-core-sql-query-efficiency-lpzxlsxr
Jul 20, 2026
Merged

perf(rivetkit): verify and optimize internal sqlite queries#5443
NathanFlurry merged 6 commits into
mainfrom
stack/test-rivetkit-verify-core-sql-query-efficiency-lpzxlsxr

Conversation

@NathanFlurry

@NathanFlurry NathanFlurry commented Jul 19, 2026

Copy link
Copy Markdown
Member

No description provided.

@claude

claude Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Review

Nice, thorough addition of a query-efficiency test harness for rivetkit-core's internal SQL, plus a CLAUDE.md policy codifying the practice going forward. One correctness issue looks important enough to block merge; the rest are smaller notes.

Bug: new index is spliced into an already-shipped migration bucket, so it never reaches existing actor databases

rivetkit-rust/packages/rivetkit-core/src/actor/internal_schema.rs adds CREATE INDEX _rivet_schedule_history_result ON _rivet_schedule_history (result) as a new statement appended inside the second &[...] bucket of MIGRATIONS, but INTERNAL_SCHEMA_VERSION stays at 6 and no new bucket was added.

MIGRATIONS has exactly 6 buckets (indices 0-5), matching INTERNAL_SCHEMA_VERSION = 6. Any actor already at schema version 6 hits the early return in ensure_internal_schema:

if current_version == INTERNAL_SCHEMA_VERSION {
    return Ok(());
}

so migration_statements never runs again for it. The new CREATE INDEX statement only executes for actors that migrate through bucket index 1 for the first time (i.e. brand-new actors created from scratch). Every already-provisioned actor currently at v6 will never get this index created, because the code comment directly above MIGRATIONS explains why this is unsafe:

Rewriting these entries in place is safe only while no internal schema version has shipped; after release, all changes must be appended as new migrations and INTERNAL_SCHEMA_VERSION must advance.

This index backs schedule::RECOVER_HISTORY_SQL (UPDATE _rivet_schedule_history SET ... WHERE result = ?), which the new sql_efficiency.rs test explicitly asserts uses _rivet_schedule_history_result (see schedule.recover_history in query_catalog()). That query runs on actor startup/recovery. For every actor that already reached schema v6 before this change ships, it will keep doing a full scan of _rivet_schedule_history (up to MAX_ACTOR_HISTORY = 10,000 rows) on every recovery — i.e. the exact class of regression this PR is meant to prevent, but only for pre-existing actors, which is precisely the case the test fixture (which always builds a fresh DB by replaying every migration bucket from scratch) can't catch.

Fix: add the new index as its own new bucket at the end of MIGRATIONS and bump INTERNAL_SCHEMA_VERSION to 7, per the file's own migration policy.

Minor notes

  • user_kv_list_where's new unbounded-listing path (rivetkit-rust/packages/rivetkit-core/src/actor/internal_storage.rs) replaces a single unbounded query with sequential 128-row keyset-paginated round trips. The keyset logic itself looks correct (bind-param ordering lines up with the generated SQL in both the plain and cursor branches, and >/< cursor comparisons against a primary-key column can't skip or duplicate rows). Worth double-checking intent: for actors with very large KV sets this turns one query into many sequential awaits. If this is meant to keep responses within the existing depot/remote-SQL row/size budgets (matching KV_TX_MAX_ROWS/USER_KV_BATCH_GET_MAX_KEYS), might be worth a one-line comment saying so, since it isn't obvious from the diff alone why an already-indexed, already-sorted unbounded scan needed to become paginated.
  • The new CLAUDE.md bullet reads well and matches the file's existing single-bullet convention.
  • The EXPLAIN QUERY PLAN + sqlite3_stmt_status combination in sql_efficiency.rs (plan-shape assertions plus runtime fullscan/sort/auto-index counters, plus a negative control that proves the harness actually detects a bad plan) is a solid pattern — good defense against the assertions silently becoming no-ops.

Overall: solid test-infrastructure work; please fix the migration/version-bump issue before merging since it defeats the stated purpose of the PR for any actor that isn't brand new.

@railway-app

railway-app Bot commented Jul 19, 2026

Copy link
Copy Markdown

🚅 Deployed to the rivet-pr-5443 environment in rivet-frontend

Service Status Web Updated (UTC)
kitchen-sink 😴 Sleeping (View Logs) Web Jul 20, 2026 at 7:00 am
frontend-cloud 😴 Sleeping (View Logs) Web Jul 20, 2026 at 12:37 am
frontend-inspector 😴 Sleeping (View Logs) Web Jul 20, 2026 at 12:35 am
website 😴 Sleeping (View Logs) Web Jul 19, 2026 at 10:54 pm
ladle ✅ Success (View Logs) Web Jul 19, 2026 at 10:43 pm
mcp-hub ✅ Success (View Logs) Web Jul 19, 2026 at 10:42 pm

@NathanFlurry
NathanFlurry force-pushed the stack/test-rivetkit-verify-core-sql-query-efficiency-lpzxlsxr branch from df2c575 to 00aa267 Compare July 20, 2026 00:29
@railway-app
railway-app Bot temporarily deployed to rivet-frontend / rivet-pr-5443 July 20, 2026 00:29 Destroyed
@NathanFlurry NathanFlurry changed the title test(rivetkit): verify core SQL query efficiency perf(rivetkit): verify and optimize internal sqlite queries Jul 20, 2026
@NathanFlurry
NathanFlurry force-pushed the stack/test-rivetkit-verify-core-sql-query-efficiency-lpzxlsxr branch from 00aa267 to 1bc55d6 Compare July 20, 2026 00:37
@railway-app
railway-app Bot temporarily deployed to rivet-frontend / rivet-pr-5443 July 20, 2026 00:37 Destroyed
@NathanFlurry
NathanFlurry changed the base branch from cron to main July 20, 2026 08:37
@NathanFlurry
NathanFlurry merged commit c6d172f into main Jul 20, 2026
15 of 27 checks passed
@NathanFlurry
NathanFlurry deleted the stack/test-rivetkit-verify-core-sql-query-efficiency-lpzxlsxr branch July 20, 2026 08:39
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