Skip to content

Commit 21eb0f5

Browse files
core: services: disk_usage: Add dynamic disk speed test
Signed-off-by: Patrick José Pereira <patrickelectric@gmail.com>
1 parent 1fd0a57 commit 21eb0f5

File tree

1 file changed

+63
-15
lines changed

1 file changed

+63
-15
lines changed

core/services/disk_usage/main.py

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
import time
1010
from functools import wraps
1111
from pathlib import Path
12-
from typing import Any, Dict, List, Optional
12+
from typing import Any, AsyncGenerator, Dict, List, Optional
1313

1414
from commonwealth.utils.apis import GenericErrorHandlingRoute, PrettyJSONResponse
1515
from commonwealth.utils.logs import InterceptHandler, init_logger
1616
from commonwealth.utils.sentry_config import init_sentry_async
17+
from commonwealth.utils.streaming import streamer
1718
from fastapi import APIRouter, FastAPI, HTTPException, Query, status
19+
from fastapi.responses import StreamingResponse
1820
from fastapi_versioning import VersionedFastAPI, versioned_api_route
1921
from loguru import logger
2022
from pydantic import BaseModel, Field
@@ -56,6 +58,12 @@ class DiskSpeedResult(BaseModel):
5658
error: Optional[str] = Field(None, description="Error message if test failed")
5759

5860

61+
class DiskSpeedTestPoint(BaseModel):
62+
size_mb: int = Field(..., description="Test size in MB")
63+
write_speed: Optional[float] = Field(None, description="Write speed in MiB/s")
64+
read_speed: Optional[float] = Field(None, description="Read speed in MiB/s")
65+
66+
5967
DiskNode.update_forward_refs()
6068

6169

@@ -303,20 +311,8 @@ def parse_disktest_speed(output: str) -> tuple[Optional[float], Optional[float],
303311
return write_speed, read_speed, seed
304312

305313

306-
# pylint: disable=too-many-locals
307-
@disk_router.get(
308-
"/speed",
309-
response_model=DiskSpeedResult,
310-
summary="Run disk speed test using disktest binary.",
311-
)
312-
@to_http_exception
313-
async def disk_speed(
314-
size_bytes: int = Query(
315-
1024 * 1024 * 1024,
316-
ge=1024 * 1024,
317-
description="Number of bytes to test (default 1 GiB).",
318-
),
319-
) -> DiskSpeedResult:
314+
async def run_single_speed_test(size_bytes: int) -> DiskSpeedResult:
315+
"""Run a single disk speed test and return the result."""
320316
disktest_binary = "disktest"
321317
temp_file_path: Optional[Path] = None
322318

@@ -408,6 +404,58 @@ async def disk_speed(
408404
logger.warning(f"Failed to clean up temporary file {temp_file_path}: {e}")
409405

410406

407+
# pylint: disable=too-many-locals
408+
@disk_router.get(
409+
"/speed",
410+
response_model=DiskSpeedResult,
411+
summary="Run disk speed test using disktest binary.",
412+
)
413+
@to_http_exception
414+
async def disk_speed(
415+
size_bytes: int = Query(
416+
1024 * 1024 * 1024,
417+
ge=1024 * 1024,
418+
description="Number of bytes to test (default 1 GiB).",
419+
),
420+
) -> DiskSpeedResult:
421+
return await run_single_speed_test(size_bytes)
422+
423+
424+
async def multi_size_speed_test_generator() -> AsyncGenerator[str, None]:
425+
"""Generator that runs speed tests at multiple sizes and yields JSON results."""
426+
import json
427+
428+
test_sizes_mb = [10, 50, 100, 200]
429+
430+
for size_mb in test_sizes_mb:
431+
size_bytes = size_mb * 1024 * 1024
432+
result = await run_single_speed_test(size_bytes)
433+
434+
point = DiskSpeedTestPoint(
435+
size_mb=size_mb,
436+
write_speed=result.write_speed_mbps,
437+
read_speed=result.read_speed_mbps,
438+
)
439+
yield json.dumps(point.dict())
440+
441+
442+
@disk_router.get(
443+
"/speed/stream",
444+
summary="Run multi-size disk speed test with streaming results.",
445+
)
446+
async def disk_speed_stream() -> StreamingResponse:
447+
return StreamingResponse(
448+
streamer(multi_size_speed_test_generator(), heartbeats=1.0),
449+
media_type="application/x-ndjson",
450+
headers={
451+
"Content-Type": "application/x-ndjson",
452+
"Cache-Control": "no-cache",
453+
"Connection": "keep-alive",
454+
"X-Accel-Buffering": "no",
455+
},
456+
)
457+
458+
411459
fast_api_app = FastAPI(
412460
title="Disk Usage API",
413461
description="Inspect disk usage and delete files using du.",

0 commit comments

Comments
 (0)