Skip to content

Use the cleanup index for actor retention - #69

Merged
cardmagic merged 2 commits into
mainfrom
fix/indexed-instance-retention
Sep 16, 2026
Merged

cardmagic merged 2 commits into
mainfrom
fix/indexed-instance-retention

Conversation

@cardmagic

@cardmagic cardmagic commented Sep 16, 2026

Copy link
Copy Markdown
Owner

Instance retention wrapped last_used_at in COALESCE, 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 used idx_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:

  • Current commit efa407edc31c7d6bbd135b173bd659a472d00086: PR CI and branch CI passed. Greptile reports 5/5 on this commit, with no unresolved review threads.
  • Regression first failed with Expected: "idx_so_instances_cleanup"; Actual: nil (full scan). Reverting the fix failed again with Actual: "idx_so_instances_identity"; restoring it passed. The test checks the actual MySQL plan, not the SQL spelling.
  • SQLite full Ruby suite: 658 tests, 2,308 assertions, zero failures/errors, 35 adapter/integration skips. Command: ruby -rbundler/setup -Itest -e 'Dir.glob("test/**/*_test.rb").sort.each { |file| require_relative file }' under Ruby 3.3.9.
  • Focused retention suite on MySQL 26.7 via mysql2 and Trilogy: 8 tests, 37 assertions, zero failures/errors/skips for each. PostgreSQL 17: 8 tests, 31 assertions, zero failures/errors, one MySQL-plan skip. Command: 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 }'.
  • Standard Ruby, RuboCop, RBS generation/validation, and Steep pass. Generated signatures are unchanged.

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.

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.
@cardmagic

Copy link
Copy Markdown
Owner Author

@greptileai please review the current head 2cbbe95.

@greptile-apps

greptile-apps Bot commented Sep 16, 2026

Copy link
Copy Markdown

Greptile Summary

Updates actor-retention filtering to let MySQL use the existing cleanup index while preserving the prior expiration semantics.

  • Replaces the COALESCE predicate with index-friendly timestamp conditions.
  • Adds retention-boundary and MySQL query-plan regression coverage.
  • Prepares release 0.15.1 with aligned runtime, lockfile, and changelog metadata.

Confidence Score: 5/5

The 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

Filename Overview
lib/solid_objects/instance_pruner.rb Rewrites the expiration predicate without changing its strict cutoff or creation-time fallback behavior.
test/integration/instance_pruner_test.rb Covers retention boundaries and verifies that MySQL selects the cleanup index.
lib/solid_objects/version.rb Updates the runtime version to 0.15.1.
Gemfile.lock Aligns the local gem specification and checksum entry with version 0.15.1.
CHANGELOG.md Adds dated release notes for the retention-query optimization.

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.
@cardmagic

Copy link
Copy Markdown
Owner Author

@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.

@cardmagic
cardmagic merged commit 99c41e1 into main Sep 16, 2026
41 checks passed
@cardmagic
cardmagic deleted the fix/indexed-instance-retention branch September 16, 2026 15:36
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