Skip to content

Commit 9574ec5

Browse files
author
Frederik Wystup
committed
feat(api): add resource limits configuration endpoint
This commit introduces a new API endpoint (`GET /api/limits`) that retrieves the current resource limit configurations for workspace management. This includes parameters such as maximum concurrent workspaces, CPU and memory limits per workspace, and whether resource limits enforcement is enabled. Additionally, the frontend has been updated to load these resource limits dynamically, ensuring users can only select values within the configured limits when setting up their workspaces. This enhancement aims to improve user experience by preventing resource over-allocation and system overload. Documentation for the new endpoint has been added to API.md, and existing sections in ARCHITECTURE.md and INSTALLATION.md have been updated to reflect these changes.
1 parent 8e3a9e6 commit 9574ec5

4 files changed

Lines changed: 184 additions & 12 deletions

File tree

docs/API.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,36 @@ Retrieves platform configuration information.
328328
}
329329
```
330330

331+
### Resource Limits Configuration
332+
333+
Retrieves current resource limit configuration for workspace management.
334+
335+
**Endpoint**: `GET /api/limits`
336+
337+
**Response**:
338+
```json
339+
{
340+
"maxConcurrentWorkspaces": 5,
341+
"cpuPerWorkspace": 4.0,
342+
"memoryPerWorkspaceMB": 8192,
343+
"enableResourceLimits": true
344+
}
345+
```
346+
347+
**Parameters**:
348+
- `maxConcurrentWorkspaces` (integer): Maximum number of simultaneously running workspaces
349+
- `cpuPerWorkspace` (number): Maximum CPU cores allocated per workspace
350+
- `memoryPerWorkspaceMB` (integer): Maximum RAM in megabytes per workspace
351+
- `enableResourceLimits` (boolean): Whether resource limit enforcement is enabled
352+
353+
**Usage**:
354+
```bash
355+
# Check current resource limits
356+
curl http://localhost/api/limits
357+
```
358+
359+
This endpoint is used by the frontend to dynamically populate resource selection options, ensuring users can only choose values within configured limits.
360+
331361
## Git Integration
332362

333363
### List Git Repositories
@@ -410,6 +440,7 @@ All error responses follow this format:
410440
| 401 | `INVALID_WORKSPACE_PASSWORD` | Incorrect workspace password |
411441
| 404 | `PROJECT_NOT_FOUND` | Project ID does not exist |
412442
| 409 | `WORKSPACE_LIMIT_EXCEEDED` | Maximum workspaces per user reached |
443+
| 409 | `CONCURRENT_LIMIT_REACHED` | Maximum concurrent workspaces reached |
413444
| 500 | `CONTAINER_CREATE_FAILED` | Failed to create Docker container |
414445
| 500 | `GIT_REPO_CREATE_FAILED` | Failed to create Git repository |
415446
| 503 | `SERVICE_UNAVAILABLE` | Docker daemon or system unavailable |

docs/ARCHITECTURE.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,30 @@ deploy:
216216
memory: 1G
217217
```
218218
219+
**Resource Limits Configuration**:
220+
XaresAICoder implements configurable resource limits to prevent system overload:
221+
222+
- **Concurrent Workspace Limits**: Maximum number of simultaneously running workspaces (default: 3)
223+
- **Per-Workspace CPU**: Maximum CPU cores per workspace (default: 1.0)
224+
- **Per-Workspace Memory**: Maximum RAM per workspace in MB (default: 4096)
225+
226+
Configuration via environment variables:
227+
```bash
228+
MAX_CONCURRENT_WORKSPACES=3
229+
CPU_PER_WORKSPACE=1.0
230+
MEMORY_PER_WORKSPACE_MB=4096
231+
ENABLE_RESOURCE_LIMITS=true
232+
```
233+
234+
**Enforcement Mechanisms**:
235+
- **Backend**: Concurrent limit checked in `createProject()` and `startProject()` methods
236+
- **Frontend**: Dynamic select box population prevents users from selecting values beyond limits
237+
- **Docker**: Resource limits enforced via Docker's HostConfig.Memory and HostConfig.CpuShares
238+
- **API Endpoint**: `/api/limits` exposes current configuration to frontend
239+
219240
**System Services**:
220241
- nginx: 128MB RAM, 0.1 CPU
221-
- server: 512MB RAM, 0.5 CPU
242+
- server: 512MB RAM, 0.5 CPU
222243
- forgejo: 1GB RAM, 0.5 CPU
223244

224245
### Volume Management

docs/INSTALLATION.md

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ curl http://localhost/api/health
157157
| `WORKSPACE_TIMEOUT_MINUTES` | 120 | Workspace auto-stop timeout |
158158
| `MAX_WORKSPACES_PER_USER` | 5 | Maximum workspaces per user |
159159
| `ENABLE_GIT_SERVER` | false | Enable integrated Forgejo Git server |
160+
| `MAX_CONCURRENT_WORKSPACES` | 3 | Maximum concurrent running workspaces |
161+
| `CPU_PER_WORKSPACE` | 1.0 | CPU cores allocated per workspace |
162+
| `MEMORY_PER_WORKSPACE_MB` | 4096 | RAM in MB per workspace |
163+
| `ENABLE_RESOURCE_LIMITS` | true | Enable resource limit enforcement |
160164

161165
### Git Server Configuration
162166

@@ -428,20 +432,52 @@ docker compose up -d
428432

429433
### Resource Limits
430434

431-
Edit `docker-compose.yml` to adjust resource limits:
435+
XaresAICoder supports configurable resource limits to prevent system overload. Configure via `.env` file:
432436

