app.services.polling¶
Poll orchestration: list, skip unchanged, fetch, ingest, reconcile, log.
PollingService polls registrations one after another in
configuration order (ownership depends on that order). Each registration
runs in three phases:
Run log – an
intake_runrow is started in its own unit of work, so it survives a rollback of the ingest transaction.Collect (no transaction held, no lock) – open the adapter (
open_adapter), list projects, list each project’s documents with the stored change tokens (bounded concurrency), drop documents owned by an earlier registration (ownership_conflictwarning), fetch only changed documents (bounded concurrency) and prepare them with the shared ingest path (app.services.ingest.prepare_document()).Apply – under
lock_registration()(one transaction): ingest the fetched documents, rewrite change tokens of unchanged documents whose token moved, and delete published rows that a complete listing no longer contains.
Finally, in a separate unit of work, the run is finished and the
registration’s last_attempt_at / last_success_at / last_status /
last_error are set.
Reconciliation rules: nothing is deleted unless the project listing was complete; within a project, nothing is deleted unless its document listing was complete. A vanished project (complete project listing) loses its published rows. Candidates are never deleted by polling.
Ownership: the first enabled registration in configuration order whose
stored published rows contain a (project, path) owns it; later
registrations skip that file (and drop their own stale copy).
Statuses: failed when no project listing could be produced, the
adapter could not be opened or the write failed (prior content is kept);
partial when any listing was incomplete or any fetch failed;
otherwise ok. A failure in one registration never affects others.
Public API: DEFAULT_CONCURRENCY, PollingService, and
re-exported from app.services.poll_plan: PollProblem,
PollCounts, PollOutcome.
- app.services.polling.DEFAULT_CONCURRENCY = 8¶
Default bound for concurrent listings and fetches per registration.
- class app.services.polling.PollCounts(projects=0, documents=0, unchanged=0, fetched=0, created=0, updated=0, same_content=0, tokens_refreshed=0, deleted=0, skipped_owned=0, failed=0)¶
Bases:
objectCounters of one poll run.
- Parameters:
projects (
int) – Projects listed.documents (
int) – Documents listed and owned by the registration.unchanged (
int) – Documents skipped without fetching.fetched (
int) – Documents fetched.created (
int) – New published rows.updated (
int) – Rows whose digest changed.same_content (
int) – Fetched rows whose digest did not change.tokens_refreshed (
int) – Unchanged rows whose change token moved.deleted (
int) – Published rows removed by reconciliation.skipped_owned (
int) – Files skipped (owned by an earlier registration).failed (
int) – Listings or fetches that failed.
- class app.services.polling.PollOutcome(*, source_id, status, run_id, counts, problems)¶
Bases:
objectResult of polling one registration.
- Parameters:
source_id (
str) – Registration id.status (
PollStatus) – Outcome (disabledwhen not pollable).run_id (
UUID|None) – Intake run id (Nonewhen nothing ran).counts (
PollCounts) – Counters.problems (
tuple[PollProblem,...]) – Findings.
- status: PollStatus¶
- counts: PollCounts¶
- problems: tuple[PollProblem, ...]¶
- class app.services.polling.PollProblem(*, code, message, project=None, path=None)¶
Bases:
objectOne finding of a poll run.
- class app.services.polling.PollingService(provider, sources, *, concurrency=8, clock=<function _utcnow>)¶
Bases:
objectPolls source registrations into the catalog.
- Parameters:
provider (
CatalogStoreProvider) – Storage provider.sources (
Sequence[SourceRegistration]) – All configured registrations, in configuration order.concurrency (
int) – Bound for concurrent listings and fetches.clock (
Callable[[],datetime]) – Time source forlast_attempt_at.
- async poll_all(*, trigger=None)¶
Poll every pollable registration, in configuration order.
- Parameters:
trigger (
JsonValue) – Who triggered the poll (stored, no secrets).- Return type:
- Returns:
One outcome per pollable registration.
- async poll_source(source_id, *, trigger=None)¶
Poll one registration.
- Parameters:
- Return type:
- Returns:
The outcome;
disabled(nothing ran) when the registration is invalid or has nopollintake.- Raises:
NotFoundError – If no such registration is configured.