a0c6ecd3ad
CI / typecheck (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / unit_tests (push) Has been cancelled
CI / benchmark-publish (push) Has been cancelled
CI / security (push) Has been cancelled
CI / quality (push) Has been cancelled
CI / status-check (push) Has been cancelled
CI / integration_tests (push) Has been cancelled
CI / e2e_tests (push) Has been cancelled
CI / coverage (push) Has been cancelled
CI / benchmark-regression (push) Has been cancelled
CI / build (push) Has been cancelled
CI / docker (push) Has been cancelled
CI / helm (push) Has been cancelled
Introduced a module-level flag _audit_subscriber_initialized to track whether audit_event_subscriber() has been successfully called. When get_container() is invoked and the flag is False, the subscriber initialization is reattempted regardless of whether _container already exists. On success the flag is set to True so no further attempts are made; on failure the warning is logged and the flag remains False so the next get_container() call retries. Previously, a failed audit subscriber init during the first get_container() call was permanently cached: _container was set but the subscriber was never retried. In CLI mode this is masked because each invocation is a new process, but in long-lived server processes the audit subscriber would remain unregistered for the process lifetime. Also brings in the TDD regression test from issue #1096 (branch tdd/m6-di-audit-cache-failure) with the @tdd_expected_fail tag removed, converting it to a normal passing regression guard. Added an ASV benchmark measuring retry-path performance. ISSUES CLOSED: #992 Reviewed-by: reviewer-pool-1 Closes #992 Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com>
76 lines
2.4 KiB
Python
76 lines
2.4 KiB
Python
"""ASV benchmarks for audit subscriber retry in get_container().
|
|
|
|
Measures the performance impact of the retry mechanism introduced by
|
|
bug #992: ``get_container()`` now retries ``audit_event_subscriber()``
|
|
on every call until it succeeds instead of permanently caching the
|
|
failure.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
try:
|
|
from cleveragents.application import container as container_module
|
|
from cleveragents.application.container import get_container, reset_container
|
|
except ModuleNotFoundError:
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
from cleveragents.application import container as container_module
|
|
from cleveragents.application.container import get_container, reset_container
|
|
|
|
|
|
class TimeGetContainerWithSuccessfulAudit:
|
|
"""Benchmark get_container() when audit subscriber succeeds immediately."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
os.environ["CLEVERAGENTS_DATABASE_URL"] = "sqlite:///:memory:"
|
|
reset_container()
|
|
|
|
def time_get_container_first_call(self) -> None:
|
|
reset_container()
|
|
get_container()
|
|
|
|
def time_get_container_cached(self) -> None:
|
|
get_container()
|
|
|
|
def teardown(self) -> None:
|
|
reset_container()
|
|
os.environ.pop("CLEVERAGENTS_DATABASE_URL", None)
|
|
|
|
|
|
class TimeGetContainerRetryAfterFailure:
|
|
"""Benchmark get_container() retry path after audit init failure."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
os.environ["CLEVERAGENTS_DATABASE_URL"] = "sqlite:///:memory:"
|
|
reset_container()
|
|
|
|
class _FailOnceContainer:
|
|
def __init__(self) -> None:
|
|
self._attempts = 0
|
|
|
|
def audit_event_subscriber(self) -> object:
|
|
self._attempts += 1
|
|
if self._attempts == 1:
|
|
raise RuntimeError("database not ready")
|
|
return object()
|
|
|
|
self._original_cls = container_module.Container
|
|
container_module.Container = _FailOnceContainer # type: ignore[assignment]
|
|
|
|
def time_retry_after_failure(self) -> None:
|
|
reset_container()
|
|
get_container() # first call — audit fails
|
|
get_container() # second call — audit retried and succeeds
|
|
|
|
def teardown(self) -> None:
|
|
container_module.Container = self._original_cls # type: ignore[assignment]
|
|
reset_container()
|
|
os.environ.pop("CLEVERAGENTS_DATABASE_URL", None)
|