433-
```yaml
434-
services:
435-
# Per-workspace limits
436-
deploy:
437-
resources:
438-
limits:
439-
cpus: '2.0'
440-
memory: 4G
441-
reservations:
442-
memory: 1G
437+
```bash
438+
# Maximum concurrent workspaces
439+
MAX_CONCURRENT_WORKSPACES=3
440+
441+
# CPU cores per workspace
442+
CPU_PER_WORKSPACE=1.0
443+
444+
# Memory in MB per workspace
445+
MEMORY_PER_WORKSPACE_MB=4096
446+
447+
# Enable resource limit enforcement
448+
ENABLE_RESOURCE_LIMITS=true
449+
```
450+
451+
**Configuration Examples**:
452+
453+
**Personal Development** (default):
454+
```bash
455+
MAX_CONCURRENT_WORKSPACES=3
456+
CPU_PER_WORKSPACE=1.0
457+
MEMORY_PER_WORKSPACE_MB=4096
458+
```
459+
460+
**Powerful Workstation**:
461+
```bash
462+
MAX_CONCURRENT_WORKSPACES=10
463+
CPU_PER_WORKSPACE=2.0
464+
MEMORY_PER_WORKSPACE_MB=8192
465+
```
466+
467+
**Shared Server** (conservative):
468+
```bash
469+
MAX_CONCURRENT_WORKSPACES=5
470+
CPU_PER_WORKSPACE=0.5
471+
MEMORY_PER_WORKSPACE_MB=2048
443472
```
444473

474+
**Check Current Limits**:
475+
```bash
476+
curl http://localhost/api/limits
477+
```
478+
479+
See [README - Resource Limits](../README.md#resource-limits) for detailed information.
480+
445481
### Storage Configuration
446482

447483
```bash

frontend/app.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,90 @@ class XaresAICoder {
103103
protocol: 'http'
104104
};
105105
}
106+
107+
// Load resource limits
108+
await this.loadResourceLimits();
109+
}
110+
111+
async loadResourceLimits() {
112+
try {
113+
const response = await fetch(`${this.apiBase}/limits`);
114+
const data = await response.json();
115+
116+
if (response.ok) {
117+
this.limits = data;
118+
console.log('Resource limits loaded:', this.limits);
119+
120+
// Populate select boxes based on limits
121+
this.populateResourceSelects();
122+
} else {
123+
console.error('Failed to load resource limits:', data);
124+
// Set default limits
125+
this.limits = {
126+
maxConcurrentWorkspaces: 3,
127+
cpuPerWorkspace: 1.0,
128+
memoryPerWorkspaceMB: 4096,
129+
enableResourceLimits: true
130+
};
131+
this.populateResourceSelects();
132+
}
133+
} catch (error) {
134+
console.error('Error loading resource limits:', error);
135+
// Set default limits
136+
this.limits = {
137+
maxConcurrentWorkspaces: 3,
138+
cpuPerWorkspace: 1.0,
139+
memoryPerWorkspaceMB: 4096,
140+
enableResourceLimits: true
141+
};
142+
this.populateResourceSelects();
143+
}
144+
}
145+
146+
populateResourceSelects() {
147+
// Memory options in MB
148+
const memoryOptions = [
149+
{ value: '1g', mb: 1024, label: '1 GB RAM' },
150+
{ value: '2g', mb: 2048, label: '2 GB RAM' },
151+
{ value: '4g', mb: 4096, label: '4 GB RAM' },
152+
{ value: '8g', mb: 8192, label: '8 GB RAM' },
153+
{ value: '16g', mb: 16384, label: '16 GB RAM' }
154+
];
155+
156+
// CPU options
157+
const cpuOptions = [
158+
{ value: '1', cores: 1, label: '1 Core' },
159+
{ value: '2', cores: 2, label: '2 Cores' },
160+
{ value: '4', cores: 4, label: '4 Cores' },
161+
{ value: '8', cores: 8, label: '8 Cores' }
162+
];
163+
164+
// Filter options based on limits
165+
const maxMemoryMB = this.limits.memoryPerWorkspaceMB || 4096;
166+
const maxCpu = this.limits.cpuPerWorkspace || 2.0;
167+
168+
const availableMemoryOptions = memoryOptions.filter(opt => opt.mb <= maxMemoryMB);
169+
const availableCpuOptions = cpuOptions.filter(opt => opt.cores <= maxCpu);
170+
171+
// Populate memory select
172+
const memorySelect = document.getElementById('memoryLimit');
173+
if (memorySelect) {
174+
memorySelect.innerHTML = availableMemoryOptions.map((opt, index) => {
175+
// Set 2GB as default, or the highest available if 2GB not available
176+
const isDefault = opt.value === '2g' || (opt.mb === maxMemoryMB && maxMemoryMB < 2048);
177+
return `<option value="${opt.value}" ${isDefault ? 'selected' : ''}>${opt.label}${isDefault && opt.value === '2g' ? ' (Default)' : ''}${opt.mb === maxMemoryMB ? ' (Max)' : ''}</option>`;
178+
}).join('');
179+
}
180+
181+
// Populate CPU select
182+
const cpuSelect = document.getElementById('cpuCores');
183+
if (cpuSelect) {
184+
cpuSelect.innerHTML = availableCpuOptions.map((opt, index) => {
185+
// Set 2 cores as default, or the highest available if 2 cores not available
186+
const isDefault = opt.value === '2' || (opt.cores === maxCpu && maxCpu < 2);
187+
return `<option value="${opt.value}" ${isDefault ? 'selected' : ''}>${opt.label}${isDefault && opt.value === '2' ? ' (Default)' : ''}${opt.cores === maxCpu ? ' (Max)' : ''}</option>`;
188+
}).join('');
189+
}
106190
}
107191

108192
setupUI() {

0 commit comments

Comments
 (0)