Repository navigation
perf(store): cluster container tables on (container_id, time) - #56
Closed
adiologydev wants to merge 2 commits into
Closed
adiologydev wants to merge 2 commits into
adiologydev wants to merge 2 commits into
Conversation
The container tables were rowid tables keyed on (time, container_id) with a (container_id, time) index. A history read for one container found its rows in the index and then did one random table lookup per row: 200-500 ms for 28k rows on a live database with the 2 MB page cache. The four container tables are now WITHOUT ROWID with PRIMARY KEY (container_id, time), plus a time index for retention, downsampling and the latest disk cycle. A history read is one range scan (7-11 ms), each row is in two B-trees instead of three, and the live data is about 43% smaller. Existing databases are rebuilt once at startup, one transaction and one WAL checkpoint per table. The old table is read in rowid order and the time index is built after the copy, so a 661 MB database takes about 21 s. The startup API answers health checks during the rebuild.
Member
Author
|
Merged into #55, so all metric history speedups are in one PR. |
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.
Problem
Container history reads are slow on real servers. The four container tables (
container_{cpu,memory,disk,network}_usage) are rowid tables keyed on(time, container_id), with a separate(container_id, time)index. To read one container's history, SQLite finds the rows in that index and then does one random table lookup per row. On a copy of a live DB (49 containers, about 1.2M rows per table, on disk) with Sentinel's 2 MB page cache, that is 190–220 ms per read. On the live 1.0.2 agent, the same calls took 0.4–0.95 s. Each row is also stored 3 times: the table, the PK index and the secondary index.Change
WITHOUT ROWID, withPRIMARY KEY (container_id, time)and an index ontime. A history read is one range scan. Thetimeindex serves retention cleanup, downsampling, and the latest-cycle read ofcontainer_disk_usage. "Latest row per container" uses the primary key.Store::open(schema::cluster_container_tables):ORDER BY container_id, timemade the copy 7× slower (one random lookup per row). Thetimeindex is built after the copy (about 20% faster).pragma_table_list.wr), so an interrupted rebuild rolls back and runs again on the next start. It runs beforeschema::apply, so the newtimeindexes are not built on old tables first./api/healthwhile the store opens, so the rebuild does not fail health checks.Benchmark
Full results:
docs/benchmark-results/2026-10-24-container-tables-clustered.md./api/container/{id}/cpu/history(avg / p95)/api/container/{id}/memory/history/api/container/{id}/network/history/api/containers/currentDownsample and cleanup are 6–9% slower, because they now go through the
timeindex. They run once a day.Risks
/api/healthanswers, and the metric endpoints answer like they do during the existing legacy migration.SQLITE_FULL), its transaction rolls back, andStore::openthen does what it does for any open failure: it moves the file aside and starts fresh. The file does not shrink after the rebuild (noVACUUMat startup, same as the legacy migration). The freed pages are reused for new rows.docs/benchmark-results/README.md, so whichever merges second has a one-line conflict. There is no code overlap.Tests
clusters_rowid_container_tables_preserving_rows. It starts from the old typed layout. After open, all 4 tables areWITHOUT ROWID, rows and per-container history are the same, the old index is gone, and a second open changes nothing.cargo fmt --check,cargo clippy --workspace --all-targets [--features traffic] -- -D warnings,cargo test --workspace [--features traffic]: all pass (626 / 683 tests).How to test
cargo test -p storemetrics.sqlitefrom a server into./db/, then runSENTINEL_DEVELOPMENT=true TOKEN=t COLLECTOR_ENABLED=false cargo run --release. You should see onecontainer table clustered on (container_id, time)log line per table, and/api/healthshould answer 200 during the rebuild.sqlite3 db/metrics.sqlite "SELECT name, wr FROM pragma_table_list WHERE name LIKE 'container_%'"should showwr = 1for the 4 time-series tables.sentinel-bench latency --base http://127.0.0.1:8888 --token tshould show container history in single-digit to low double-digit ms.