Skip to content

Conversation

@rgarcia
Copy link
Contributor

@rgarcia rgarcia commented Dec 7, 2025

Problem

ProcessSpawn starts asynchronous processes that continue running after the HTTP request completes. The middleware only disables scale-to-zero during the HTTP request lifecycle, meaning the VM could scale down while a spawned process was still running.

Solution

Explicitly call stz.Disable() after starting the process, and stz.Enable() when the process exits. Uses context.WithoutCancel() for the Enable call since the goroutine runs after the HTTP request returns.

The DebouncedController handles reference counting, so multiple spawned processes correctly keep scale-to-zero disabled until all have exited.

Changes

  • Add s.stz.Disable(ctx) after cmd.Start() succeeds
  • Add s.stz.Enable(stzCtx) in the waiter goroutine when the process exits
  • Use context.WithoutCancel(ctx) to ensure Enable works after HTTP request completes

This follows the same pattern used by FFmpegRecorder.Start() for handling long-running processes.


Note

Disables scale-to-zero after spawning a process and re-enables it on exit using a non-cancelled context; tests updated to use a Noop scale-to-zero controller.

  • Backend (Process management)
    • ProcessSpawn: call s.stz.Disable(ctx) after cmd.Start() and track success via stzDisabled.
    • Waiter goroutine: use context.WithoutCancel(ctx) and re-enable via s.stz.Enable(stzCtx) only if previously disabled.
    • Minor: pass stzDisabled into waiter; add log on enable/disable errors.
  • Tests
    • Update spawn/stream/stdin/kill tests to initialize ApiService with scaletozero.NewNoopController().
    • Add scaletozero import.

Written by Cursor Bugbot for commit bdb981c. This will update automatically on new commits. Configure here.

ProcessSpawn starts asynchronous processes that continue running after
the HTTP request completes. The middleware only disables scale-to-zero
during the HTTP request lifecycle, meaning the VM could scale down while
a spawned process was still running.

Now we explicitly call stz.Disable() after starting the process, and
stz.Enable() when the process exits. Uses context.WithoutCancel() for
the Enable call since the goroutine runs after the HTTP request returns.

The DebouncedController handles reference counting, so multiple spawned
processes correctly keep scale-to-zero disabled until all have exited.
@rgarcia rgarcia requested a review from hiroTamada December 7, 2025 19:56
Address bugbot feedback: if Disable() fails, calling Enable() would
incorrectly decrement the reference count and could prematurely re-enable
scale-to-zero while other processes are still running.

Now we track whether Disable() succeeded and only call Enable() in the
waiter goroutine if it did.
@rgarcia
Copy link
Contributor Author

rgarcia commented Dec 7, 2025

Fixed in b3fc9ab - now we track whether Disable() succeeded and only call Enable() in the waiter goroutine if it did. This prevents the unpaired Enable call from corrupting the reference count.

The ProcessSpawn changes now call s.stz.Disable/Enable, but the tests
created ApiService without the stz field, causing nil pointer panic.
@rgarcia rgarcia merged commit 1349e74 into main Dec 8, 2025
5 checks passed
@rgarcia rgarcia deleted the fix/process-spawn-scale-to-zero branch December 8, 2025 02:28
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.

3 participants