Files
cleveragents-core/benchmarks/devcontainer_lifecycle_bench.py
T
freemo c65e8a5285
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 16s
CI / lint (pull_request) Successful in 17s
CI / quality (pull_request) Successful in 29s
CI / typecheck (pull_request) Successful in 59s
CI / security (pull_request) Successful in 59s
CI / unit_tests (pull_request) Successful in 3m9s
CI / integration_tests (pull_request) Successful in 3m34s
CI / e2e_tests (pull_request) Successful in 3m54s
CI / docker (pull_request) Successful in 55s
CI / coverage (pull_request) Successful in 6m1s
CI / build (push) Successful in 15s
CI / lint (push) Successful in 16s
CI / quality (push) Successful in 27s
CI / typecheck (push) Successful in 38s
CI / benchmark-regression (push) Has been skipped
CI / security (push) Successful in 48s
CI / unit_tests (push) Successful in 3m14s
CI / integration_tests (push) Successful in 3m30s
CI / docker (push) Successful in 56s
CI / e2e_tests (push) Successful in 4m43s
CI / coverage (push) Successful in 6m1s
CI / benchmark-publish (push) Successful in 19m50s
CI / benchmark-regression (pull_request) Successful in 37m17s
feat(resource): add cloud infrastructure resources
Implement cloud resource types (aws, gcp, azure) with credential
fields, region/tenant metadata, and stubbed sandbox strategies.
Credential resolution uses environment variables and profile names
with no secrets logged.

Key changes:
- Add CloudResourceHandler with aws/gcp/azure type definitions
- Add credential resolution from env vars and profile names
- Add stubbed sandbox strategies (validate config, raise NotImplementedError)
- Register cloud types in bootstrap_builtin_types
- Credential masking via existing redaction patterns
- Add Behave BDD tests, Robot integration tests, ASV benchmarks

ISSUES CLOSED: #343
2026-03-17 13:14:37 -04:00

221 lines
6.6 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.DETECTED,
ContainerLifecycleState.BUILDING,
)
def time_invalid_transition(self) -> None:
"""Time invalid transition check (1000 iterations)."""
for _ in range(1000):
validate_transition(
ContainerLifecycleState.DETECTED,
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.DETECTED,
)
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()