Skip to content

[Brook K7] Withdrew orphaned Local Activity Records and a retry run's Records. - #218

Closed
moetemp wants to merge 33 commits into
moe/brook/review/streams-k8-readsfrom
moe/brook/review/streams-k7-withdrawals
Closed

moetemp wants to merge 33 commits into
moe/brook/review/streams-k8-readsfrom
moe/brook/review/streams-k7-withdrawals

Conversation

@moetemp

@moetemp moetemp commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

What changed?

This PR adds the retry run rule and the orphaned Local Activity rule to the service and the memory Store.

  • A Local Activity writes before its Workflow Task commits, so a failed task leaves orphans. The next Local Activity write gives the last completed and failed tasks, and withdraws the orphans.
  • A retry run's first write withdraws and fences the previous run. The memory Store declares RUN_FENCE.
  • At the completed task's started id, the rule withdraws a task key lower than the completed task's. C1 sends that task's attempt and started time, so a lower transient attempt is withdrawn too.
  • The Lua side is R2, temporal-community/sdk-streams#2.

Depends on #217 (K8), for ReadRecords in the tests. Review lane is the Rust reviewers, and the Redis and Lua reviewers with R2.
Part of the Brook review series.

Why?

The orphan rule is the hardest part to review, and it uses Core field 21 (C1, temporalio/sdk-rust#1703).

Known limits, open for review.

  • A failed task's call that no later Local Activity write covers keeps its Records.
  • Core sends only the last 100 failed tasks, so an older failed task keeps its Records.
  • ow keeps every withdrawn task of a run, so it grows with the run.
  • R2's docs/redis.md lists the others. They are a Local Activity that outlives its task, a retry run after a run that never wrote, a close before a retried failure, and a reset.

How did you test it?

cargo fmt, cargo clippy -- -D warnings (with and without features), cargo test -p temporalio-streams --all-features and cargo doc pass at this commit.

@moetemp
moetemp force-pushed the moe/brook/review/streams-k8-reads branch from bbe1e40 to bd7cdf3 Compare October 27, 2026 07:35
@moetemp
moetemp force-pushed the moe/brook/review/streams-k7-withdrawals branch from d21bc6e to e12bbce Compare October 27, 2026 07:35
@moetemp
moetemp force-pushed the moe/brook/review/streams-k8-reads branch from bd7cdf3 to a428ee2 Compare October 27, 2026 08:17
@moetemp
moetemp force-pushed the moe/brook/review/streams-k7-withdrawals branch from e12bbce to 855fbbc Compare October 27, 2026 08:17
@moetemp moetemp added the skip-changelog Changelog entry rides another PR label Oct 27, 2026
@moetemp
moetemp force-pushed the moe/brook/review/streams-k8-reads branch from a428ee2 to 08eb55b Compare October 27, 2026 16:55
@moetemp
moetemp force-pushed the moe/brook/review/streams-k7-withdrawals branch from 855fbbc to 1228850 Compare October 27, 2026 16:55
@moetemp
moetemp force-pushed the moe/brook/review/streams-k8-reads branch from 08eb55b to 913292f Compare October 27, 2026 23:46
@moetemp
moetemp force-pushed the moe/brook/review/streams-k7-withdrawals branch 2 times, most recently from 8601859 to 70da9b2 Compare October 28, 2026 08:55
@moetemp
moetemp force-pushed the moe/brook/review/streams-k8-reads branch from 913292f to 04b1a06 Compare October 28, 2026 08:55
@moetemp
moetemp force-pushed the moe/brook/review/streams-k7-withdrawals branch from 70da9b2 to 7363e49 Compare October 29, 2026 04:06
@moetemp
moetemp force-pushed the moe/brook/review/streams-k8-reads branch from 04b1a06 to 1970b1f Compare October 29, 2026 04:06
@moetemp
moetemp force-pushed the moe/brook/review/streams-k7-withdrawals branch from 7363e49 to eb4a41f Compare October 29, 2026 08:37
@moetemp
moetemp force-pushed the moe/brook/review/streams-k8-reads branch 2 times, most recently from c454268 to a42f66d Compare October 29, 2026 09:17
@moetemp
moetemp force-pushed the moe/brook/review/streams-k7-withdrawals branch from eb4a41f to 9416b54 Compare October 29, 2026 09:17
A Local Activity write with its run's task facts withdraws the Records of the
run's tasks that never completed, and a retry run's first write withdraws and
fences the previous run. The task facts come from Core field 20. The memory
Store declares `RUN_FENCE`, and the suites check the rules on every Store.
@moetemp
moetemp force-pushed the moe/brook/review/streams-k8-reads branch from a42f66d to 84fb6ca Compare October 29, 2026 10:03
@moetemp
moetemp force-pushed the moe/brook/review/streams-k7-withdrawals branch from 9416b54 to 64c31e5 Compare October 29, 2026 10:03
The service suite keeps K8's close status case after K7's withdrawal cases.
# Conflicts:
#	crates/streams/src/service.rs
#	crates/streams/src/testing/service.rs
# Conflicts:
#	crates/streams/src/service.rs
A task can fail before its call writes, so no orphan item withdraws that call's later Records.
The Store keeps the task facts of the run's highest task in `tf|<run>` and fences a write whose
task they show never completed.
A write with no task facts, as from an older Core, stayed out of `oa`, so no later orphan item
withdrew its Records. Every Local Activity write with Records adds its task to `oa`, and only a
write with facts applies the orphan rule.
Such a batch dropped `h|`, though its run item doesn't withdraw the held epoch's Records. Then
the Producer's next epoch didn't flag, and those Records stayed. Only a newer epoch drops `h|`.
`ow|<run>|<task key>` matches the Redis layout, so the fence reads one field. The shared vectors
name the `oa|`, `ow|` and `tf|` fields, and two shared cases cover a batch with two items and a
resend of an orphan batch.
The `tf|<run>` value is the task keys back to back, all made by `task_key`. So a change of the
key's format reaches the field with no other change.
`oa|<run>` holds task keys of one width with no separator, as `tf|<run>` does. So binary task
keys fit it with no other change.
Only a Local Activity sends its run's task facts, and a started event id is positive. Both Stores dropped the facts of other Producers, and the run facts turned a zero or negative id into the zero key.
The task facts check reads the kind of the Producer's case. In the suite, a retry run's create uses the Stream that its previous run made.
A retry reads the same oa or tf field, so the error isn't retryable.
The withdrawal cases carry their StreamRef in ref and in stream_ref, as the other suites do.
The orphan item's writer is the run's Workflow Producer with the batch's task facts. The withdrawal suite changes the facts through the request's writer.
The review asked to stop representing invalid states. The Producer kind is now a oneof with run_id only in the run-based variants, so a client can't carry a run. A write's task facts and withdraw_run move into RunWithdrawal, which only a run writer sets.
@moetemp

moetemp commented Nov 10, 2026

Copy link
Copy Markdown
Owner Author

Closed in favor of #228, the combined branch, which has this change.

@moetemp moetemp closed this Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changelog Changelog entry rides another PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant