Parallélisme de Docling
Deux paramètres déterminent combien de documents sont convertis en même temps par Docling : QANSWER_CELERY_DOCLING_POOL et QANSWER_CELERY_DOCLING_CONCURRENCY. Cette page explique comment les choisir en fonction du nombre de réplicas du backend Python et de réplicas de workers Docling que vous exploitez.
Comment un document arrive dans Docling
Chaque PDF ou document bureautique indexé passe par trois étapes :
- Chaque réplica du backend Python exécute un worker Celery qui consomme la file docling_extraction_queue. Ces deux paramètres configurent ce worker.
- Le serveur d'API Docling (docling, QANSWER_DOCLING_ENDPOINT) reçoit chaque requête et la place dans une file Redis (RQ).
- Les réplicas de workers Docling (docling-worker) prennent les tâches dans cette file et les convertissent. Chaque réplica exécute un worker RQ, qui traite une tâche à la fois : il compte donc pour un convertisseur.
Une tâche Celery correspond à une requête HTTP bloquante vers le serveur d'API pour un lot de pages. Un lot compte 8 pages, sauf si vous définissez QANSWER_INDEXING_PAGE_BATCH_SIZE (vide : valeur par défaut du backend).
Ce que contrôlent ces paramètres
- QANSWER_CELERY_DOCLING_POOL est le type de pool Celery. La valeur par défaut du bundle est prefork.
- QANSWER_CELERY_DOCLING_CONCURRENCY est le nombre d'emplacements de ce pool. Le bundle utilise 2 par défaut.
Avec prefork, N emplacements maintiennent N conversions en cours par réplica du backend. Le nombre total de requêtes envoyées à Docling est donc le nombre de réplicas du backend multiplié par la concurrence.
La règle
Visez environ deux requêtes en cours par worker Docling : une en cours de conversion et une en attente, pour qu'un convertisseur ne reste jamais inactif entre deux tâches. Avec QANSWER_SCALING_PYTHON_REPLICAS_NUMBER réplicas du backend et QANSWER_SCALING_DOCLING_WORKER_REPLICAS_NUMBER workers Docling, on obtient :
concurrency = max(1, round(2 × docling workers ÷ backend replicas))Arrondissez à l'entier le plus proche, avec un minimum de 1 : avec moins de workers Docling que de réplicas du backend, gardez 1 par backend.
Configurations courantes
| Réplicas du backend | Workers Docling | Concurrence | Requêtes en cours | Cible |
|---|---|---|---|---|
| 1 | 1 | 2 | 2 | 2 |
| 1 | 2 | 4 | 4 | 4 |
| 2 | 2 | 2 | 4 | 4 |
| 2 | 4 | 4 | 8 | 8 |
| 3 | 2 | 1 | 3 | 4 |
| 3 | 4 | 3 | 9 | 8 |
| 3 | 6 | 4 | 12 | 12 |
| 4 | 2 | 1 | 4 | 4 |
Les valeurs par défaut du bundle (2 réplicas du backend, 2 workers Docling) correspondent à la troisième ligne.
Exemple concret
Vous exploitez 3 réplicas du backend et 4 workers Docling. La cible est de 2 x 4 = 8 requêtes en cours, et 8 / 3 = 2,67, ce qui donne une concurrence de 3 après arrondi. Ajoutez ceci dans .env.override :
QANSWER_SCALING_PYTHON_REPLICAS_NUMBER=3
QANSWER_SCALING_DOCLING_WORKER_REPLICAS_NUMBER=4
QANSWER_CELERY_DOCLING_POOL=prefork
QANSWER_CELERY_DOCLING_CONCURRENCY=3Cela maintient 3 x 3 = 9 requêtes en cours pour 4 convertisseurs : proche de la cible de 8.
Pourquoi ne pas aller plus haut
Les requêtes au-delà de ce que les convertisseurs peuvent traiter ne vont pas plus vite : elles attendent dans la file de Docling. Cette file n'a pas de priorités : chaque requête supplémentaire qu'un gros envoi a en cours passe devant les documents des autres utilisateurs. QAnswer répartit les lots de chaque utilisateur sur les niveaux de priorité de son broker Celery, ce qui n'aide que tant que les lots attendent encore côté QAnswer.
Quand une conversion échoue
Lorsqu'une requête Docling échoue ou expire, le backend journalise un avertissement (« Docling serve conversion failed, falling back to vanilla parser ») et convertit ce lot avec son analyseur simple (OCR Tesseract si l'OCR était demandé). Les limites sont :
- Le serveur d'API Docling attend au plus 200 secondes (DOCLING_SERVE_MAX_SYNC_WAIT) une conversion synchrone, et le backend demande à Docling d'abandonner un lot au bout de 180 secondes.
- Une tâche Docling est arrêtée après 300 secondes (DOCLING_SERVE_ENG_RQ_JOB_TIMEOUT), quel que soit l'état de la requête qui l'a créée.
En pratique, la cause est le plus souvent un worker Docling ou le serveur d'API tué faute de mémoire, ce qui fait échouer toutes les requêtes qu'il a en cours : lors d'un de nos essais, un seul redémarrage du serveur d'API a envoyé 121 lots sur 255 vers l'analyseur simple. Dans les mesures ci-dessus, aucun lot n'est passé par l'analyseur simple, même avec 4 par backend.
Mémoire
- Worker Docling : QANSWER_DOCLING_WORKER_MEMORY_LIMIT, 8G par défaut.
- Les workers Docling grossissent avec le temps (de 3 à 8 Gio en une journée lors de nos essais, ticket docling-serve 366). Lorsqu'un worker reste au-dessus de QANSWER_DOCLING_WORKER_RECYCLE_MB (6144 par défaut) pendant cinq contrôles consécutifs, son contrôle de santé échoue et autoheal le redémarre proprement, une fois la tâche en cours terminée. Gardez ce seuil sous la limite de mémoire d'une marge supérieure au pic d'une tâche.
- Serveur d'API Docling : QANSWER_DOCLING_APISERVER_MEMORY_LIMIT, 4G par défaut. Il lui faut plus de mémoire à mesure que les requêtes en cours augmentent : avec 1 Gio, il a été tué avec quatre requêtes en cours.
- Backend Python : QANSWER_PYTHON_BACKEND_MEMORY_LIMIT, 20G par défaut.
Avec prefork, chaque emplacement est un processus distinct dans le conteneur du backend : une concurrence plus élevée s'impute donc sur la limite du backend. Dans les mesures ci-dessus, le backend est resté sous 9,5 Gio de ses 20 Gio avec jusqu'à 4 emplacements.
Appliquer la modification
Définissez les variables dans le fichier .env.override du répertoire du bundle (créez-le s'il n'existe pas). Il est chargé après .env et .env.specs, donc il l'emporte, et il n'est pas versionné. Les nombres de réplicas se trouvent dans .env.specs et peuvent être surchargés de la même façon.
# .env.override
QANSWER_CELERY_DOCLING_POOL=prefork
QANSWER_CELERY_DOCLING_CONCURRENCY=2Relancez ensuite le script de déploiement, qui recrée les services dont la configuration a changé :
./deploy-scripts/up.shVérifier le résultat
Si vous exploitez le bundle de supervision, deux tableaux de bord Grafana montrent si la configuration fonctionne :
- Celery Monitoring - Flower Metrics : le panneau Currently Executing Tasks (by worker type) indique combien de tâches d'extraction Docling s'exécutent en même temps ; il devrait approcher le nombre de réplicas du backend multiplié par la concurrence pendant un gros import.
- Docling Serve - OTel Observability : Active Requests (Concurrency) et Request Duration Percentiles montrent la charge du serveur d'API, Error Rate Over Time (4xx / 5xx) les requêtes en échec, et les panneaux de santé des conteneurs la mémoire et les redémarrages des workers.
Pendant l'indexation d'un gros fichier, regardez le taux d'occupation des workers Docling. Nettement sous 80 %, il reste de la marge pour plus de concurrence ; vers 80 % ou plus, une concurrence plus élevée n'ajoute que de l'attente.
Dans les journaux du backend, recherchez « falling back to vanilla parser ». Quelques lignes isolées sont normales ; un flux continu signifie le plus souvent que des conteneurs Docling redémarrent.