alibaba / alibaba/ROCK

[Feature] Support CPU/memory/disk resource updates on sandbox restart

Open
#1,099 0 comments 0 reactions 0 assignees View on GitHub
enhancement
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.