[Feature] Support CPU/memory/disk resource updates on sandbox restart
- Dominant language
- Python
- Stars
- 485
- Forks
- 81
- Avg merge
- 16h 12m
- Merged PRs (30d)
- 8
Description
**Feature Category**
- [x] Sandbox
- [ ] Actions
- [x] Deployments
- [x] SDK & API
- [ ] Envhub
- [ ] CLI
- [ ] Performance & Optimization
- [ ] Documentation & Examples
**Problem Statement**
When a sandbox is restarted via `POST /restart` or `sandbox.restart()`, it always reuses the original resource configuration (CPU, memory, disk) from the initial `start()` call. Users have no way to adjust resources on restart — for example, using a smaller spec during development and scaling up for production runs, increasing memory after hitting OOM, or expanding disk after running out of space.
ROCK does not expose any resource update capability through the restart API.
**Proposed Solution**
Add optional `cpus`, `memory`, and `disk` parameters to the restart path (Admin API → SandboxManager → StateMachine → DockerDeployment), and let the SDK always carry the current resource values via `get_status()`:
1. **Admin API** — `POST /restart` accepts optional `cpus`, `memory`, `disk` in the request body
2. **SandboxManager** — builds `resource_overrides` dict from non-None params
3. **StateMachine** — merges overrides into the spec snapshot, persists updated spec to DB
4. **DockerDeployment** — calls `docker update --cpus --memory --memory-swap` before `docker start`
5. **SDK** — `restart()` takes no resource parameters; always calls `get_status(include_all_states=True)` to fetch current `cpus`/`memory`/`disk` and forwards them to the restart endpoint
**Detailed Feature Description**
### Admin API
```
POST /restart
{
"sandbox_id": "abc123",
"cpus": 4, // optional, omit to keep current
"memory": "16g", // optional, omit to keep current
"disk": "50g" // optional, omit to keep current
}
```
When resource params are provided, the server merges them into the `DockerDeploymentConfig` spec snapshot, persists the updated spec to the database, and applies the changes before `docker start`.
### SDK Layer
`Sandbox.restart()` does **not** accept resource parameters directly. Instead it always fetches the current configuration from the server:
```python
async def restart(self):
status = await self.get_status(include_all_states=True)
data = {
"sandbox_id": self.sandbox_id,
"cpus": status.cpus,
"memory": status.memory,
"disk": status.disk,
}
response = await HttpUtils.post(url, headers, data)
```
**Design rationale**: `SandboxConfig` has default values for `cpus` (2) and `memory` ("8g"), making it impossible to distinguish user-explicit values from defaults at the SDK layer. Forcing `get_status()` ensures the restart request always carries the actual server-side resource values, not stale or default config values. Users who need to change resources should use the Admin API directly.
### Resource Update
Before `docker start`, the deployment layer applies resource changes:
- **cpus / memory**: `docker update --cpus --memory --memory-swap` on the stopped container
- **disk**: update XFS project quota for the log directory; rootfs quota via `--storage-opt` is set at `docker run` time and persisted in the spec snapshot for future container recreation
### Spec Snapshot Persistence
When resource overrides are provided, the state machine:
1. Merges overrides into `DockerDeploymentConfig` built from the DB spec snapshot
2. Persists the updated spec to the database via `meta_store.update()`
3. Updates flat fields (`cpus`, `memory`, `disk`) in `SandboxInfo` for `get_status()` consistency
This ensures that subsequent `get_status()` calls and future restarts reflect the most recently applied resources.
### Call Chain
```
SDK Sandbox.restart()
→ get_status(include_all_states=True) # fetch current cpus/memory/disk
→ POST /restart {sandbox_id, cpus, memory, disk}
→ SandboxManager.restart_async(sandbox_id, cpus, memory, disk)
→ StateMachine.on_restart(resource_overrides={cpus, memory, disk})
→ merge overrides into DockerDeploymentConfig
→ meta_store.update() # persist updated spec + flat fields
→ Operator.restart(config, host_ip)
→ Actor.restart()
→ DockerDeployment.restart()
→ docker update --cpus --memory
→ update log dir XFS quota (if disk changed)
→ docker start
```
**Test Plan**
Unit tests covering resource override behavior:
- [x] `test_cpu_override_merges_into_config` — cpus override applied to restart config passed to operator
- [x] `test_memory_override_merges_into_config` — memory override applied to restart config
- [x] `test_disk_override_merges_into_config` — disk override applied to restart config
- [x] `test_no_overrides_uses_original_spec` — without overrides, original spec values are preserved
- [x] `test_overrides_update_spec_snapshot_in_meta_store` — updated spec (cpus/memory/disk) persisted to DB via meta_store.update(), flat fields updated in SandboxInfo
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the listed call chain from Sandbox.restart() through SandboxManager, StateMachine, and DockerDeployment, then inspect the five named unit tests covering overrides, persistence, and no-overrides behavior. Run those tests first; done means optional restart resources reach the operator, updated CPU/memory/disk values are persisted, and Docker resource changes occur before restart.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, python
- Domain
- api, backend, databases, devops
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100