Repository navigation
Conversation
AdaAibaby
requested review from
ValentaTomas,
dobrac and
jakubno
as code owners
October 30, 2026 02:09
Terminal sandbox lifecycle events (killed/paused/checkpointed) were delivered to ClickHouse through a non-blocking, fixed-size queue that drops on overflow with no retry and no back-pressure. Under a burst of kills the orchestrator logs the kill but the row never lands in sandbox_events, so billing/audit disagree with the logs. - batcher: add PushWithContext (bounded-blocking) so a caller can apply back-pressure instead of dropping the instant the queue is full. - batcher: retry a failed flush with capped backoff before dropping the batch (flag-gated, default off for static batchers). - events delivery: route terminal events through PushWithContext with a flag-controlled timeout; non-terminal high-volume events keep the non-blocking Push, so the hot path is unchanged. - events delivery: add a per-event-type drop counter (sandbox_events.delivery.dropped) so terminal-event loss is alertable. Fixes e2b-dev#3770
AdaAibaby
force-pushed
the
fix/terminal-events-no-silent-drop
branch
from
October 30, 2026 03:18
e0817ed to
22467af
Compare
- batcher: StopWithContext bounds the final drain (and an in-flight flush + its retries) by a ctx, cancelling a stalled flush so a graceful stop stays within kill_timeout instead of being SIGKILLed mid-drain. Stores a CancelFunc (not a ctx) to stay containedctx-clean. - events: Close bounds the drain by the shutdown ctx and a new clickhouse-batcher-drain-timeout-ms flag (default 10s); honors ForceStop. - events: narrow terminal events to killed-only (paused/checkpointed are recoverable and kept on the non-blocking path; widen later behind a per-type flag if needed). - flags: dark-launch terminal-push-timeout and flush-max-retries (default 0 = legacy behavior); enable per-env via LaunchDarkly. - tests: StopWithContext bounds a 1M-retry drain; isTerminalEvent killed-only.
This branch has not been deployed
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.
What
Stops terminal sandbox lifecycle events (
killed) from being silently dropped on their way to ClickHouse. Fixes #3770.Today those events reach
sandbox_eventsthrough a non-blocking, fixed-size queue that drops on overflow with no retry and no back-pressure. Under a burst of kills the orchestrator logs"Killing sandbox"but the row never lands insandbox_events, so billing/audit/analytics disagree with the logs (and with the separate webhook/Redis path). We observed 253 such sandboxes in one production day, clustered in a single burst window — see #3770 for the full root-cause trace.This is not the already-fixed drain-loss bug (
publishEventAsyncis correctlyTrackWork()-guarded); the loss is one layer below, in the ClickHouse delivery target.Scope — what this does and does not fix
This PR reduces the probability of terminal-event loss and adds back-pressure + bounded durability. It is a mitigation, not a correctness guarantee:
killedunder queue overflow (A).Changes
A — bounded-blocking push for terminal events (the core fix)
batcher: addPushWithContext(ctx, item)— blocks until the queue has room orctxis done, instead of dropping the instant the queue is full. On timeout it drops and returnsErrBatcherQueueFull, matchingPush's accounting. The RLock/Lock discipline meansStopcannot close the channel mid-send, so there's no send-on-closed panic.eventsdelivery: route only terminal events throughPushWithContextwith a flag-controlled timeout. High-volume non-terminal events keep the non-blockingPush, so the hot path is unchanged. The publish already runs in theTrackWork-guarded goroutine, so bounded blocking here does not block theDeleteRPC.killed(notpaused/checkpointed): those are recoverable and can be higher volume, so they stay on the non-blocking path for now;isTerminalEventis a one-liner to widen later, ideally behind a per-type flag.B — retry a failed flush before dropping the batch
batcher: on flush error, retry with capped exponential backoff up toclickhouse-batcher-flush-max-retriesbefore giving up, so a transient ClickHouse blip doesn't discard a whole batch. Each retry incrementsbatcher.flushes{result="retry"}.Bounded shutdown drain (fixes a hazard the first revision introduced)
kill_timeout→ SIGKILL → lose the whole queue (the opposite of the goal). Nowbatcher.StopWithContext(ctx)bounds the drain, cancelling an in-flight flush + its retries when the budget expires;events.Closederives that budget from the shutdown ctx andclickhouse-batcher-drain-timeout-ms(default 10s) and honorsForceStop. The flush goroutine is always awaited (never leaked).Observability
eventsdelivery: addsandbox_events.delivery.dropped, a per-event_type/terminaldrop counter, so terminal-event loss is directly alertable instead of hidden in the genericbatcher.items.dropped.Rollout — dark-launched
The two behavior-changing flags default to their legacy values, so merging is a no-op until enabled per-environment via LaunchDarkly:
clickhouse-batcher-terminal-push-timeout-mskilledevent blocks for queue room before droppingclickhouse-batcher-flush-max-retriesclickhouse-batcher-drain-timeout-msTests
batcher:PushWithContextblocks-then-succeeds / times out / errors when not started; flush retries recover a transient failure; static batcher does not retry;StopWithContextbounds a 1,000,000-retry drain (returns promptly instead of hanging).events:isTerminalEvent(killed-only) /terminalPushTimeoutunits; routing test proving akilledevent applies bounded back-pressure on a full queue while non-terminal events drop immediately.packages/clickhousesuite +go vet+golangci-lintclean.