Skip to content

fix: the remaining Unbounded workflow histories sub-items - #449

Merged
KillerX merged 5 commits into
fix/incremental-ingest-signal-selectorfrom
fix/unbounded-histories-payloads
Aug 14, 2026
Merged

fix: the remaining Unbounded workflow histories sub-items#449
KillerX merged 5 commits into
fix/incremental-ingest-signal-selectorfrom
fix/unbounded-histories-payloads

Conversation

@KillerX

@KillerX KillerX commented Aug 13, 2026

Copy link
Copy Markdown
Member

10/n of a stack. Base: fix/incremental-ingest-signal-selector (#448). One commit per sub-item — the last three of the Unbounded workflow histories finding, after the two loops in #447 and #448.

MoveFilesWorkerFlow continues as new — 909ef4d

The finding calls it an eternal fixed-ID signal workflow whose history is never reset. It is not eternal: ReceiveWithTimeout gives up after ten seconds and the run completes, restarted by the next SignalWithStartWorkflow.

The real exposure is narrower. Requests arriving less than ten seconds apart keep one run alive indefinitely, and every move adds a GetShapes plus one MoveFileWait per shape to the same history — a bulk move never lets the run end. It now continues as new when the server suggests it, and only while the signal channel is empty so no queued request is dropped. A request landing after the check is handled by the server, which turns an unhandled signal at completion into a new workflow task.

MergeExportData stops persisting a marker — 049131a

exportDataToMergeInputs ran inside a workflow.SideEffect, which writes its return value into the history as a marker: every clip path, language and offset, recorded once as the marker and again in the activity inputs derived from it. The function reads no clock, file or global, so the SideEffect bought nothing.

It does range over the per-clip audio and subtitle maps, so those keys are now sorted — each language accumulates into its own MergeInput so the order was already immaterial, but running in workflow code it has to be visibly so, to workflowcheck and to the next reader.

No workflow.GetVersion guard: this changes what the workflow writes to its history, so an export started before the deploy fails to replay it. None has to survive the deploy.

CleanupTemp returns counts — 36d239c

It sweeps ~60 folders and returned every deleted path as its result, which goes into the completion event. DeletedFiles is replaced by DeletedCountPerRoot, which is what the result is actually read for — seeing a folder that is not being cleaned. Paths remain in the activity results and worker logs.

Also moved the per-folder log line below the error check, where it can report the folder it just finished: as written it logged the running total, so it printed 0 for the first folder however much it had deleted.

ImportSubtitles can take a path — ea0e06d

A word-level transcription of a long programme is megabytes, and a workflow argument lives in the WorkflowExecutionStarted event. A big enough one does not merely bloat the history, it exceeds Temporal's payload limit and the workflow cannot start.

SubtitlesFile is an alternative input read by an activity. Subtitles is kept, not replaced — nothing in this repository starts ImportSubtitles, so the field cannot go until whatever produces the transcription has moved over.

Dropped: VXExport sending children narrowed clips

The fifth sub-item, trimming the per-destination ExportData copies down to the clip fields the children read, is no longer in this PR. The parent history already carries the full ExportData twice — the GetExportDataActivity result and the MergeExportData child input, which has to be complete — so narrowing the destination copies saved a fraction of what was already there, on a payload never close to Temporal's limit. It bought that by making any field a child later reads silently zero. Not worth the trade.

🤖 Generated with Claude Code

@KillerX

KillerX commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

Trimmed in 1dd3280. You were right about the density — roughly a quarter of the lines this branch added were comment, and most of the excess was the commit message written into the source a second time.

What stays is the part that is not recoverable by reading the code: why the version gate exists, why the channel has to be empty before continuing as new, why the clip fields are narrowed and where to add one, and why the cleanup result carries counts instead of paths.

One went entirely rather than shrinking: the note explaining that the per-folder log line had moved below the error check described the change rather than the code, and the line reads fine on its own.

@KillerX
KillerX force-pushed the fix/unbounded-histories-payloads branch from a353c1d to 0f57e7b Compare August 14, 2026 06:37
@KillerX

KillerX commented Aug 14, 2026

Copy link
Copy Markdown
Member Author

Confirmed and fixed. e0f1a44 was a rebase error of mine, not a decision: when I rebased this branch onto the updated #448 I passed e6b5f21 as the upstream, and 8e90add sits inside that range — so git replayed the reverted commit on top of its own revert. It landed here rather than in #448, which is why it was missing from the description and why the stack ended up doing both things at once.

It is gone. This branch no longer touches workflows/ingest/incremental_ingest.go — the file list is the five payload changes and their tests, nothing else.

While in there I also removed the add-and-revert pair from #448 itself, so its history is now just the selector change and the copy/catch-up fix rather than three commits arguing with each other. The whole stack was rebased with the correct upstream this time and force-pushed; make test is green on the tip, and there are no revert or duplicate commits left anywhere in the 29.

@KillerX
KillerX force-pushed the fix/unbounded-histories-payloads branch from 0f57e7b to 043709d Compare August 14, 2026 07:15
@KillerX

KillerX commented Aug 14, 2026

Copy link
Copy Markdown
Member Author

Related to the P1 on #448: the ContinueAsNew added here has the same problem, in a narrower form, and is now gated in 043709d.

GetContinueAsNewSuggested replays the value the server sent at the time, so an execution that had already crossed the suggest threshold before this deploys would emit CONTINUE_AS_NEW at a point where its history has a signal receive. It only bites a run that is both open at deploy time and already large — which is exactly the bulk move this change exists for. Behind versionContinueAsNew now, so those runs behave as they do today and the next one gets the new behaviour.

No test for it: the test environment cannot make GetContinueAsNewSuggested return true, so a test of the gated path would pass whether or not the gate were there. The commit says so rather than implying coverage.

To be clear about the other half of that review comment: this branch does not need a GetVersion gate for incremental_ingest.go. It no longer touches that file — the change that did was e0f1a44, the accidentally reintroduced transcode, removed before the review.

@KillerX
KillerX force-pushed the fix/unbounded-histories-payloads branch 3 times, most recently from 3296bf2 to 031c76a Compare August 14, 2026 08:54
The finding calls this an eternal fixed-ID signal workflow whose history is
never reset. It is not eternal: ReceiveWithTimeout gives up after ten seconds
and the run completes, to be started again by the next SignalWithStartWorkflow.

The exposure is narrower and real. Requests arriving less than ten seconds
apart keep one run alive indefinitely, and every move adds a GetShapes call
plus one MoveFileWait per shape to the same history — so a bulk move never lets
the run end and never resets the history.

It now continues as new when the server suggests it, which is the signal that
takes the actual event count and size into account rather than a threshold
guessed here. The check runs only while the signal channel is empty, so no
queued request is dropped; a request that arrives after the check but before
the server applies the continue-as-new is handled by the server, which turns an
unhandled signal at completion into a new workflow task.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@KillerX
KillerX force-pushed the fix/unbounded-histories-payloads branch 3 times, most recently from a4e799c to 5394cc6 Compare August 14, 2026 09:33
KillerX and others added 4 commits August 14, 2026 11:36
MergeExportData computed its merge inputs inside a workflow.SideEffect, which
writes the returned value into the history as a marker. For a long export that
value is every clip path, every language and every offset — recorded once as
the marker and again in the activity inputs derived from it.

exportDataToMergeInputs takes an ExportData and two paths and returns a value;
nothing in it reads a clock, a file or a global, so the SideEffect bought
nothing. It does range over the per-clip audio and subtitle maps, which is why
it now sorts those keys: each language accumulates into its own MergeInput so
the order was already immaterial, but running in workflow code it has to be
visibly so — to workflowcheck and to the next reader.

Unguarded: this changes what the workflow writes to its history, so an export
started before the deploy fails to replay it. None has to survive the deploy.

Four tests, including one that calls the function fifty times and compares, so
a future edit that lets map order leak into the result is caught rather than
being caught by a customer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CleanupTemp sweeps around sixty folders and returned the full list of deleted
paths as its result. A workflow result is written into the completion event, so
a fortnight of temp files landed in the history and in every caller that
fetches the result — to say something a number says.

DeletedFiles is replaced by DeletedCountPerRoot, which is more useful for the
thing the result is actually read for: seeing that a folder is not being
cleaned. The paths are still in the activity results and the worker logs.

The per-folder log line moves below the error check, where it can report the
folder it just finished rather than the running total — as written it logged 0
for the first folder no matter how much it had deleted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ption

The transcription arrives as a workflow argument, and a workflow argument is
stored in the WorkflowExecutionStarted event. A word-level transcription of a
long programme runs to megabytes, so every import writes that into the history
— and a big enough one does not merely bloat it, it exceeds Temporal's payload
limit and the workflow cannot be started at all.

SubtitlesFile is an alternative input: point it at the same JSON on shared
storage and an activity reads it, keeping the payload out of the history. When
it is set, Subtitles is ignored.

Subtitles is kept rather than replaced. Nothing in this repository starts
ImportSubtitles — it is registered and triggered from outside — so the field
cannot be removed until whatever produces the transcription has moved over. The
comment on it says why to prefer the path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Roughly a quarter of the lines added by this branch were comment, and most of
the excess was the commit message written into the source a second time — why
the change was made rather than what a reader of the code needs.

What stays is the part that is not recoverable from the code: why the channel
has to be empty before continuing as new, why the merge inputs are sorted, and
why the cleanup result carries counts. What goes is the retelling around it.

One comment went entirely rather than shrinking: the note explaining that the
per-folder log line had moved below the error check described the change, not
the code, and the line reads correctly on its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@KillerX
KillerX force-pushed the fix/unbounded-histories-payloads branch from 5394cc6 to c7516d2 Compare August 14, 2026 09:37
@KillerX
KillerX merged commit dc8d15e into master Aug 14, 2026
@KillerX
KillerX deleted the fix/unbounded-histories-payloads branch August 14, 2026 10:44
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