Use the cleanup index for actor retention - #69
Conversation
Expose the last-used timestamp to the query planner instead of wrapping it in COALESCE. Keep the creation-time fallback for never-used actors and the strict expiration boundary. Verify retention behavior on SQLite, PostgreSQL, mysql2, and Trilogy. The MySQL plan regression fails again when the old predicate is restored.
|
@greptileai please review the current head 2cbbe95. |
Greptile SummaryUpdates actor-retention filtering to let MySQL use the existing cleanup index while preserving the prior expiration semantics.
Confidence Score: 5/5The PR appears safe to merge with no outstanding correctness, security, conditional-structure, or repository-rule issues identified. The rewritten predicate is logically equivalent to the previous strict retention check, the new tests cover its boundary behavior and intended MySQL query plan, and the 0.15.1 release metadata is consistent. Important Files Changed
Reviews (2): Last reviewed commit: "chore: prepare version 0.15.1" | Re-trigger Greptile |
Set the runtime version and date the retention fix release notes. Regenerate the lockfile so its local gem entries match the package.
|
@greptileai please review current head efa407e. This prepares patch version 0.15.1: runtime version, dated changelog, and regenerated lockfile agree. The built gem and focused loading/retention checks pass. |
Instance retention wrapped
last_used_atinCOALESCE, preventing MySQL from using the existing cleanup index to narrow expired candidates. With 70,311 instances of one actor type, a read-only pruning check scanned all 70,311 rows and took 274 ms even though none qualified.Rewrite the predicate as
last_used_at < cutoff OR (last_used_at IS NULL AND created_at < cutoff). The equivalent check usedidx_so_instances_cleanup, examined 49 candidate rows, and took 0.81 ms on the same live MySQL 8.4 dataset. These are individual measurements, not a guaranteed speedup for every workload.The change preserves the strict cutoff, creation-time fallback, actor-type policies, and all pending-work/lease/paused protections. It adds no migration, index, public API, or behavior change.
Prepare patch release 0.15.1 with matching runtime and package metadata and dated release notes. Bundler regenerated the lockfile without dependency changes; the built gem reports 0.15.1.
Validation:
efa407edc31c7d6bbd135b173bd659a472d00086: PR CI and branch CI passed. Greptile reports 5/5 on this commit, with no unresolved review threads.Expected: "idx_so_instances_cleanup"; Actual: nil(full scan). Reverting the fix failed again withActual: "idx_so_instances_identity"; restoring it passed. The test checks the actual MySQL plan, not the SQL spelling.ruby -rbundler/setup -Itest -e 'Dir.glob("test/**/*_test.rb").sort.each { |file| require_relative file }'under Ruby 3.3.9.SOLID_OBJECTS_DATABASE_URL=<dedicated-database-url> ruby -rbundler/setup -Itest -e '%w[test/integration/instance_pruner_test.rb test/integration/retention_test.rb].each { |file| require_relative file }'.Boundary coverage includes never-used actors, old creation with recent use, recent creation with old use, and timestamps exactly at the cutoff. Existing retention tests cover pending mailbox work and reminders.