-
Notifications
You must be signed in to change notification settings - Fork 91
Codex/fix sandbox gc expiry #1016
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c881042
945f53d
4d6ee68
d2009d5
9fd481a
901eb6d
80c0962
84baf74
528b257
1c93564
1a105e1
bda9beb
2f14578
03d6bb8
0ce6adc
bbed9ce
055413d
5f5f056
026d5c2
d85b483
51ab0c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -977,8 +977,8 @@ async def _terminate_expired_sandbox(self, task_id_str: str) -> None: | |
| async def _collect_expired_sandboxes(self) -> None: | ||
| """Terminate expired sandboxes. | ||
|
|
||
| Uses repository to efficiently find sandboxes whose last_activity_timestamp | ||
| is older than the configured TTL. | ||
| Scans active sandboxes and terminates those that have been idle longer | ||
| than the configured inactivity timeout. | ||
| """ | ||
| lock = get_distributed_lock() | ||
| if not lock.acquire("sandbox_gc", expire_seconds=300): | ||
|
|
@@ -989,9 +989,26 @@ async def _collect_expired_sandboxes(self) -> None: | |
|
|
||
| try: | ||
| logger.info("[SandboxManager] Running sandbox GC...") | ||
| expired_task_ids = self._repository.get_expired_sandbox_ids( | ||
| self._config.timeout.redis_ttl | ||
| ) | ||
| active_task_ids = self._repository.get_active_sandbox_ids() | ||
| if not active_task_ids: | ||
| logger.info("[SandboxManager] No active sandboxes found") | ||
| return | ||
|
|
||
| now = time.time() | ||
| inactivity_timeout = self._config.timeout.sandbox_inactive_timeout | ||
| expired_task_ids = [] | ||
| for task_id_str in active_task_ids: | ||
| sandbox = self._repository.load_sandbox(task_id_str) | ||
| if sandbox is None: | ||
| self._repository.remove_from_active_set(task_id_str) | ||
| logger.debug( | ||
| f"[SandboxManager] Cleaned orphaned ZSet entry: {task_id_str}" | ||
| ) | ||
| continue | ||
|
|
||
| idle_seconds = now - sandbox.last_activity_at | ||
| if idle_seconds >= inactivity_timeout: | ||
| expired_task_ids.append(task_id_str) | ||
|
Comment on lines
+999
to
+1011
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a legacy fallback for sandboxes without Records saved before this change can load with 🛠️ Suggested fallback for legacy records expired_task_ids = []
+ legacy_cutoff = time.time() - self._config.timeout.redis_ttl
for task_id_str in active_task_ids:
sandbox = self._repository.load_sandbox(task_id_str)
if sandbox is None:
self._repository.remove_from_active_set(task_id_str)
logger.debug(
f"[SandboxManager] Cleaned orphaned ZSet entry: {task_id_str}"
)
continue
- if sandbox.is_expired():
+ if sandbox.expires_at is None:
+ if sandbox.last_activity_at <= legacy_cutoff:
+ expired_task_ids.append(task_id_str)
+ continue
+
+ if sandbox.is_expired():
expired_task_ids.append(task_id_str)🤖 Prompt for AI Agents |
||
|
|
||
| if not expired_task_ids: | ||
| logger.info("[SandboxManager] No expired sandboxes found") | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_collect_expired_sandboxesis an async scheduled job, but it now iterates every active sandbox and calls synchronousload_sandboxfor each ID. With many active sandboxes this introduces N blocking Redis round-trips on the event loop, which can delay other async jobs (like heartbeat checks) and API responsiveness during GC runs.Useful? React with 👍 / 👎.