Skip to content

Leaner storage: store only events the row can't say, 15 → 9 indexes, HOT renewals - #27

Merged
alob-mtc merged 3 commits into
mainfrom
spec/event-details
Oct 3, 2026
Merged

alob-mtc merged 3 commits into
mainfrom
spec/event-details

Conversation

@alob-mtc

@alob-mtc alob-mtc commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Follows runnerq/runnerq-spec#1 (spec b24225d): store only the events the activity row can't say, cut runnerq_activities from 15 indexes to 9, and make lease renewals HOT. This PR began as "write stored events in the spec's shapes", which this supersedes. The remaining events keep those shapes.

Measured with this SDK on Postgres 17 (5,000 activities): WAL for submit, claim and complete goes from 5,296 B to 2,545 B per activity, a lease renewal from 856 B to 182 B, and storage from 2,636 B to 1,354 B.

Events

  • Submission, claims, success and checkpoints write no event; the row's times and runnerq_results say them.
  • An event ending an attempt (Retrying, Failed, DeadLetter, Requeued) records the attempt's started_at and its claim as worker_id. The reaper's events do too; they used to have no worker.

Retention

  • An events window (retention.eventsMs) trims events of finished activities sooner than their tree.
  • Expired roots are found with one short walk of idx_runnerq_root_terminal per status; the candidate is then locked and rechecked. Before, every sweep sorted all the queue's finished roots.

Queries rewritten to use the indexes

  • Trees are id = root OR (root_activity_id = root AND parent_activity_id IS NOT NULL), served by idx_runnerq_root_children.
  • Deleting a tree locks and deletes its idempotency keys by primary key; this used to scan all of the queue's keys.
  • The pin check and the dependency delete go through idx_runnerq_dependencies_result; the pin check used to scan every dependency in the queue.
  • Status filters are compiled as literals, so idx_runnerq_query_status (which excludes completed activities) can serve them. root_id filters use the root index.

Schema

  • Init drops retired indexes once their successors are built. connect accepts a database that lacks the new indexes or still has retired ones; initialize completes it.

Tested locally against Postgres 17: unit and integration suites pass (126/126 integration). Conformance: 41 passed with this driver alone (3 mixed scenarios skipped), 44 passed in the mixed Go/TS fleet.

Companion: runnerq/runnerq-go#86.

runnerq-spec now defines each event type's detail
(schema/postgres/events.schema.json), and its conformance runner checks
every event against it. To match:
- Scheduled carries its scheduled_at.
- Dequeued carries the lease deadline.
- Retrying carries retry_count and scheduled_at instead of retryable;
  Failed drops retryable; a dead letter from a failure says reason
  attempts_exhausted instead of retryable.
- Requeued carries retry_count.
…HOT renewals

Follows runnerq-spec lean-storage (b24225d), as runnerq-go does.

- Submission, claims, success and checkpoints write no event; an event ending
  an attempt records the attempt's started_at and its claim as worker_id, the
  reaper's included.
- Retention.eventsMs trims events of finished activities sooner than their
  tree.
- Retention finds expired roots with one short walk of
  idx_runnerq_root_terminal per status, then locks and rechecks the candidate.
- Trees are id = root OR (root_activity_id = root AND parent IS NOT NULL);
  key locks and deletes go through the primary key; the pin check and the
  dependency delete use idx_runnerq_dependencies_result.
- Query filters: statuses as literals (idx_runnerq_query_status), root_id
  through idx_runnerq_root_children.
- initialize drops retired indexes after the concurrent builds; connect
  accepts a database without the new indexes.
@alob-mtc alob-mtc changed the title Write stored events in runnerq-spec's shapes Leaner storage: store only events the row can't say, 15 → 9 indexes, HOT renewals Oct 3, 2026
@alob-mtc
alob-mtc merged commit 8d05e4b into main Oct 3, 2026
16 checks passed
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