perf(wallet): join the transaction lookups only when a column filter needs them - #240
Merged
Merged
Conversation
…needs them
Host report (rant 2026-09-14T21:0x): refreshing `#/sharing` on dev lands on the
login page. Measured with browser-harness: `/api/me` and `/api/models` are
queued behind slow queries on a shared DB lock, and the boot session restore
awaits all four requests before calling `enterApp()` — so the login page stays
up (with the token intact) until the browser eventually retries.
The slow query is `/api/transactions`: its summary, COUNT and trend statements
add `tx_joins()` (keys/users/api_keys) unconditionally, and the presence of
those LEFT JOINs makes SQLite drop the covering index for a row-by-row lookup.
Same statement, dev db (186k rows on NFS), measured on one copy:
no joins -> COVERING INDEX idx_transactions_user_id_time_type_pts_tokens 0.21s
tx_joins() -> SEARCH ... USING INDEX idx_transactions_user_id_id 22.6s
Only `user_name` and `key_name` filters ever reference the joined tables:
`model`, `status` and the pts range all live on `transactions` itself. So the
three statements that touch nothing but `t.*` now join only when one of those
two filters is present. `needs_joins()` is the single predicate for that, so
the JOIN and the references in `tx_where` cannot fork again.
The list query keeps its joins (it renders user_name / key_label / key_name);
it is bounded by `ORDER BY t.id DESC LIMIT n`, so it stays cheap.
`cargo test` 249 passed / 0 failed; `cargo fmt --check` clean. The SQL-plan
comparison above is the acceptance evidence; the existing filter tests cover
the joined paths (user_name / key_name still filter correctly).
argszero
added a commit
that referenced
this pull request
Sep 14, 2026
Ships two changes since v0.7.23 (#239, #240): - #240: the transactions summary/COUNT/trend statements joined keys/users/ api_keys unconditionally, which made SQLite drop the covering index and fall back to a row-by-row lookup (0.21s -> 22.6s on the dev db, 186k rows over NFS). That query held the shared DB lock long enough to queue /api/me and /api/models behind it, and the boot session restore waits for all four requests, so a refresh could sit on the login page with the token intact. The joins are now conditional on a column filter actually needing them. - #239: README tagline rewritten and the live instance surfaced. No schema change (12 -> 14 happened in v0.7.23). - Cargo.toml / Cargo.lock: 0.7.23 -> 0.7.24. - ui/index.html: asset cache-bust -> 20260914-11 (all five refs). - CHANGELOG.md: v0.7.24 entry. Gates: `cargo test` 249 passed / 0 failed, `cargo fmt --check` clean.
This was referenced Sep 14, 2026
Merged
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.
Summary
修复宿主报告(rant
2026-09-14T21:0x):dev 上刷新#/sharing会停在登录页(URL 仍#/sharing、token 仍在 sessionStorage)。根因:
/api/transactions的 summary / COUNT / trend 三条语句无条件拼上tx_joins()(keys/users/api_keys 三个 LEFT JOIN),而这几个 LEFT JOIN 一出现,SQLite 就弃用覆盖索引、退回逐行回表。同一条聚合语句、同一个 dev 库(18.6 万行,NFS)实测:COVERING INDEX idx_transactions_user_id_time_type_pts_tokenstx_joins()SEARCH t USING INDEX idx_transactions_user_id_id只有
user_name/key_name两个筛选会引用 JOIN 表列(model/status/pts全在transactions自身)⇒ 三条「只碰t.*」的语句改为按需 JOIN。故障链(browser-harness 实测):慢查询长时间占住共享 DB 锁 →
/api/me、/api/models被排队(实测单次/api/me曾被卡到 35s,并发下出现网关 504)→ui/js/app.js的 boot 会话恢复串行等 4 个请求全部完成才enterApp(),其中/api/models是非必需的 → 任一个卡住,登录页就无限期停着(且api.js无任何超时)。实测 3 次刷新中 2 次卡住。Related Issue
保留 token 却显示登录页是误导性最强的表现,故前端错误兜底另开一条(见 rant 正文第 4 点),本 PR 只修慢查询这一层。
Changes
src/routes/wallet.rs:新增needs_joins()(唯一判定谓词)与tx_joins_if();summary / COUNT / trend 按需 JOINuser_name/key_label/key_name,且有LIMIT兜底)Tests
cargo test249 passed / 0 failedcargo fmt --check通过user_name/key_name筛选测试覆盖 JOIN 路径仍在(transactions_user_and_api_key_name等)EXPLAIN QUERY PLAN+ 同副本 A/B 计时(0.21s vs 22.6s)Checklist
fix/)