Repository navigation
Conversation
The server starts the operation only once the Workflow Task that issued it commits, so a failed task closes nothing. The caller is the run in the operation's link, and the close goes in as its Workflow Producer with no epoch.
A command's start always has the system callback and a direct call never has one. A direct call waits for no Workflow Task to commit, so it gets stream_invalid and a pointer to the gRPC call.
A Local Activity writes before its Workflow Task commits, and a failed task can't take the close back. So only the close Activity or the System Nexus command closes.
The crate checks the owner first, so a caller that can't close gets stream_not_owner also after the close.
Outside a chain the Stream got the safety TTL with no chain check and no close. A Workflow create of a client Stream now changes the owner too, as the crate does.
A create has no task facts, so it withdrew a run's earlier tasks that a last read then hid. Its Superseded Record now has no epoch, as the crate's.
A page stopped after 10,000 entries of other Topics with no Record. A read with no wait took it for the end, and a parked read waited for a later append.
One append woke every parked read in one transaction and copied its batch into each. That could push the execution past the mutable state limit. Any other Workflow read now answers at once.
A wake whose create wait ended left the node abandoned, and every later create failed. A nested node has no execution of its own to replace.
The size cap dropped what a Workflow read from a client's Stream, and its replay then failed in a loop. An append that would drop a floor's range now fails, as on a Workflow Stream.
A wake outlived its run until the next write or its 7-day wait, so dead wakes filled stream.maxParkedReads.
The reset run carries the chain on, but the terminated run counted as the chain's end and closed the first run's Streams.
The active policy fails with NamespaceNotActive on a standby, and the queue retried the delete task with no end.
The namespace retention deleted the Activity and its Streams, though a closed Stream stays readable for its Retention. A closed host runs no Stream task, so the hold reads each Stream's expiry time.
A later run can't tell whether the first run has Streams, so every continue-as-new or retry chain end wrote the first run.
The server has its own copy of both rules, and nothing checked it against the crate's.
The stack already wrapped the chain-end code at 100. Two rows of upstream tables keep their gofmt alignment.
The helpers keep scan and ReadOrPark at or under their complexity before this round.
CHASM dropped the tasks of a Stream in a first run after continue-as-new or in a closed Activity. Its expiry, wake waits and readers check never ran, so the hold kept the host forever.
The fmt job runs go fix and fails on the loops it rewrites to slices.Backward and slices.Contains.
The replace points the module at api-go, so go mod tidy drops the upstream sums that Misc checks flags.
The golangci job failed on their complexity. The helpers keep the behavior.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR lets a Workflow close its own Stream with a System Nexus
CloseStreamcommand, and fixes the server findings of an independent review.A Local Activity close runs before its Workflow Task commits. If the task failed after the close, the Stream stayed closed, and the retried task's write failed the run. A command commits with its task, and native Workers need no Activity for it.
What changed?
The close command.
CloseStreamis a sync operation of the stream service. It routes likeReadRecordsandDescribeStream.wf:<run id>), with the request's result, failure and status. A caller that isn't an open run of the Stream's chain getsstream_not_owner. A run that a retry withdrew getsstream_fenced.stream_invalid. Only a direct System Nexus call has no callback, so a direct caller must use the gRPCCloseStream.CloseStreamand the close at the Run Chain end don't change.Review fixes.
stream_invalid. A Workflow create of an open client Stream sets the owner, not onlyWorkflowOwned.to_offset. Only awake_onlyread with a callback parks, and a parked wake stores no Records. Other command reads return at once.stream_too_large. The reader getscontinue_as_new_suggestedwhen its floor holds half ofstream.maxStoredBytes. The readers check cancels the parked wakes of runs that closed or continued as new.withdraw_runwrites only the run's Superseded Record, with no epoch, as the crate does. The chain-end task skips the write to a first run with no Streams. An abandoned nested Stream node can be created or woken again.CompareEpochson the crate'swithdrawals.jsonandepochs.json.Known limits.
Laststill stops after the scan cap, as in the crate. A filteredlastread can return fewer Records than asked when the matches are far back.waitandcreate_wait.Why?
The review found that a Local Activity could close a Workflow's Stream before its task committed. It also found parked reads that could push an execution past the mutable state limit, and a reset that closed a chain's Streams. The new hold tests found that a nested Stream's tasks stopped when its host closed.
How did you test it?
Each behavior fix has a test that fails on
7dfc8a77and passes at the head. The vector tests pass on both, and the standby policy has no test, since it needs an XDC setup. Unit tests of./chasm/...,./service/history/workflow/,./common/api/,./common/rpc/interceptor/and./service/frontend/pass. The functionalTestStreamTestSuiteandTestStandaloneActivityTestSuitepass on SQLite.go vetis clean on the touched packages. The crate k3 and sdk-python j9 native lanes pass against a build of the head.On the fork CI, every job passes at the head,
fmt,golangciandMisc checksincluded. Those three failed since t14. The fixes are thego fixmodernizers inread.go, the stalego.sumlines of the replacedgo.temporal.io/api, and helpers that splitReadOrPark,Appendandscanunder revive's limits with no behavior change. TheMixed brain testfailed once in its Omesscheduler_stressscenario, and passed on a rerun.The superproject branch
temporalio/internal-ai-prototypes2026/10/ai198-streamspins the head,9f912df3.