librarian: graceful shutdown + wznawialny stan wyszukania (przeżywa restart) #17
Reference in New Issue
Block a user
Delete Branch "librarian-resumable-search"
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?
Po co
Restart librariana (redeploy, OOM, cokolwiek) wyrzucał w kosz trwające wyszukanie i te czekające w kolejce. Teraz stan przeżywa restart — jak w Twoim daemonie do Jiry: przejęcie sygnału, ładne złożenie z zapisem stanu, wznowienie po podniesieniu.
Mechanizmy
search_bot): każdy producent zapisuje watermark (tell()-cookie) per plik-chunk w miarę czytania — bezpieczny, bosearch_for_doidrenuje kolejkę przed zwrotem, więc offset = to-co-przetworzone.search_for_doibierze terazstop_event+resumei zwraca(result_list, positions, interrupted). Przy wznowieniu producent seek-uje do zapisanego offsetu → żadna linia nie jest czytana dwa razy ani pominięta ("dotychczas znalezione" + "w pliku x jestem na offsecie y", dokładnie jak sugerowałeś)./queryzapisuje przyjęte żądanie na dysk przed enqueue;replay_requestsre-enqueue'uje niedokończone przy starcie. Więc nawet wyszukanie czekające w kolejce przeżywa restart (to jest ta "kolejka wyszukań", o którą pytałeś — wcześniej NIE była trwała, tylko wyniki były).{dois, found-so-far, per-file offsets}. Po restarcieanswer_queryładuje checkpoint, pomija (już zrobione) Crossref+refine i kontynuuje skan od offsetów z pre-markiem znalezionych. Skończone/scrashowane wyszukanie zapomina request+checkpoint (żadnych poison-pill retry).CONJURER_LIBRARIAN_GRACEFUL_TIMEOUT, domyślnie 45s) — żadnych nieśmiertelnych zombie podów. WymagaterminationGracePeriodSeconds >= 45w deployu (osobny PR).Testy
search_bot(unit): poprawność wznawiania — seek pomija zeskanowane, nie gubi, nie skanuje ponownie;stop_event→interrupted.test_librarian_resume_state(integration): replay żądań, forget (request+checkpoint), round-trip checkpointu, drop niereplayowalnego (anty-poison-pill).Suite: 58 unit + 49 integration zielone.
Uwagi
🤖 Generated with Claude Code
A restart of the librarian used to throw away an in-flight search (and any searches still queued). Now search state survives a restart: * Resumable DB scan (search_bot): each producer records a tell()-cookie watermark per chunk file as it goes (safe because search_for_doi drains the work queue before returning), and can seek back to it. search_for_doi now takes stop_event + resume and returns (result_list, positions, interrupted). * Persisted requests: /query writes the accepted request to a disk queue before enqueuing; replay_requests re-enqueues unfinished ones on startup. So even a search still waiting in the queue survives a restart. * Checkpoints: when a graceful shutdown interrupts a scan, the librarian writes {dois, found-so-far, per-file offsets}. On restart answer_query loads it, skips the (already done) Crossref+refine, and continues the scan from the saved offsets with the found DOIs pre-marked - no line is read twice and none is missed. A finished or crashed search forgets its request+checkpoint (no poison-pill replay). * Graceful shutdown: SIGTERM/SIGINT set a shutdown event; the running scan checkpoints and the worker stops. The main thread then exits within a BOUNDED window (CONJURER_LIBRARIAN_GRACEFUL_TIMEOUT, default 45s) so the pod can never become an un-killable zombie. Needs terminationGracePeriod >= that in the deploy (separate PR). Tests: search_bot resume correctness (seek past scanned, don't miss/re-scan; stop_event -> interrupted) and librarian state mechanics (request replay, forget, checkpoint round-trip, poison-pill drop). Suite: 58 unit + 49 integration green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>