From 77a36fa82151aa6e929970b8be05b60315fc7712 Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Fri, 5 Jun 2026 00:06:40 +0200 Subject: [PATCH] fix: lower DOCUMENT_PDF_GRAPHICS_LIMIT default 5000 -> 1000 The OOM hotfix (#852) set graphics_limit=5000, which caught the 955k-drawing bomb but let a second pathology through: form/table PDFs (e.g. student records) have ~1.5k grid-line vector drawings per page -- under 5000, so uncapped. With those pages uncapped, pymupdf4llm's O(n^2) find_tables grinds ~17s/page, so a 7-page form hits the 120s timeout. All 6 current backfill parse failures in tenant-blackbox-demo are this exact timeout (zero OOM, zero error). Measured on a 7-page sample: graphics_limit=2000 -> 119s (timeout), 1000 -> 2.9s -- with identical extracted text and ZERO recovered tables either way (the expensive analysis produces nothing useful on these dense forms). Lowering the default to 1000 makes them index in ~3s; the bomb file (955k >> 1000) stays capped, and pages with genuine simple tables (<1000 drawings) still get table detection. Co-Authored-By: Claude Opus 4.8 (1M context) --- nextcloud_mcp_server/config.py | 13 +++++++++---- tests/unit/test_pdf_parse_isolation.py | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/nextcloud_mcp_server/config.py b/nextcloud_mcp_server/config.py index 08a79081..4dbd204f 100644 --- a/nextcloud_mcp_server/config.py +++ b/nextcloud_mcp_server/config.py @@ -133,7 +133,7 @@ _DEFAULTS: dict[str, Any] = { "document_chunk_size": 2048, "document_chunk_overlap": 200, # PDF parse isolation (OOM guard) - "document_pdf_graphics_limit": 5000, + "document_pdf_graphics_limit": 1000, "document_parse_timeout_seconds": 120.0, "document_parse_mem_limit_mb": 1536, # Observability @@ -712,9 +712,14 @@ class Settings: # PDF parse isolation (OOM guard). The parse runs in a subprocess so one # pathological file fails that doc, not the pod. - # to_markdown graphics cap; pages above it skip graphics analysis. Must be - # >=1 -- pymupdf4llm treats 0 as "no cap", which re-exposes the OOM. - document_pdf_graphics_limit: int = 5000 + # to_markdown graphics cap: pages with more vector drawings than this skip + # the O(n^2) find_tables analysis. Must be >=1 -- pymupdf4llm treats 0 as + # "no cap", which re-exposes the OOM. Default 1000: form/table PDFs have + # ~1.5k grid-line drawings per page, which at the old 5000 cap slipped + # through uncapped and timed out after ~17s/page (for zero recovered + # tables); at 1000 they parse in ~3s with identical text. Pages with genuine + # simple tables (<1000 drawings) still get table detection. + document_pdf_graphics_limit: int = 1000 # wall-clock cap per parse; the worker subprocess is killed on timeout. # float so a fractional DOCUMENT_PARSE_TIMEOUT_SECONDS is honoured, matching # anyio.move_on_after's float seconds. diff --git a/tests/unit/test_pdf_parse_isolation.py b/tests/unit/test_pdf_parse_isolation.py index 7daf90ce..ebafe8f9 100644 --- a/tests/unit/test_pdf_parse_isolation.py +++ b/tests/unit/test_pdf_parse_isolation.py @@ -182,7 +182,7 @@ async def test_processor_success_builds_page_boundaries(monkeypatch): assert "Hello world" in result.text assert result.metadata["page_boundaries"][0]["page"] == 1 # settings forwarded to the isolated parse - assert seen["graphics_limit"] == 5000 + assert seen["graphics_limit"] == 1000 assert seen["timeout_seconds"] == 120 assert seen["mem_limit_mb"] == 1536