Reliability¶
docgate makes provider failure visible while allowing an ordered fallback ladder. The receipt records each degradation, so a successful fallback does not hide a failed primary.
Circuit breakers¶
CircuitBreakers protects a provider from repeated calls after it fails enough
times. Configure the process-wide defaults with configure():
docgate.configure(
providers=[primary_ocr, backup_vision],
breaker_failures=3,
breaker_cooldown_s=120,
)
After the configured number of failures, new calls to that provider are skipped
until its cooldown passes. Then exactly one recovery probe is allowed. A
successful probe closes the breaker; a failed probe starts a fresh cooldown.
When a breaker prevents a call, the receipt has a Degradation with
reason="breaker_open", and docgate moves to the next provider if one exists.
For call-specific control, construct CircuitBreakers and pass it as
breakers to execute().
Timeouts, retries, and backoff¶
ExecutionPolicy applies to one provider/page call. It sets a logical timeout,
the maximum number of attempts, and bounded exponential backoff.
import docgate
policy = docgate.ExecutionPolicy(
timeout_s=30,
max_attempts=3,
initial_backoff_s=0.5,
backoff_multiplier=2,
max_backoff_s=4,
)
result = docgate.read(
"scan.pdf",
providers=[primary_ocr, backup_vision],
execution_policy=policy,
)
The delay after failed attempt n is
min(initial_backoff_s * backoff_multiplier ** (n - 1), max_backoff_s).
Failures and timeouts create Degradation entries with an attempt number.
Retries point fell_back_to at the same provider; after the final failed
attempt, the field identifies the next provider when there is one.
Important: a timeout ends docgate's wait for the callback, but Python cannot safely force-kill a running native or local thread. The underlying task may continue in the background. Built-in remote adapters also have transport-level timeouts. Use provider/client controls appropriate to your environment; hard termination through subprocess isolation is not currently provided by docgate.
Progress events¶
Pass on_progress to read() or execute() for operational metadata. A
ProgressEvent includes a page number, event name, optional provider, optional
attempt, and optional detail—never extracted text, images, filenames, or keys.
def log_progress(event: docgate.ProgressEvent) -> None:
print(event.page, event.event, event.provider, event.attempt, event.detail)
result = docgate.read("scan.pdf", providers=[primary_ocr], on_progress=log_progress)
Current event names are page_skipped, page_unknown, page_cached,
provider_skipped, provider_started, provider_failed, provider_rejected,
and page_completed. Exceptions raised by the callback are ignored so
observability cannot change extraction correctness.