Async Task Result Management in docling-serve#
docling-serve's async task results follow a two-phase lifecycle: an initial storage TTL (controlled per-backend) keeps results available until the client retrieves them, then a removal-delay TTL expires the Redis key shortly after first fetch. The result_removal_delay setting applies across all backends (Local, RQ, Ray); backend-specific TTLs govern how long results persist before any client access.
All orchestrators are instantiated in orchestrator_factory.py, which wires settings.py values into each orchestrator config. Per-backend settings are documented in docs/configuration.md.
Common: Single-Use Results and Removal Delay#
The global setting DOCLING_SERVE_SINGLE_USE_RESULTS (default: true) makes results consumable once. When enabled, prepare_response() registers a background task calling orchestrator.on_result_fetched(task_id) immediately after the response is serialized — triggered by any of the result-returning endpoints (/v1/result/{task_id}, /v1/convert/source, /v1/convert/file, etc.) .
DOCLING_SERVE_RESULT_REMOVAL_DELAY (default: 300 seconds) sets how quickly Redis expires a result key after on_result_fetched() is called. This delay is passed to both the RQ and Ray orchestrator configs .
The expiry is implemented via a Redis EXPIRE call rather than immediate deletion, making it crash-safe: if the API process dies between fetch and expiry, Redis will still expire the key on schedule .
RQ Backend#
Results are serialized into Redis under the prefix docling:results (configurable via DOCLING_SERVE_ENG_RQ_RESULTS_PREFIX) .
TTLs:
| Setting | Env Var | Default |
|---|---|---|
| Successful job results | DOCLING_SERVE_ENG_RQ_RESULTS_TTL | 4 hours (14400 s) |
| Failed job results | DOCLING_SERVE_ENG_RQ_FAILURE_TTL | 4 hours (14400 s) |
| After-fetch expiry | DOCLING_SERVE_RESULT_REMOVAL_DELAY | 300 s |
on_result_fetched() in RQ :
- Calls Redis
EXPIREon the result key withresult_removal_delay. - Removes the key from the in-memory
_task_result_keyslookup. - Deletes the underlying RQ
Jobobject from Redis. - Calls the base-class
delete_task()to remove in-memory tracking.
Zombie reaper: The RQ orchestrator runs a background _reap_zombie_tasks() coroutine, started at app startup and cancelled on shutdown. It wakes every DOCLING_SERVE_ENG_RQ_ZOMBIE_REAPER_INTERVAL seconds (default: 300) and evicts from the in-memory self.tasks dict any completed task whose finished_at predates DOCLING_SERVE_ENG_RQ_ZOMBIE_REAPER_MAX_AGE (default: 3600 s) . This prevents unbounded memory growth for async callers who never retrieve their result.
Ray Backend#
Results are stored under the prefix docling:ray:results (configurable via DOCLING_SERVE_ENG_RAY_RESULTS_PREFIX) . The full Redis key pattern is {results_prefix}:task:{task_id}:result . Results are msgpack-encoded StoredSuccessOutcome or StoredFailureOutcome objects.
TTLs:
| Setting | Env Var | Default |
|---|---|---|
Stored results (setex) | DOCLING_SERVE_ENG_RAY_RESULTS_TTL | 4 hours (14400 s) |
| After-fetch expiry | DOCLING_SERVE_RESULT_REMOVAL_DELAY | 300 s |
Results are stored via redis.setex() with results_ttl at write time . Retrieval uses get_task_outcome(), which reads {results_prefix}:task:{task_id}:result .
on_result_fetched() in Ray :
- Constructs the result key as
{results_prefix}:task:{task_id}:result. - Calls
redis_manager.expire_result(result_key, result_removal_delay), which issues RedisEXPIRE. - Calls the base-class
delete_task()to clean up in-memory tracking and WebSocket connections.
The Ray backend has no equivalent of the zombie reaper — its crash-safe Redis TTL and the base results_ttl are the primary expiry mechanisms.
Manual Cleanup#
The GET /v1/clear/results endpoint calls orchestrator.clear_results(older_than=<seconds>) (default: 3600 s). This endpoint requires authentication and is distinct from the automatic TTL-based expiry.
Key Files#
| File | Purpose |
|---|---|
docling_serve/settings.py | All TTL, delay, and reaper settings |
docling_serve/orchestrator_factory.py | Wires settings into orchestrator configs |
docling_serve/response_preparation.py | Triggers on_result_fetched() on response |
docling_serve/app.py | API endpoints, zombie reaper startup/shutdown |
docling_jobkit/.../rq/orchestrator.py | RQ on_result_fetched() + zombie reaper |
docling_jobkit/.../ray/orchestrator.py | Ray on_result_fetched() |
docling_jobkit/.../ray/redis_helper.py | Ray result storage, retrieval, expire_result() |
docs/configuration.md | Human-readable config reference |