Skip to content

fix(api): run native document conversion off the Node.js event loop - #4433

Open
abimaelmartell wants to merge 1 commit into
mainfrom
claude/xenodochial-colden-232b82
Open

fix(api): run native document conversion off the Node.js event loop#4433
abimaelmartell wants to merge 1 commit into
mainfrom
claude/xenodochial-colden-232b82

Conversation

@abimaelmartell

@abimaelmartell abimaelmartell commented Aug 27, 2026

Copy link
Copy Markdown
Member

Problem

convert_document_to_markdown in apps/api/native/src/document.rs was a synchronous #[napi] fn, so the await at its call site in the document engine was a no-op. Every DOCX/ODT/RTF/XLSX/PPTX/EPUB conversion ran directly on the Node.js event loop of whichever pod executed the scrape — including API app pods that run sync scrapes in-process — freezing every unrelated in-flight request for the duration of the conversion.

This is the same bug class fixed for PDFs in e33e1f6 (processPdf/detectPdf).

Fix

  • Convert convert_document_to_markdown to pub async fn that offloads the CPU-bound conversion to tokio's blocking thread pool via tokio::task::spawn_blocking, mirroring the pattern in apps/api/native/src/pdf.rs. The binding now takes an owned Uint8Array and returns Promise<string>.
  • The engine call site already awaited, so it needed no change; the direct calls in the document-converter snips test are now awaited.

Tests

  • New regression test apps/api/src/scraper/scrapeURL/engines/document/__tests__/nativeBinding.test.ts, modeled on the PDF one: generates a ~20MB RTF (no fixtures) whose conversion takes ~1s of CPU and asserts max event-loop lag stays under 250ms. With the old synchronous binding, lag ≈ the full conversion time; with this fix it measures ~2ms.
  • Existing document-converter snips tests pass against the async binding (happy paths for DOCX/ODT/RTF/XLSX/CSV and the failure path for unrecognized bytes).

🤖 Generated with Claude Code


Summary by cubic

Makes native document conversion (DOCX/ODT/RTF/XLSX/PPTX/EPUB) run off the Node.js event loop so large conversions no longer freeze unrelated in-flight requests.

Bug Fixes

  • The old binding was synchronous, so the await at the engine call site was a no-op and the full conversion blocked the event loop.
  • Conversion now offloads to tokio's blocking thread pool via spawn_blocking, matching the PDF processPdf/detectPdf fix.
  • The binding takes an owned Uint8Array and returns Promise<string>; the engine call site already awaited and needed no change.
  • Adds a regression test that generates a ~20MB RTF and asserts event-loop lag stays under 250ms.

Written for commit ce2be95. Summary will update on new commits.

Review in cubic

convert_document_to_markdown was a synchronous #[napi] fn, so the await
at its call site was a no-op: every DOCX/ODT/RTF/XLSX/PPTX/EPUB
conversion ran on the Node.js event loop of whichever pod executed the
scrape, including API app pods running sync scrapes in-process. Same
bug class as the PDF one fixed in e33e1f6.

Convert it to an async fn that offloads to tokio's blocking pool,
mirroring process_pdf/detect_pdf, and add a regression test that
asserts the event loop stays responsive during a large conversion.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

Re-trigger cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant