Performance Tuning
These settings control worker behavior, model inference, batch sizes, timeouts, OCR, and rate limits. Start with the symptom you are seeing, then tune the smallest matching knob.
Quick Tuning Map
| Symptom | First Adjustment | Why |
|---|---|---|
| High embedding memory | Lower EMBEDDING_BATCH_SIZE or MAX_NODES_PER_TASK | Reduces texts held per FastEmbed call and per Celery task |
| Slow document ingestion | Add embedding workers before increasing embedding concurrency | Each embedding worker keeps model memory isolated |
| Slow classification | Lower CLASSIFICATION_SELECT_TOP_K or enable MODERNBERT_PREFILTER_ENABLED | Scores fewer chunks/categories with ONNX |
| ONNX CPU oversubscription | Keep MODERNBERT_ONNX_INTRA_OP_THREADS=1 and MODERNBERT_ONNX_INTER_OP_THREADS=1 in containers | Prevents worker processes from competing for the same cores |
| OCR-heavy uploads are slow | Increase preprocessing capacity or raise OCR_PAGE_TEXT_THRESHOLD carefully | OCR runs in preprocessing and is CPU-heavy |
| Slow reranking | Increase RERANK_TIMEOUT_SEC, reduce top_k_rerank, or disable reranking | Reranking is synchronous on the search path |
| Too many protected-route 429s | Increase RATE_LIMIT, reduce request volume, or add an external limiter/capacity plan | Rate limiting is per resolved client id for protected routes; /search also has a search-specific counter |
| Too many auth-failure 429s | Increase AUTH_FAILURE_RATE_LIMIT only after ruling out credential probing | Failed auth attempts are rate limited per source address before client resolution |
Embedding and Search Knobs
| Setting | Default | Why It Matters |
|---|---|---|
MAX_NODES_PER_TASK | 50 | Number of chunks grouped into one embedding Celery task |
EMBEDDING_BATCH_SIZE | 32 | Number of texts processed at once by FastEmbed |
RETRIEVAL_TOP_K | 10 | Default number of search results |
RERANK_TOP_N | 5 | Default number of reranked results returned |
RERANK_TIMEOUT_SEC | 2.0 | Maximum reranking wait before falling back to hybrid scores |
Classification and NER Knobs
| Setting | Default | Why It Matters |
|---|---|---|
CLASSIFICATION_ENABLED | true | Disables ModernBERT classification when set to false |
SPACY_NER_ENABLED | true | Disables entity extraction when set to false |
CLASSIFICATION_SELECT_TOP_K | 3 | Number of representative chunks selected via centroid + MMR |
MODERNBERT_PREFILTER_ENABLED | false | Enables lexical prefiltering before ONNX scoring |
MODERNBERT_PREFILTER_TOP_K | 3 | Number of candidate categories kept by the prefilter |
MODERNBERT_ONNX_NLI_MAX_TOKENS | 2048 | Maximum NLI sequence length before truncation |
MODERNBERT_ONNX_INTRA_OP_THREADS | 1 | ONNX Runtime threads within a single operator |
MODERNBERT_ONNX_INTER_OP_THREADS | 1 | ONNX Runtime threads across operators |
For Docker Compose deployments, keep ONNX thread counts at 1 unless you have measured spare CPU headroom. Increasing them can make a single classification faster while reducing total worker throughput.
OCR and Upload Knobs
| Setting | Default | Why It Matters |
|---|---|---|
MAX_FILE_SIZE_MB | 200 | Upload size limit enforced before processing starts |
RAPIDOCR_OCR_ENABLED | true | Enables OCR for images and low-text PDF pages |
OCR_PAGE_TEXT_THRESHOLD | 80 | PDF pages below this native text count use OCR fallback |
ALLOWED_EXTENSIONS | See .env.example | Controls accepted file extensions |
ALLOWED_MIME_TYPES | See .env.example | Validates uploaded file MIME types |
Office File Settings
| Setting | Default | Why It Matters |
|---|---|---|
OFFICE_LEGACY_CONVERSION_ENABLED | false | Enables LibreOffice conversion for .ppt/.xls (legacy binary formats) |
OFFICE_LEGACY_CONVERSION_TIMEOUT_SEC | 120 | Timeout per legacy Office conversion; large files may need more time |
LIBREOFFICE_BINARY | soffice | LibreOffice executable path |
OFFICE_MAX_UNCOMPRESSED_BYTES | 1000000000 | Max decompressed size for OOXML files (zip-bomb guard) |
OFFICE_MAX_COMPRESSION_RATIO | 200 | Max uncompressed:compressed ratio for OOXML files |
Celery Worker Knobs
| Setting | Default | Why It Matters |
|---|---|---|
CELERY_WORKER_MAX_MEMORY_PER_CHILD | 1572864 KB | Base memory recycle threshold for worker children |
CELERY_WORKER_MAX_TASKS_PER_CHILD | 100 | Global task recycle threshold |
CELERY_WORKER_MAX_TASKS_PER_CHILD_EMBEDDING | 250 | Embedding worker recycle threshold |
CELERY_WORKER_MAX_TASKS_PER_CHILD_PREPROCESSING | 500 | Preprocessing worker recycle threshold |
CELERY_TASK_MAX_RETRIES | 3 | Maximum retry attempts for recoverable task failures |
CELERY_RETRY_BACKOFF_BASE | 60 seconds | Base delay for exponential retry backoff |
CELERY_RESULT_EXPIRES | 86400 seconds | Time Celery results remain in Redis DB /2 |
CELERY_EMBED_SOFT_TIME_LIMIT_SEC | 900 | Soft timeout for embedding tasks |
CELERY_EMBED_TIME_LIMIT_SEC | 1200 | Hard timeout for embedding tasks |
Rate-Limit Knob
| Setting | Default | Why It Matters |
|---|---|---|
RATE_LIMIT | 30 | Requests per minute per resolved client id for protected endpoints |
AUTH_FAILURE_RATE_LIMIT | 10 | Failed authentication attempts per minute per source address |
Set RATE_LIMIT=0 to disable the built-in limiter. Production traffic across multiple API workers shares Redis-backed rate-limit counters through REDIS_URL; use a reverse proxy or edge limiter only for additional network-level protection.
Status Endpoint Details
GET /status/{job_id} can return:
pendingprocessingcompletedfailedpartial_failedretrying
partial_failed means some embedding tasks failed while others succeeded.
When to Leave Defaults Alone
Keep the defaults when you are still validating correctness or running a small deployment. Most tuning should happen only after just status, logs, or benchmark results show a specific bottleneck.