orchestrata_ultimate.kernel.performance.chaos
Reference › orchestrata_ultimate › kernel › performance › chaos
Fault injection for resilience testing, SPEC-001 requirement REQ-001-US-010-030. ChaosController is disabled by default; when enabled, callers ask it whether to inject a fault at a given point. The manager owns one and exposes it through enable_chaos_testing, add_chaos_fault and maybe_inject_chaos.
The nested FaultType enum and FaultConfig dataclass describe a fault. add_fault and remove_fault register faults by id, enable and disable switch the controller. maybe_inject returns False at once when the controller is disabled or no registered fault matches the requested type and component (_select_fault). When one matches, _apply_fault acts outside the lock: a latency fault sleeps for the configured milliseconds, an error fault raises a RuntimeError naming the fault id, and a timeout fault sleeps and then raises a TimeoutError. get_stats reports the controller's counters.
At a glance
| Source | src/orchestrata_ultimate/kernel/performance/chaos.py |
| Lines of code | 151 |
| Classes | 1 |
| Functions | 0 |
| Imports (internal) | orchestrata_ultimate.core.base.traces |
| Imported by | orchestrata_ultimate.kernel.performance, orchestrata_ultimate.kernel.performance.manager |
| Tests that import it | none |
Class diagram
Dependency diagram
API
Classes
ChaosController
Decorators: traces(spec='001', story='US-010', reqs=['REQ-001-US-010-030']) · Line 15
Chaos engineering controller (REQ-001-US-010-030).
| Method | Signature | Summary |
|---|---|---|
__init__ |
__init__(enabled: bool = False) -> None |
Initialize chaos controller. |
enable |
enable() -> None |
Enable chaos testing. |
disable |
disable() -> None |
Disable chaos testing. |
add_fault |
add_fault(fault_id: str, config: ChaosController.FaultConfig) -> None |
Add fault configuration. |
remove_fault |
remove_fault(fault_id: str) -> None |
Remove fault configuration. |
maybe_inject |
async maybe_inject(fault_type: ChaosController.FaultType, component: str | None = None) -> bool |
Possibly inject a fault. |
_select_fault |
_select_fault(fault_type: ChaosController.FaultType, component: str | None) -> tuple[str | None, ChaosController.FaultConfig | None] |
Pick a matching, probability-triggered fault under the lock. |
_apply_fault staticmethod |
async _apply_fault(fault_id: str | None, config: ChaosController.FaultConfig) -> None |
Act on a selected fault outside the lock (sleep and/or raise). |
get_stats |
get_stats() -> dict[str, Any] |
Get chaos testing statistics. |
Source
# mypy: ignore-errors
"""Performance subsystem: chaos."""
import asyncio
import collections
import threading
from dataclasses import dataclass
from enum import Enum
from typing import Any
from orchestrata_ultimate.core.base.traces import traces
@traces(spec="001", story="US-010", reqs=["REQ-001-US-010-030"])
class ChaosController:
"""Chaos engineering controller (REQ-001-US-010-030).
Injects failures for testing system resilience.
"""
class FaultType(Enum):
"""Types of faults to inject."""
LATENCY = "latency"
ERROR = "error"
TIMEOUT = "timeout"
PARTITION = "partition"
RESOURCE_EXHAUSTION = "resource_exhaustion"
@dataclass
class FaultConfig:
"""Configuration for a fault."""
fault_type: ChaosController.FaultType
probability: float # 0-1
duration_ms: float
target: str | None # Specific component or None for all
enabled: bool = True
def __init__(self, enabled: bool = False) -> None:
"""Initialize chaos controller.
Args:
enabled: Whether chaos testing is enabled
"""
self._enabled = enabled
self._faults: dict[str, ChaosController.FaultConfig] = {}
self._injected_count: dict[str, int] = collections.defaultdict(int)
self._lock = threading.Lock()
def enable(self) -> None:
"""Enable chaos testing."""
self._enabled = True
def disable(self) -> None:
"""Disable chaos testing."""
self._enabled = False
def add_fault(self, fault_id: str, config: ChaosController.FaultConfig) -> None:
"""Add fault configuration.
Args:
fault_id: Unique fault identifier
config: Fault configuration
"""
with self._lock:
self._faults[fault_id] = config
def remove_fault(self, fault_id: str) -> None:
"""Remove fault configuration.
Args:
fault_id: Fault identifier to remove
"""
with self._lock:
self._faults.pop(fault_id, None)
async def maybe_inject(
self,
fault_type: ChaosController.FaultType,
component: str | None = None,
) -> bool:
"""Possibly inject a fault.
Args:
fault_type: Type of fault to potentially inject
component: Component requesting (for targeted faults)
Returns:
True if fault was injected
"""
if not self._enabled:
return False
# Capture fault config under lock, then release before any await/raise
matched_fault_id, matched_config = self._select_fault(fault_type, component)
if matched_config is None:
return False
# Act on matched fault OUTSIDE the lock — safe to await/raise here
await self._apply_fault(matched_fault_id, matched_config)
return True
def _select_fault(
self,
fault_type: ChaosController.FaultType,
component: str | None,
) -> tuple[str | None, ChaosController.FaultConfig | None]:
"""Pick a matching, probability-triggered fault under the lock.
Returns ``(fault_id, config)`` for a triggered fault, or
``(None, None)`` if nothing matched. Increments the injected counter
for the selected fault.
"""
import random
with self._lock:
for fault_id, config in self._faults.items():
if not config.enabled:
continue
if config.fault_type != fault_type:
continue
if config.target and config.target != component:
continue
if random.random() < config.probability:
self._injected_count[fault_id] += 1
return fault_id, config
return None, None
@staticmethod
async def _apply_fault(
fault_id: str | None,
config: ChaosController.FaultConfig,
) -> None:
"""Act on a selected fault outside the lock (sleep and/or raise)."""
if config.fault_type == ChaosController.FaultType.LATENCY:
await asyncio.sleep(config.duration_ms / 1000.0)
elif config.fault_type == ChaosController.FaultType.ERROR:
raise RuntimeError(f"Chaos: Injected error ({fault_id})")
elif config.fault_type == ChaosController.FaultType.TIMEOUT:
await asyncio.sleep(config.duration_ms / 1000.0)
raise TimeoutError(f"Chaos: Injected timeout ({fault_id})")
def get_stats(self) -> dict[str, Any]:
"""Get chaos testing statistics."""
with self._lock:
return {
"enabled": self._enabled,
"fault_count": len(self._faults),
"injected": dict(self._injected_count),
}