Technology

Overcoming Synchronous Parsing Issues with Docling

R
Rohan Shrestha
December 06, 2025
3 min read
Recently, I integrated Docling—a solid framework for document parsing and chunking—into a production system. With the introduction of their API server docling-serve, integrating Docling has become easier than ever. However, while working on this, I encountered a few challenges with synchronous document parsing that needed a more robust solution.

The Problem with Synchronous Parsing

When parsing documents synchronously, two main issues arose:
  1. Long Processing Times: Parsing can take several minutes (in some cases, over 5 minutes depending on the document size and complexity).
  1. Connection Timeouts: Maintaining an open HTTP connection for that duration is not ideal. Services like Cloudflare often cap connections at roughly 120 seconds, leading to dropped requests and failures.

The Solution: Asynchronous Workflow

To handle this, I implemented an asynchronous document processing workflow. There were two potential approaches:
  1. Webhooks: Expose a webhook endpoint to receive the result once processing completes.
  1. Polling: Implement a polling mechanism to check for completion and retrieve results once ready.
For simplicity and ease of integration without exposing new public endpoints, I chose the polling approach.

The Workflow

Here is the step-by-step workflow I implemented:
  1. Submit Document for Processing
  • Send the document URL via an async POST request to the Docling service to initiate background processing.
  1. Poll for Completion
  • The service returns a task_id.
  • Use a polling mechanism with exponential backoff to check the task’s status periodically.
  • Backoff configuration:
  • Initial interval: 2 seconds
  • Multiplier: ×3
  • Max interval: 60 seconds
  • Timeout: 10 minutes
  1. Retrieve Results
  • Once the status returns success, perform a final GET request to fetch the processed data.
  1. Handle Results and Errors
  • On success: Store and handle the processed document.
  • On failure or timeout: Capture and log the error for further action.

Implementation Example

Below is a simplified version of the TypeScript implementation used to handle the polling logic:
async initiateDocumentProcessing(user: UserModel, document: DocumentModel) {
try {
const axiosInstance = axios.create({
baseURL: this.configService.getStringOrThrow('app.docProcessorBaseUrl'),
headers: {
'X-API-KEY': this.configService.getStringOrThrow('app.docProcessorXApiKey'),
},
})

// 1. Submit the document for asynchronous processing
const response = await axiosInstance.post(
'/v1/chunk/hybrid/source/async',
{
sources: [{ kind: 'http', url: document.url }],
}
)

// 2. Poll for completion with exponential backoff
const resultResponse = await pollWithExponentialBackoff(
async () => {
const pollResponse = await axiosInstance.get(
`/v1/status/poll/${response.data.task_id}`
)

// 3. Once status is success, fetch the full result
if (pollResponse.data.task_status === 'success') {
const resultResponse = await axiosInstance.get(
`/v1/result/${response.data.task_id}`
)
resultResponse.data.task_status = 'success'
return resultResponse
}

return pollResponse
},
{
interval: DocumentService.POLL_INTERVAL_MS, // 2 seconds
timeout: DocumentService.POLL_TIMEOUT_MS, // 10 minutes
maxInterval: DocumentService.POLL_MAX_INTERVAL_MS, // 60 seconds
multiplier: DocumentService.POLL_BACKOFF_MULTIPLIER, // 3
shouldResolve: (response) =>
response.status === 200 && response.data.task_status === 'success',
shouldReject: (response) =>
response.status === 200 || response.data.task_status === 'failure',
}
)

// 4. Handle successful processing
await this.handleProcessedDocument({
id: document.id,
userId: user.id,
response: resultResponse.data,
})

} catch (error) {
// Handle failures (timeouts, API errors)
await this.handleProcessedDocument({
id: document.id,
userId: user.id,
error: error instanceof Error ? error.message : 'Internal server error',
})
throw error
}
}

Example Polling Intervals

To avoid hammering the server while ensuring timely updates, the exponential backoff looks something like this:
  • Attempt 1: Wait 2s
  • Attempt 2: Wait 6s
  • Attempt 3: Wait 18s
  • Attempt 4: Wait 54s
  • Attempt 5+: Wait 60s (capped)

Conclusion

By moving from a synchronous to an asynchronous polling model, we bypassed the timeout limitations of intermediate proxies like Cloudflare and built a more resilient document processing pipeline.

Comments (0)

You