Files
cleveragents-core/benchmarks/devcontainer_lifecycle_bench.py
T
HAL9000 619e8eff36 feat(resource): add --clone-into to container-instance and fix devcontainer-instance sandbox strategy
- Adds a --clone-into option to the container-instance command to clone repository contents into a specified path during container setup.
- Fixes the devcontainer-instance sandbox strategy to ensure proper isolation, correct mount permissions, and deterministic behavior across environments.
- Updates related validation and error handling to reflect the new option and sandbox changes.

ISSUES CLOSED: #7555
2026-06-02 20:37:35 -04:00

221 lines
6.7 KiB
Python

"""ASV benchmarks for devcontainer lifecycle activation latency.
Measures the performance of:
- ContainerLifecycleState transition validation
- Lifecycle tracker construction and transition
- Lazy activation with mock runner (no real subprocess)
- JSON output parsing from devcontainer up
- Lifecycle registry operations (get/set/list)
Based on issue #514: Devcontainer lifecycle management.
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
from typing import ClassVar
try:
from cleveragents.domain.models.core.container_lifecycle import (
ContainerLifecycleState,
ContainerLifecycleTracker,
transition_state,
validate_transition,
)
from cleveragents.resource.handlers.devcontainer import (
_parse_devcontainer_up_output,
activate_container,
clear_lifecycle_registry,
get_lifecycle_tracker,
list_active_containers,
set_lifecycle_tracker,
)
except ModuleNotFoundError:
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from cleveragents.domain.models.core.container_lifecycle import (
ContainerLifecycleState,
ContainerLifecycleTracker,
transition_state,
validate_transition,
)
from cleveragents.resource.handlers.devcontainer import (
_parse_devcontainer_up_output,
activate_container,
clear_lifecycle_registry,
get_lifecycle_tracker,
list_active_containers,
set_lifecycle_tracker,
)
class _MockResult:
"""Minimal mock result for benchmark subprocess calls."""
def __init__(self) -> None:
self.returncode = 0
self.stdout = json.dumps(
{
"outcome": "success",
# F3 fix: use valid 20-char hex ID to pass S1 validation
"containerId": "aabbccddee0011223344",
"remoteWorkspaceFolder": "/ws",
}
)
self.stderr = ""
def _mock_runner(args: list[str], **kwargs: object) -> _MockResult:
"""Zero-overhead mock subprocess runner."""
return _MockResult()
class TimeTransitionValidation:
"""Benchmark validate_transition throughput."""
timeout = 10
def time_valid_transition(self) -> None:
"""Time valid transition check (1000 iterations)."""
for _ in range(1000):
validate_transition(
ContainerLifecycleState.DISCOVERED,
ContainerLifecycleState.BUILDING,
)
def time_invalid_transition(self) -> None:
"""Time invalid transition check (1000 iterations)."""
for _ in range(1000):
validate_transition(
ContainerLifecycleState.DISCOVERED,
ContainerLifecycleState.RUNNING,
)
class TimeTrackerConstruction:
"""Benchmark lifecycle tracker creation."""
timeout = 10
def time_tracker_construction(self) -> None:
"""Time ContainerLifecycleTracker instantiation."""
for i in range(1000):
ContainerLifecycleTracker(
resource_id=f"01BENCH{i:020d}",
)
class TimeTransitionState:
"""Benchmark state transition with history recording."""
timeout = 30
def time_single_transition(self) -> None:
"""Time a single state transition."""
for _ in range(1000):
tracker = ContainerLifecycleTracker(
resource_id="01BENCHTRACKER0000000001",
)
transition_state(
tracker,
ContainerLifecycleState.BUILDING,
reason="benchmark",
)
class TimeActivationLatency:
"""Benchmark lazy activation with mock runner."""
timeout = 30
params: ClassVar[list[int]] = [1, 10, 50]
param_names: ClassVar[list[str]] = ["num_activations"]
def setup(self, num_activations: int) -> None:
"""Clear registry before each timing iteration (R17 fix).
Without this, trackers from prior iterations accumulate in
``_lifecycle_registry`` and subsequent activate calls fail
with invalid-transition errors (``running`` → ``building``).
"""
clear_lifecycle_registry()
def time_activation(self, num_activations: int) -> None:
"""Time container activation with mock runner.
R8-F6 fix: clear the registry after the loop to stop health
check threads spawned by each activation (F3 auto-start).
Without this, ``num_activations`` daemon threads accumulate
during the timing call.
"""
for i in range(num_activations):
activate_container(
f"01BENCHACTIVATE{i:011d}",
"/workspace",
run_command=_mock_runner,
)
clear_lifecycle_registry()
def teardown(self, num_activations: int) -> None:
"""Clear registry after each timing iteration (safety net)."""
clear_lifecycle_registry()
class TimeJsonParsing:
"""Benchmark devcontainer up JSON output parsing."""
timeout = 10
def setup(self) -> None:
"""Create sample JSON output."""
self.valid_json = json.dumps(
{
"outcome": "success",
"containerId": "aabbccddee0011223344",
"remoteWorkspaceFolder": "/workspaces/project",
}
)
self.invalid_json = "not valid json {{"
def time_parse_valid_json(self) -> None:
"""Time parsing valid JSON output."""
for _ in range(1000):
_parse_devcontainer_up_output(self.valid_json)
def time_parse_invalid_json(self) -> None:
"""Time parsing invalid JSON output."""
for _ in range(1000):
_parse_devcontainer_up_output(self.invalid_json)
class TimeRegistryOperations:
"""Benchmark lifecycle registry get/set/list operations."""
timeout = 30
def setup(self) -> None:
"""Pre-populate registry with trackers."""
clear_lifecycle_registry()
for i in range(100):
tracker = ContainerLifecycleTracker(
resource_id=f"01BENCHREG{i:016d}",
current_state=ContainerLifecycleState.RUNNING
if i % 2 == 0
else ContainerLifecycleState.DISCOVERED,
)
set_lifecycle_tracker(tracker)
def teardown(self) -> None:
"""Clear registry."""
clear_lifecycle_registry()
def time_get_tracker(self) -> None:
"""Time registry lookup."""
for i in range(1000):
get_lifecycle_tracker(f"01BENCHREG{(i % 100):016d}")
def time_list_active(self) -> None:
"""Time listing active containers."""
for _ in range(100):
list_active_containers()