Skip to content

fix(api): prune stale team index entries when ZSET orphans are swept - #3567

Open
AdaAibaby wants to merge 2 commits into
e2b-dev:mainfrom
AdaAibaby:fix/redis-team-index-orphan-sweep
Open

fix(api): prune stale team index entries when ZSET orphans are swept#3567
AdaAibaby wants to merge 2 commits into
e2b-dev:mainfrom
AdaAibaby:fix/redis-team-index-orphan-sweep

Conversation

@AdaAibaby

@AdaAibaby AdaAibaby commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

#3566
When ExpiredItems found an orphaned ZSET member (sandbox key already gone), it removed the ZSET member but left the sandboxID sitting in the per-team SET index. These stale entries accumulate indefinitely: TeamItems silently skips nil MGET results, the healer only fills missing ZSET members, and Remove only cleans the team index on the normal removal path.

Fix: when an orphaned ZSET member is confirmed absent (nil MGET result), SREM the sandboxID from the team index in the same sweep. The MGET has already confirmed the key is gone, so we cannot accidentally unindex a live sandbox. A concurrent Add that writes the key after our MGET will SADD the sandboxID back, so the worst outcome is a brief gap in TeamItems results for one eviction cycle.

The memberRef struct gains a teamID field so the orphan handler has the context it needs without restructuring the surrounding loop.

Also replace the addSandboxScript and removeSandboxScript Lua scripts with TxPipelined: they contained no conditional logic that required Lua atomicity, and MULTI/EXEC is sufficient for these cluster-safe key pairs.

/cc @jakubno @dobrac @ValentaTomas @arkamar @tvi @tomassrnka Looking forward to your code review.

When ExpiredItems found an orphaned ZSET member (sandbox key already
gone), it removed the ZSET member but left the sandboxID sitting in the
per-team SET index. These stale entries accumulate indefinitely: TeamItems
silently skips nil MGET results, the healer only fills missing ZSET
members, and Remove only cleans the team index on the normal removal path.

Fix: when an orphaned ZSET member is confirmed absent (nil MGET result),
SREM the sandboxID from the team index in the same sweep. The MGET has
already confirmed the key is gone, so we cannot accidentally unindex a
live sandbox. A concurrent Add that writes the key after our MGET will
SADD the sandboxID back, so the worst outcome is a brief gap in TeamItems
results for one eviction cycle.

The memberRef struct gains a teamID field so the orphan handler has the
context it needs without restructuring the surrounding loop.

Also replace the addSandboxScript and removeSandboxScript Lua scripts
with TxPipelined: they contained no conditional logic that required Lua
atomicity, and MULTI/EXEC is sufficient for these cluster-safe key pairs.
@AdaAibaby
AdaAibaby force-pushed the fix/redis-team-index-orphan-sweep branch from 70a3a35 to 705fba5 Compare August 14, 2026 04:01

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 70a3a3552c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/api/internal/sandbox/storage/redis/items.go

@leonmeijer leonmeijer left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExpiredItems performs the missing-key MGET and the team-index SREM as separate operations. A resume can Add a new execution under the same sandbox ID between them, after which this cleanup removes the live sandbox from its team index permanently. Recheck key absence and remove the index member atomically in one same-slot transaction or script.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants