Repository navigation
perf(audit_trail): index a record's history and its life boundaries - #416
Merged
Merged
Conversation
Migration 004 adds (collection, record_id, timestamp, id), and the same columns over create and delete rows only. Without them, life_boundary_of's latest-boundary lookup walked the whole table's (timestamp, id) index: 486 ms on a 2.1M-row table for a record created long ago, 0.04 ms with them. Adapters without partial indexes (MySQL, MariaDB) get the first one only. before_row_condition now compares (timestamp, id) as a row value: the OR form it replaces cannot use the new index as a range, and read a 50k-row history whole (7 ms vs 0.06 ms). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Coverage Impact This PR will not change total coverage. Modified Files with Diff Coverage (2)
🛟 Help
|
arnaud-moncel
approved these changes
Oct 21, 2026
Member
|
🎉 This PR is included in version 1.46.3 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
forest-bot
added a commit
that referenced
this pull request
Oct 21, 2026
## [1.46.3](v1.46.2...v1.46.3) (2026-10-21) ### Performance Improvements * **audit_trail:** index a record's history and its life boundaries ([#416](#416)) ([c33c5b9](c33c5b9))
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.

life_boundary_ofruns on every audit-trail read by a caller with a scope, on a record that still exists. On a large table it can walk the whole table. This adds the indexes it needs and rewrites one condition so Postgres can use them.Mirror of the Node.js change on ForestAdmin/agent-nodejs#1983 (migration 003 there).
What changes
004-index-record-history:(collection, record_id, timestamp, id), which serves every per-record read;createanddeleterows only (partial index), for the life boundary lookup;supports_partial_index?, false on MySQL/MariaDB), which get the first index only;record_idkeeps its 255 prefix on MySQL/MariaDB, as in 001.before_row_conditionnow uses(timestamp, id) < (?, ?)instead oftimestamp < ? OR (timestamp = ? AND id < ?). They're equivalent, but Postgres can't turn theORinto a range on the new index.past_row_conditionis unchanged: it sortsidascending whatever thetimestampdirection, which a row value cannot express.Measurements
Postgres 15, 2.16M rows: 40 collections with integer ids shared across them, plus hot records. Times are the best of 3 runs, warm cache.
.first)exists?)(timestamp, id)backwards, 2.1M rows discardedWith the indexes alone, the
ORform still read the record's whole history (7–8 ms); the row-value form brings it to 0.06 ms.Tests
life_boundary_ofcases that pin the row-value comparison on a timestamp tie: a row filed before thecreateat the same instant, and one filed after.migrator_spec's fake Postgres connection answerssupports_partial_index?.EXPLAINshows the boundary lookup onaudit_logs_life_boundariesand theexists?as an index-only scan onaudit_logs_record_history.Related: PRD-1258
Definition of Done
General
Security
🤖 Generated with Claude Code
Note
Add record-history and life-boundary indexes to audit trail migrations
004-index-record-history, creating a composite index oncollection,record_id,timestamp, andid, with arecord_idlength option for MySQL and MariaDB.Store#before_row_conditionto use a row-value comparison overtimestampandidinstead of an OR expression, so pagination predicates match the new indexes. See store.rb.Macroscope summarized effb930.