Skip to content

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

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.
  • ExtendLease (unfenced, used only by a test, and it wrote LeaseExtended) is removed from storage.Storage; renewals are ExtendLeaseForWorker.

Retention

  • An events window (RetentionConfig.Events, storage.RetentionPolicy.Events) 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.

Tested locally against Postgres 17: go test ./... passes. Conformance: 41 passed with this driver alone (3 mixed scenarios skipped), 44 passed in the mixed Go/TS fleet.

Companion: runnerq/runnerq-ts#27.

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:
- Enqueued and Scheduled carry activity_type and priority as a number (it
  was a string), Scheduled its scheduled_at; Enqueued drops a null
  scheduled_at.
- Dequeued carries activity_type with its lease deadline.
- Completed drops activity_type; ResultStored drops result_stored.
- Failed drops retryable; a dead letter from a failure says
  reason attempts_exhausted.
- Requeued carries the error.
- A plain Yield records result_id null and ready false, like any park.
…HOT renewals

Follows runnerq-spec lean-storage (b24225d).

- Submission, claims, success and checkpoints write no event; an event ending
  an attempt (Retrying, Failed, DeadLetter, Requeued) records the attempt's
  started_at and its claim as worker_id, the reaper's included.
- ExtendLease (unfenced, event-writing) is gone; renewals are
  ExtendLeaseForWorker, now HOT updates.
- RetentionPolicy.Events / RetentionConfig.Events 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.
- Schema init drops retired indexes after the concurrent builds.
@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 eea496f into main Oct 3, 2026
3 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