llm: embed fan-out aborts the whole index run when one pool host dies mid-run #796
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Seen 2026-09-22 in the scheduled CMS-2026-2377 re-farm (#615):
stack llm index --collection comments --docket CMS-2026-2377died withConnectError: [Errno -2] Name or service not knownraised fromllm.pool.embed_texts→client.post(f"{host}/api/embed").HostPool.check()had already filtered the pool to live hosts at start, butrig.local(mDNS) stopped resolving partway through, andembed_texts.runhas no recovery: the first transport error on any batch propagates throughfuture.result()and the whole run exits 1 — every remaining batch, on the hosts that were still fine, is abandoned. The same happened on 2026-09-04 (comments: ConnectError: [Errno -2] Name or service not knownin the runner output, #615 thread).Fix: on
httpx.TransportError(ConnectError / ConnectTimeout / ReadTimeout / RemoteProtocolError) from a host, drop that host from the pool and retry the batch on another live host; only when no host is left raise a clearRuntimeError.HostPool.acquire()on an empty pool must raise the same error instead ofmin()'sValueError. Keepraise_for_statusfailures (a host that answers 500) as-is — that is a model/config problem, not a dead host.Done when:
tests/llm/test_pool.pycovers (a) a batch that hits a dying host completes on the survivor and the dead host is gone frompool.hosts, (b) every host dying raisesRuntimeErrornaming the last failure; and the re-farm's index step survives a host disappearing mid-run.Fixed in
cbc55c9, merged to main asd83e955.embed_texts.runnow catcheshttpx.TransportErrorfrom a host, calls the newHostPool.drop(host)and retries the batch on the remaining hosts; when none are left it raisesRuntimeError("every Ollama host failed; last <host>: <exc>").acquire()/acquire_generation()on an empty pool raise the same clear error instead ofValueErrorfrommin()/max(). HTTP error responses still surface throughraise_for_status. Tests: dying host dropped + batch completes on the survivor (in-flight map clean), every host dying raises, empty-pool acquire is a clear error —tests/llm/test_pool.py24 passed; llm suite green in the pre-commit run. The deployedllmimage needs a roll to pick this up for chat-side embeds; the host-sidestack llm index(what the re-farm cron runs) uses it as soon as the checkout is on main.