Skip to content

SG-44724 Fail fast on stale connections instead of hanging - #457

Open
jerrycheng9 wants to merge 2 commits into
masterfrom
ticket/SG-44724_stale-connections-cause-multi-hundred-second-hangs-instead-of-fast-failure
Open

SG-44724 Fail fast on stale connections instead of hanging#457
jerrycheng9 wants to merge 2 commits into
masterfrom
ticket/SG-44724_stale-connections-cause-multi-hundred-second-hangs-instead-of-fast-failure

Conversation

@jerrycheng9

@jerrycheng9 jerrycheng9 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Problem

shotgun_api3 reuses one persistent HTTP(S) connection per Shotgun instance
across all RPC calls (Shotgun._connection, populated by _get_connection();
the underlying socket is cached in httplib2.Http.connections{}), and every
request is sent with Connection: keep-alive.

If a NAT, firewall or load balancer along the path silently drops that idle TCP
session — no FIN, no RST — the client cannot tell. Only TCP_NODELAY was set on
the socket; there was no OS-level keepalive and no idle-age tracking on cached
connections. The next call writes into the send buffer successfully and then
blocks in getresponse() for the full timeout_secs before it can recover,
turning what should be a near-instant reconnect into a multi-hundred-second
hang.

Recovery does not help here, so the fix has to be preventive: _make_call
retries only ssl.SSLEOFError / ssl.SSLError, while its generic
except Exception closes the connection and re-raises with no retry, and
httplib2 re-raises socket.timeout unconditionally.

Changes

Idle expiry (the actual fix):

  • Add _Config.max_connection_idle_secs, default 60, which bounds how long a
    cached connection may sit idle before it is closed and recreated instead of
    reused. Tunable per instance; None or 0 disables expiry.
  • Stamp Shotgun._connection_last_used in _http_request only after the
    request completes. A request that raised is no evidence the socket is alive,
    so it must not refresh the idle clock.
  • _get_connection() closes and rebuilds a stale connection through the
    existing _close_connection(). Both connection consumers (connect() and
    _http_request) route through _get_connection(), so there is no bypass.

TCP keepalive (defense in depth):

  • KeepaliveHTTPConnection / KeepaliveHTTPSConnection call
    super().connect() and then enable SO_KEEPALIVE, tuning idle/interval/count
    to 30/10/3 so the kernel can notice a vanished peer within roughly a minute of
    idling.
  • Tuning is best effort: SIO_KEEPALIVE_VALS via ioctl on Windows,
    TCP_KEEPIDLE/TCP_KEEPALIVE elsewhere, skipping constants that do not exist
    on the platform and swallowing OSError. Bare SO_KEEPALIVE would not fire a
    probe for 7200s on default Linux settings, so the per-platform tuning is what
    makes this worth anything — but it is still secondary to the idle expiry.

@jerrycheng9 jerrycheng9 changed the title SG-44724 Quick Fail on Staled Connection SG-44724 Fail fast on stale connections instead of hanging Aug 13, 2026
@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.65%. Comparing base (6f56cf2) to head (e602516).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #457      +/-   ##
==========================================
+ Coverage   78.83%   79.65%   +0.82%     
==========================================
  Files           7        7              
  Lines        1847     1897      +50     
==========================================
+ Hits         1456     1511      +55     
+ Misses        391      386       -5     
Flag Coverage Δ
Linux 79.65% <100.00%> (+0.82%) ⬆️
Python-3.10 79.59% <100.00%> (+0.82%) ⬆️
Python-3.11 79.59% <100.00%> (+0.82%) ⬆️
Python-3.13 79.59% <100.00%> (+0.82%) ⬆️
Python-3.9 79.58% <100.00%> (+0.82%) ⬆️
Windows 79.71% <100.00%> (+0.82%) ⬆️
macOS 79.65% <100.00%> (+0.82%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dsauve-adsk dsauve-adsk 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.

One minor comment

Comment thread shotgun_api3/shotgun.py
LOG.debug("Unable to set %s on socket." % option_name, exc_info=True)


class KeepaliveHTTPConnection(HTTPConnectionWithTimeout):

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.

nit : Both KeepaliveHTTPConnection and KeepaliveHTTPSConnection are identical, maybe use a mixin to prevent duplication

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.

2 participants