Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/features/prometheus-metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ below are the **actual** series names — none carry a `cachekit_` prefix.
> The `serializer` label is the tier that served the record, not the `@cache(serializer=...)`
> preset: `rust` = L2 backend path, `l1_memory` = L1 in-memory hit; `unknown` is the value
> recorded when the emitting call site passes no serializer (an instrumentation gap, not a
> tier).
> tier). Only failure records (`success="False"`) can carry `unknown`: every success record
> names its tier.

### Counters (always increasing)

Expand Down
16 changes: 12 additions & 4 deletions tests/unit/test_operation_context_tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from cachekit.decorators.orchestrator import FeatureOrchestrator
from cachekit.reliability.async_metrics import AsyncMetricsCollector
from cachekit.reliability.circuit_breaker import CircuitBreakerConfig
from cachekit.reliability.circuit_breaker import CircuitBreaker, CircuitBreakerConfig


@pytest.fixture
Expand Down Expand Up @@ -53,16 +53,24 @@ def test_record_failure_defaults_when_context_not_set(self, recorded: list[dict[

assert [(c["operation"], c["duration_ms"]) for c in recorded] == [("cache_operation", 0.0)]

def test_record_success_emits_no_metric(self, recorded: list[dict[str, Any]]):
"""Every success site records its own labelled metric; a second record here double-counts."""
def test_record_success_feeds_breaker_and_emits_no_metric(
self, recorded: list[dict[str, Any]], monkeypatch: pytest.MonkeyPatch
):
"""Every success site records its own labelled metric; a second record here double-counts.

Feeding the breaker is record_success()'s only job: a HALF_OPEN probe slot is released
only when its outcome reaches the breaker.
"""
orchestrator = _orchestrator(circuit_breaker_enabled=True)
on_success_calls: list[CircuitBreaker] = []
monkeypatch.setattr(CircuitBreaker, "_on_success", lambda self: on_success_calls.append(self))

for _ in range(3):
orchestrator.set_operation_context("get", duration_ms=1.5)
orchestrator.record_success()

assert recorded == []
assert orchestrator.circuit_breaker.get_stats()["state"] == "CLOSED"
assert on_success_calls == [orchestrator.circuit_breaker] * 3

def test_context_isolation_between_operations(self, recorded: list[dict[str, Any]]):
orchestrator = _orchestrator()
Expand Down
Loading