Emit a docling_serve.request span per instance attempt
This commit is contained in:
parent
da79f92af1
commit
73ba764922
3 changed files with 40 additions and 2 deletions
|
|
@ -4,6 +4,7 @@
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Logfire spans carry `service.version` and a per-process `service.name` (`haiku-ingester`, `haiku-rag`, `haiku-rag-app`); `OTEL_SERVICE_NAME` / `LOGFIRE_SERVICE_NAME` override the default.
|
- Logfire spans carry `service.version` and a per-process `service.name` (`haiku-ingester`, `haiku-rag`, `haiku-rag-app`); `OTEL_SERVICE_NAME` / `LOGFIRE_SERVICE_NAME` override the default.
|
||||||
|
- Each docling-serve request emits a `docling_serve.request` span carrying the instance `url` and `attempt`.
|
||||||
|
|
||||||
## [0.65.0] - 2026-07-09
|
## [0.65.0] - 2026-07-09
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import httpx
|
||||||
|
|
||||||
from haiku.rag.circuit_breaker import CircuitBreaker
|
from haiku.rag.circuit_breaker import CircuitBreaker
|
||||||
from haiku.rag.config import CircuitBreakerConfig, DoclingServeConfig
|
from haiku.rag.config import CircuitBreakerConfig, DoclingServeConfig
|
||||||
|
from haiku.rag.telemetry import logfire
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -156,8 +157,14 @@ class DoclingServeClient:
|
||||||
base_url = self._pick_url(exclude=frozenset(tried))
|
base_url = self._pick_url(exclude=frozenset(tried))
|
||||||
breaker = self._breaker_for(base_url)
|
breaker = self._breaker_for(base_url)
|
||||||
try:
|
try:
|
||||||
async with self._httpx_client() as client:
|
with logfire.span(
|
||||||
result = await attempt(client, base_url)
|
"docling_serve.request",
|
||||||
|
name=name,
|
||||||
|
url=base_url,
|
||||||
|
attempt=attempt_no,
|
||||||
|
):
|
||||||
|
async with self._httpx_client() as client:
|
||||||
|
result = await attempt(client, base_url)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
if not _is_retryable(exc):
|
if not _is_retryable(exc):
|
||||||
raise
|
raise
|
||||||
|
|
|
||||||
|
|
@ -301,6 +301,36 @@ async def test_task_failure_is_not_retried():
|
||||||
assert set(seen) == {"h"}
|
assert set(seen) == {"h"}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_request_span_records_instance_per_attempt(monkeypatch):
|
||||||
|
"""Each attempt opens a docling_serve.request span tagged with the
|
||||||
|
instance URL, so failover is traceable in Logfire."""
|
||||||
|
from contextlib import nullcontext
|
||||||
|
|
||||||
|
from haiku.rag.providers import docling_serve as ds_module
|
||||||
|
|
||||||
|
spans: list[dict] = []
|
||||||
|
|
||||||
|
def _fake_span(span_name, /, **attrs):
|
||||||
|
spans.append({"span_name": span_name, **attrs})
|
||||||
|
return nullcontext()
|
||||||
|
|
||||||
|
monkeypatch.setattr(ds_module.logfire, "span", _fake_span)
|
||||||
|
|
||||||
|
transport, _ = _failover_transport({"down-s"}, "t", {"ok": True})
|
||||||
|
client = DoclingServeClient(
|
||||||
|
base_urls=["http://down-s:5001", "http://up-s:5001"],
|
||||||
|
transport=transport,
|
||||||
|
retry_base_delay=0.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
await _poll(client)
|
||||||
|
|
||||||
|
requests = [s for s in spans if s["span_name"] == "docling_serve.request"]
|
||||||
|
assert [s["url"] for s in requests] == ["http://down-s:5001", "http://up-s:5001"]
|
||||||
|
assert [s["attempt"] for s in requests] == [0, 1]
|
||||||
|
|
||||||
|
|
||||||
def test_pick_url_skips_excluded_instances():
|
def test_pick_url_skips_excluded_instances():
|
||||||
"""On retry, _pick_url advances past every excluded instance."""
|
"""On retry, _pick_url advances past every excluded instance."""
|
||||||
urls = ["http://p1:5001", "http://p2:5001", "http://p3:5001"]
|
urls = ["http://p1:5001", "http://p2:5001", "http://p3:5001"]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue