Skip to content

fix(vizualizer): heartbeat while waiting for a visualization job - #445

Open
KillerX wants to merge 1 commit into
fix/generate-short-options-before-first-activityfrom
fix/heartbeat-polling-activities
Open

fix(vizualizer): heartbeat while waiting for a visualization job#445
KillerX wants to merge 1 commit into
fix/generate-short-options-before-first-activityfrom
fix/heartbeat-polling-activities

Conversation

@KillerX

@KillerX KillerX commented Aug 12, 2026

Copy link
Copy Markdown
Member

6/n of a stack. Base: fix/generate-short-options-before-first-activity (#444).

This is the finding I pointed at from #443: the heartbeat trap, fixed at the activity rather than by weakening the defaults.

WaitForVisualization is a poll loop with no heartbeat, scheduled from VXExportToVOD under GetDefaultActivityOptions, which sets a ten minute HeartbeatTimeout. Its own poll Timeout is two hours. Those two numbers contradict each other: a visualization takes about as long as the audio it renders, so anything past ten minutes is killed mid-render, retried, and killed again at the same point. Ten identical failures, then the export gives up — and the render itself was fine.

Each poll now records a heartbeat, with the job status as the detail so a slow but progressing job is visible in Temporal rather than merely silent. This is the pattern WaitForFileVisibleInStorageActivity already uses; the other long-running activities go through newHeartBeater/simpleHeartBeater in common.go.

The timeout was right — the activity was the thing not holding up its end, which is why this is fixed here and not by dropping HeartbeatTimeout from the defaults.

I checked the other activities that heartbeat nowhere. All single API calls, or pure argument-building in CropShortActivity's case. One is worth a second look and is not fixed here: GetAudioDiff makes an unbounded HTTP request to the sync service and takes _ context.Context, so it can neither heartbeat nor be cancelled.

Verified: the test records zero heartbeats without the fix.

🤖 Generated with Claude Code

WaitForVisualization is a poll loop with no heartbeat, scheduled from
VXExportToVOD under GetDefaultActivityOptions, which sets a ten minute
HeartbeatTimeout. Its own poll Timeout is two hours. Those two numbers
contradict each other: a visualization takes about as long as the audio it
renders, so anything past ten minutes is killed mid-render, retried, and killed
again at the same point. Ten identical failures, then the export gives up —
and the render itself was fine.

Each poll now records a heartbeat, with the job status as the detail so a slow
but progressing job is visible rather than merely silent. This is the pattern
WaitForFileVisibleInStorageActivity already uses; the rest of the long-running
activities go through newHeartBeater or simpleHeartBeater in common.go.

Fixed here rather than by leaving HeartbeatTimeout out of the defaults: the
timeout is right, the activity was the thing not holding up its end. Checked
the other activities that heartbeat nowhere — they are single API calls or, in
CropShortActivity's case, pure argument building. The one worth a second look
is GetAudioDiff, which makes an unbounded HTTP request to the sync service and
drops its context entirely, so it can neither heartbeat nor be cancelled;
logged rather than reworked here.

Verified: the test records zero heartbeats without the fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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