Skip to content

feat(run-store): Redis-backed store for the run execution-state log - #4754

Merged
d-cs merged 23 commits into
mainfrom
feature/tri-13446-redis-snapshot-store
Aug 21, 2026
Merged

feat(run-store): Redis-backed store for the run execution-state log#4754
d-cs merged 23 commits into
mainfrom
feature/tri-13446-redis-snapshot-store

Conversation

@d-cs

@d-cs d-cs commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Adds RedisSnapshotStore to @internal/run-store: a Redis-backed, append-only store for a run's execution-state log, as an alternative to keeping that log in Postgres.

Nothing constructs it. No existing code path can reach it, so merging this changes no behaviour. The store, the wiring that would use it, and the switch that would enable it are deliberately separate changes.

Design

Four keys per run, plus one key per wait cycle, all sharing a {runId} hash tag. Every mutation for a run therefore lands in one cluster slot, and each operation is a single Lua script.

No script mints a key name. Dynamic keys are derived from KEYS[1] by string surgery, because ioredis applies keyPrefix only to the KEYS array: a key built inside Lua would be unprefixed while the client wrote a prefixed one.

Retention is keyed to run completion. A non-terminal run's keys carry no expiry at all, since a suspended run can wait indefinitely with nothing left to refresh a TTL. The terminal transition sets the completion expiry once, and a write arriving after completion re-applies that same expiry rather than a live one, so a stale client cannot resurrect a key.

Entry JSON round-trips byte for byte. No script calls cjson, and the values the store assigns itself live in their own hash fields instead of being patched into the caller's document.

Sizes are observed, never enforced. Entry and cycle-key bytes are recorded, with a warning above a configurable mark. Nothing rejects, truncates, or spills.

append takes an optional expected-current-snapshot argument. Left out, it advances the pointer unconditionally, matching the Postgres behaviour it replaces. Supplied, it advances only on a match and otherwise reports the conflict without writing.

Covered by 48 tests against a real Redis container, including the retention transitions, the single-slot guarantee under a key prefix, and tenant-scoped reads.

d-cs added 21 commits August 21, 2026 12:44
Thread runId into #observeSizes so all three high-water logger.warn
payloads name the run, per spec. Adds a capturing-logger test proving
the warning fires with the run id above the mark, and stays silent
under a high threshold.
Record actual byte values instead of booleans and partition per append
so a mis-wired metric can't hide behind a flat toContain. Cover the
succeeding direction of expectedCur: "" against a genuinely unset cur,
assert recordCycleMismatch fires, pin the CRC16 helper against a known
vector plus a negative control, bound the prefixed cycle-key TTL, prove
cur is untouched by a stale CAS, and add a matching-environment getSince
with a non-empty window.
A retried append whose write already succeeded advanced cur to its own
id, so the CAS above the duplicate guard saw its own id as a stale
expectedCur and reported forked instead of duplicate. Snapshot ids are
unique per append, so checking duplicate first is always correct.
Adds the reachable-in-tests, unreachable-in-prod case where the Lua-
chosen head is dropped by the TS env filter. It surfaced a real bug:
headOrder stayed attached to whatever row ended up last after
filtering, donating the dropped head's waitpoints to it. Track whether
the actual head row survives and only then attach its order.
… order

Implements the spec's read-side check that was previously unwritten: a
sentinel problem (an empty order string meant both "read as empty" and
"not read for this row" in getSince's tail rows) blocked it. #decode
now takes an explicit orderKnown flag, runs the count-vs-length check
only when the order was actually read, and never sets
completedWaitpointIds on a row whose order wasn't read -- which also
removes the need to delete it again afterward.
@changeset-bot

changeset-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 99f72f7

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b352b032-a514-4d03-b395-756cafbfdc87

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Added a Redis-backed RedisSnapshotStore with public types and package exports. The store uses atomic Redis commands for snapshot appends, duplicate detection, cursor checks, sequence allocation, cycle metadata, validity indexing, and terminal TTLs. It provides reads by ID, latest snapshot, waitpoint IDs, and cursor windows with environment filtering and cycle validation. The change also adds metrics, high-water logging, Redis client shutdown handling, command typings, and comprehensive Vitest coverage.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 3 files. (1 skipped: 1 unsupported.) Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly explains the change, design, behavior, testing, and scope, although it omits several template sections.
Title check ✅ Passed The title clearly and concisely identifies the Redis-backed run-store feature added by the pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/tri-13446-redis-snapshot-store

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@d-cs d-cs self-assigned this Aug 21, 2026
coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@pkg-pr-new

pkg-pr-new Bot commented Aug 21, 2026

Copy link
Copy Markdown

Open in StackBlitz

@trigger.dev/build

npm i https://pkg.pr.new/@trigger.dev/build@0daa273

trigger.dev

npm i https://pkg.pr.new/trigger.dev@0daa273

@trigger.dev/core

npm i https://pkg.pr.new/@trigger.dev/core@0daa273

@trigger.dev/python

npm i https://pkg.pr.new/@trigger.dev/python@0daa273

@trigger.dev/react-hooks

npm i https://pkg.pr.new/@trigger.dev/react-hooks@0daa273

@trigger.dev/redis-worker

npm i https://pkg.pr.new/@trigger.dev/redis-worker@0daa273

@trigger.dev/rsc

npm i https://pkg.pr.new/@trigger.dev/rsc@0daa273

@trigger.dev/schema-to-json

npm i https://pkg.pr.new/@trigger.dev/schema-to-json@0daa273

@trigger.dev/sdk

npm i https://pkg.pr.new/@trigger.dev/sdk@0daa273

commit: 0daa273

@d-cs
d-cs marked this pull request as ready for review August 21, 2026 16:24
@d-cs
d-cs merged commit dd3a1c0 into main Aug 21, 2026
62 checks passed
@d-cs
d-cs deleted the feature/tri-13446-redis-snapshot-store branch August 21, 2026 17:02
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.

2 participants