sandbox cache: StartRemoving state transition not broadcast, all allocations see stale Running state
まだ誰も着手していません。
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 86/100
調査の方向性
packages/api/internal/sandbox/storage/redis/state_change.go の StartRemoving から始め、operations.go の Update() にある publish パターンと遷移処理を比較します。subscription_manager.go の dispatch を追跡して、cache が sandbox の更新イベントを受け取ることを確認します。StartRemoving の後に cache が新しい遷移状態を反映し、既存の callback の動作を変更していなければ完了です。
索引モデルが issue の本文から書いたものです。
説明
Summary
When StartRemoving atomically writes a state transition to Redis (e.g. Running → Killing), it never calls publishSandboxEvent. The per-allocation in-process sandbox cache (introduced in #3593) therefore never learns about the new state, and every allocation continues to serve the old Running state from memory until the sandbox is eventually deleted and a remove event arrives.
Code Path
Step 1 — Lua script writes new state to Redis, but no event is published
// state_change.go
updated := sbx
updated.State = newState // e.g. Killing
written, err := startTransitionScript.Run(ctx, s.redisClient,
[]string{key, transitionKey, resultKey},
newData, transitionID, ...) // Redis now has State=Killing
// StartRemoving returns here — no publishSandboxEvent call
return updated, false, s.createCallback(...), nil
Step 2 — createCallback publishes only a routing key, not a sandboxEvent
// state_change.go — createCallback
s.publisher.Publish(cbCtx, getTransitionRoutingKey(teamID.String(), sandboxID, transitionID))
// payload: "lock:sandbox:storage:...:transition:<uuid>"
Step 3 — dispatch routes the payload to waiters, never to cache.apply
// subscription_manager.go
func (m *subscriptionManager) dispatch(payload string) {
if isSandboxEvent(payload) { // strings.HasPrefix(payload, "{") → false for routing keys
m.cache.apply(evt) // never reached
return
}
// falls through to waiter fan-out
}
Impact
Between startTransitionScript.Run() and the final Remove() call (which does publish a remove event), every allocation returns State = Running from TeamItems for a sandbox that Redis already has as Killing or Pausing. For transient transitions, restoreToRunning calls Update() which publishes correctly, but the first half of the round-trip remains invisible to the cache.
Concretely:
TeamItems(states: [Running])returns sandboxes that are mid-removal — callers may act on them incorrectly.- Metrics and user-facing sandbox list show inflated
Runningcounts during high-eviction periods.
Fix
In StartRemoving, after startTransitionScript.Run() succeeds, broadcast the updated sandbox:
s.publisher.publishSandboxEvent(ctx, sandboxEvent{
Op: sandboxEventOpUpdate,
Sandbox: &updated,
})
This mirrors exactly what Update() does in operations.go. dispatch() will call cache.apply({update, updated}), replacing the cached entry with the correct state.
The createCallback path does not need a separate publish because:
- For permanent removals (
TransitionExpires), the subsequentRemove()call already publishes aremoveevent. - For transient transitions,
restoreToRunningcallsUpdate()which already publishes anupdateevent.
Related
- #3593 — introduces the per-allocation cache that this change affects
packages/api/internal/sandbox/storage/redis/state_change.gopackages/api/internal/sandbox/storage/redis/operations.go(reference: correct publish pattern inUpdate())
- 主要言語
- Go
- スター
- 1.6k
- フォーク
- 438
- PR マージ指標
- 30日以内にマージされた PR はありません
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
e2b-dev/runtime のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
-
難易度 1/5 1時間未満 初心者へのやさしさ 86/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
e2b-dev/runtime の issue をすべて見る
似ている issue
-
難易度 1/5 1時間未満 初心者へのやさしさ 84/100
-
enhancement needs triage
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
-
kind/cleanup
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
kubernetes-sigs/kueue#15947 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
sympozium-ai/sympozium#627 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100