-
Notifications
You must be signed in to change notification settings - Fork 5
[Feature] Add sandbox pools #7
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
Conversation
Summary of ChangesHello @Yunnglin, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a significant new feature: sandbox pooling. The primary goal is to enhance the efficiency and scalability of sandbox environments by allowing sandboxes to be pre-initialized and reused for multiple tasks. This reduces the overhead of creating and destroying sandboxes for each execution, making the system more responsive for rapid, repeated tool invocations. The changes involve a refactored manager architecture, new configuration options, and expanded API capabilities to support this pooling mechanism. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
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.
Code Review
This pull request introduces a significant new feature: sandbox pooling. This allows for reusing sandboxes to improve performance, which is a great addition. The implementation includes both a local manager with queuing and an HTTP manager that delegates to a server. I appreciate the introduction of a SandboxManagerFactory and the use of a SandboxManagerConfig object, which makes the system more extensible and configurable. The addition of comprehensive tests for the new pooling feature is also excellent.
My feedback focuses on a critical issue in the local pool implementation that could lead to deadlocks, a performance improvement for the HTTP pool initialization, and a couple of improvements to the tests to make them more accurate and robust.
| # Pool still has IDs but sandboxes are gone | ||
| self.assertEqual(len(self.manager._sandbox_pool), 2) |
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.
This test asserts that after cleanup_all_sandboxes is called, the _sandbox_pool still contains sandbox IDs. This is validating incorrect behavior. When all sandboxes are cleaned up, the pool should also be cleared to prevent the manager from being in an inconsistent state. The HttpSandboxManager correctly clears its pool tracking on cleanup. The LocalSandboxManager should do the same, and this test should be updated to assert that the pool is empty.
| # Pool still has IDs but sandboxes are gone | |
| self.assertEqual(len(self.manager._sandbox_pool), 2) | |
| # Pool should also be cleared, and the initialized flag reset. | |
| self.assertEqual(len(self.manager._sandbox_pool), 0) | |
| self.assertFalse(self.manager._pool_initialized) |
| sandbox_id = await self.manager.create_sandbox(SandboxType.DOCKER, config) | ||
| sandbox = await self.manager.get_sandbox(sandbox_id) | ||
| self.assertIn(sandbox.status, [SandboxStatus.STOPPED, SandboxStatus.STOPPING]) | ||
| self.assertIn(sandbox.status, [SandboxStatus.RUNNING]) |
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.
While this assertion is now correct for a newly created sandbox, the test name test_stop_sandbox is misleading because the test doesn't actually call stop_sandbox. The test should either be renamed to reflect that it's testing creation status, or it should be expanded to test the stop functionality.
For example, you could add this after the creation and initial assertion:
# Now test stopping
await self.manager.stop_sandbox(sandbox_id)
sandbox = await self.manager.get_sandbox(sandbox_id)
self.assertEqual(sandbox.status, SandboxStatus.STOPPED)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.
Pull request overview
This PR adds sandbox pooling functionality to the ms-enclave sandbox system, enabling pre-initialized sandbox reuse for improved performance and resource management.
Key Changes:
- Introduces sandbox pooling with FIFO queue management for concurrent execution
- Adds
SandboxManagerFactorywith decorator-based registration pattern for manager types - Implements pool operations (
initialize_pool,execute_tool_in_pool) for both local and HTTP managers
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
ms_enclave/sandbox/model/config.py |
Adds SandboxManagerConfig class with pool configuration options |
ms_enclave/sandbox/model/base.py |
Adds IDLE and BUSY status enums, SandboxManagerType enum for manager types |
ms_enclave/sandbox/model/responses.py |
Adds success property to ToolResult for convenient status checking |
ms_enclave/sandbox/model/__init__.py |
Exports new model types for external use |
ms_enclave/sandbox/manager/base.py |
Implements SandboxManagerFactory and pool-related abstract methods |
ms_enclave/sandbox/manager/local_manager.py |
Implements local pool initialization and FIFO execution with condition variables |
ms_enclave/sandbox/manager/http_manager.py |
Implements HTTP-based pool operations delegating to server endpoints |
ms_enclave/sandbox/manager/__init__.py |
Exports SandboxManagerFactory for public API |
ms_enclave/sandbox/server/server.py |
Adds /pool/initialize and /pool/execute endpoints, updates constructor to use config |
tests/test_sandbox.py |
Updates Docker image references and removes ToolFactory tests |
tests/test_manager.py |
Adds factory tests, simplifies manager tests by removing redundant test classes |
tests/test_manage_pool.py |
Comprehensive new test suite for pool functionality covering initialization, execution, concurrency, FIFO, errors, and HTTP integration |
examples/sandbox_usage_examples.py |
Updates Docker image reference to standard Python image |
| @self.app.post('/pool/initialize') | ||
| async def initialize_pool( | ||
| pool_size: Optional[int] = None, sandbox_type: Optional[SandboxType] = None, config: Optional[Dict] = None | ||
| ): |
Copilot
AI
Dec 11, 2025
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.
The endpoint defines 'config' as Optional[Dict] but it should be a request body parameter using FastAPI's Body or a Pydantic model. Currently, FastAPI will try to parse this from query parameters, which won't work for complex configuration objects. Consider using a Pydantic request model or explicitly marking config with Body(None) to accept it as a JSON request body.
| raise HTTPException(status_code=500, detail=str(e)) | ||
|
|
||
| @self.app.post('/pool/execute', response_model=ToolResult) | ||
| async def execute_tool_in_pool(tool_name: str, parameters: Dict[str, Any], timeout: Optional[float] = None): |
Copilot
AI
Dec 11, 2025
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.
The 'parameters' parameter will be parsed as a query parameter by FastAPI, but tool parameters are typically complex objects that should be passed in the request body. Consider using a Pydantic request model or explicitly marking parameters with Body(...) to accept it as JSON in the request body.
| def validate_pool_size(cls, v): | ||
| """Validate pool size.""" | ||
| if v < 0: | ||
| raise ValueError('Pool size must be non-negative') | ||
| return v | ||
|
|
||
| @field_validator('cleanup_interval') | ||
| def validate_cleanup_interval(cls, v): |
Copilot
AI
Dec 11, 2025
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.
Normal methods should have 'self', rather than 'cls', as their first parameter.
| def validate_pool_size(cls, v): | |
| """Validate pool size.""" | |
| if v < 0: | |
| raise ValueError('Pool size must be non-negative') | |
| return v | |
| @field_validator('cleanup_interval') | |
| def validate_cleanup_interval(cls, v): | |
| def validate_pool_size(self, v): | |
| """Validate pool size.""" | |
| if v < 0: | |
| raise ValueError('Pool size must be non-negative') | |
| return v | |
| @field_validator('cleanup_interval') | |
| def validate_cleanup_interval(self, v): |
| from unittest.mock import AsyncMock, MagicMock, patch | ||
|
|
Copilot
AI
Dec 11, 2025
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.
Import of 'AsyncMock' is not used.
Import of 'MagicMock' is not used.
Import of 'patch' is not used.
| from unittest.mock import AsyncMock, MagicMock, patch |
| SandboxInfo, | ||
| SandboxManagerConfig, | ||
| SandboxStatus, | ||
| SandboxType, | ||
| ToolResult, |
Copilot
AI
Dec 11, 2025
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.
Import of 'SandboxInfo' is not used.
Import of 'ToolResult' is not used.
| SandboxInfo, | |
| SandboxManagerConfig, | |
| SandboxStatus, | |
| SandboxType, | |
| ToolResult, | |
| SandboxManagerConfig, | |
| SandboxStatus, | |
| SandboxType, |
| task1.cancel() | ||
| try: | ||
| await task1 | ||
| except asyncio.CancelledError: |
Copilot
AI
Dec 11, 2025
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.
'except' clause does nothing but pass and there is no explanatory comment.
| except asyncio.CancelledError: | |
| except asyncio.CancelledError: | |
| # Task was cancelled as part of test cleanup; this is expected. |
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
No description provided.