ir.maintenance¶
Idempotent background-work runner — ir maintain (issue #58).
The executing half of ir’s maintenance story (the declarative half is
ir.policy). maintain() reads each corpus’s resolved
MaintenancePolicy and does the work that is due:
an incremental rebuild when reindex is due (
source-changeis always due — the build is a near-no-op when nothing changed;intervalonly when stale;manualnever);when
synopsis.enabled, the rebuild’s strategy is wrapped inir.with_synopsis(), so new / changed artifacts gain an LLM synopsis —and because that build may call an LLM, it is run **only inside the policy’s downtime window**.
It is safe to call as often as a scheduler likes: it no-ops when nothing is due,
and records last_maintained so interval policies converge. ir runs the work;
scheduling it is external — a cron / launchd entry calls ir maintain --all
every N minutes, and the downtime window lives in the policy (data), not in ir.
ir.schedule installs that entry for you (ir schedule) and reports on it;
it writes a definition and exits, so the executor is still cron / launchd:
# what `ir schedule --every 15m` writes on a cron machine:
*/15 * * * * <python> -m ir maintain --all >> ~/.cache/ir/maintain.log 2>&1
Because a scheduled run has nobody watching it, maintain() is fault-isolated
per corpus: one corpus failing is recorded on its own result and the sweep carries
on, rather than aborting and leaving the corpora after it silently unmaintained.
Module Attributes
A lock older than this is assumed to belong to a run that was killed rather than one still working. |
Functions
Where the single-run lock lives (regenerable, so the cache dir). |
|
|
Run due background work for one corpus (name) or every registered one (all). |
|
Do the due background work for one corpus (idempotent). |
|
Hold the maintenance lock, or raise |
Classes
|
What |
Exceptions
Another maintenance run holds the lock. |
- ir.maintenance.DFLT_STALE_LOCK_AFTER = datetime.timedelta(seconds=21600)¶
A lock older than this is assumed to belong to a run that was killed rather than one still working. Generous, because a first full build of a large corpus legitimately takes a long time and reclaiming a live lock is the bad outcome.
- exception ir.maintenance.MaintenanceBusy[source]¶
Bases:
RuntimeErrorAnother maintenance run holds the lock.
- class ir.maintenance.MaintenanceResult(name, ran, reason, reindex=False, synopsis=False, records=None, error=None)[source]¶
Bases:
objectWhat
maintain_corpus()did (or would do) for one corpus.
- ir.maintenance.lock_path()[source]¶
Where the single-run lock lives (regenerable, so the cache dir).
- Return type:
- ir.maintenance.maintain(name=None, *, all=False, now=None, dry_run=False)[source]¶
Run due background work for one corpus (name) or every registered one (all).
With neither, defaults to all registered corpora. Returns one
MaintenanceResultper corpus considered.A named corpus raises; a sweep records. Asking for one corpus by name is a direct request, and swallowing its error would turn a typo’d name into a result that reads as success to any caller written before
errorexisted. A sweep is the unattended case, where the opposite is true: one corpus failing must not leave every corpus after it stale with nothing to say so.- Return type:
- ir.maintenance.maintain_corpus(name, *, now=None, dry_run=False, full=True)[source]¶
Do the due background work for one corpus (idempotent).
Reads the corpus’s resolved policy and its
last_maintainedtime, decides whether a (synopsis-aware) reindex is due and permitted now, and — unlessdry_run— runs the incremental build and records the run.- Return type:
- ir.maintenance.single_run(*, path=None, stale_after=None)[source]¶
Hold the maintenance lock, or raise
MaintenanceBusy.Two maintenance runs on one corpus store are not safe: the packed store rewrites
matrix/ids/metasas separate files, so interleaved writers can leave a matrix whose rows no longer line up with its ids — an index that answers confidently and wrongly, with nothing raised. launchd already runs one instance per label, but that does not cover cron (which happily stacks runs) or the manualir maintain --allthis tool’s own menu suggests while the agent may be mid-run.A lock whose owning process is gone, or which is older than stale_after, is reclaimed — a killed run must not wedge maintenance forever.