Skip to content

fix: long partitions behind a NAT idle timeout never return - #359

Open
CyMule wants to merge 4 commits into
mainfrom
fix/tcp-keepalive
Open

CyMule wants to merge 4 commits into
mainfrom
fix/tcp-keepalive

Conversation

@CyMule

@CyMule CyMule commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

What

Partition requests that take longer than a few minutes now return their response when the client is behind a NAT or load balancer that drops idle connections. The SDK's own HTTP clients now send TCP keepalive probes while they wait. That covers the default sync and async clients and the split-PDF client.

hi_res partition, response headers after ~5 min, client behind a NAT gateway with a 4 min idle timeout

Before: request sent → no packets for 4 min → NAT drops the flow → response never arrives → ReadTimeout at the full read timeout (900 s)
After:  request sent → keepalive probe every 30 s after 60 s idle → flow stays open → 200

It is on by default (60 s idle, 30 s interval, 5 probes). It can be tuned or turned off with UNSTRUCTURED_CLIENT_TCP_KEEPALIVE, UNSTRUCTURED_CLIENT_TCP_KEEPALIVE_IDLE, UNSTRUCTURED_CLIENT_TCP_KEEPALIVE_INTERVAL and UNSTRUCTURED_CLIENT_TCP_KEEPALIVE_COUNT. The split-PDF plan_created and batch_async_start logs show the setting in use.

Why

Long-running partition requests can exceed a NAT gateway’s idle timeout, causing responses to be lost while the client waits for its read timeout. This change enables TCP keepalive to keep those connections active.

Neither httpx nor the SDK turns on keepalive, and the OS keepalive settings only apply to sockets that ask for it. That makes the SDK, which opens these sockets, the place to fix it.

Unchanged:

  • Clients passed in by the caller (client= / async_client=) are not modified.
  • Proxy routing from HTTP(S)_PROXY / NO_PROXY works as before. Connections made through a proxy do not get keepalive, because httpcore drops socket options for proxied connections.
  • If a future httpx release moves the helper used to rebuild proxy routing, the SDK falls back to building its clients exactly as it did before this change.

Validation

  • Unit tests (366 passed), pylint and mypy pass.
  • Tests check that real sockets carry SO_KEEPALIVE and every idle, interval and count setting. They also cover the bounds on the environment values, proxy routing and parity of TLS, limits and timeouts.
  • Docker reproduction with a NAT router that silently drops idle flows after 20 s, and a server that answers after 45 s:
    • with keepalive: 200 at 45 s;
    • without keepalive: ReadTimeout at 90 s.
  • CHANGELOG entry added under 0.46.3, which is not released yet.

Refresh idle NAT and load-balancer flows while partition requests wait for
response headers. Enable SO_KEEPALIVE with 60s idle, 30s interval and five
probes on SDK-owned sync, async and split-PDF transports. Resolve supported
platform options defensively and expose validated environment overrides.
Caller-provided clients retain their existing transport configuration.

HTTPX 0.28 disables environment proxy discovery when transport is supplied.
Rebuild its environment proxy mounts with get_environment_proxies, including
NO_PROXY exclusions, and apply socket options to proxy transports as well.
Move split-PDF limits, verify and cert into every transport so these settings
remain effective. Log resolved probe settings alongside pool and TLS settings.

Keep the changelog entry under 0.46.3: no corresponding remote tag or GitHub
release exists. Generation was decommissioned in cb867f0; .genignore is absent
from the current checkout, and the SDK entry point is maintained by hand.

Validation: make lint (pylint 10.00/10, mypy clean); make test-unit (353 passed,
1 xfailed). A Docker MASQUERADE router with a 20s conntrack timeout and a
45s server delay returned 200 at 45.0s with keepalive idle/interval 5s;
disabling keepalive produced ReadTimeout at 90.0s.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 7 files

Shadow auto-approve: would not auto-approve because issues were found.

View guided diff | Re-trigger cubic

Comment thread _test_unstructured_client/unit/test_tcp_keepalive.py
Comment thread _test_unstructured_client/unit/test_tcp_keepalive.py Outdated

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

0 issues found across 1 file (changes from recent commits).

Shadow auto-approve: would not auto-approve. This PR does not meet the repository auto-approval settings.

View guided diff | 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