Skip to content

fix(client): detect dead connections with gRPC keepalive - #23

Merged
utay merged 3 commits into
mainfrom
fix/client-grpc-keepalive
Oct 16, 2026
Merged

utay merged 3 commits into
mainfrom
fix/client-grpc-keepalive

Conversation

@utay

@utay utay commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Enable client-side gRPC keepalive: ping after 30s of silence, treat the connection as dead if the ping goes unanswered for 20s (matching the server's settings).
  • Add a regression test that cuts a connection mid-statement and expects FlightUnavailableError instead of a hang.

If a connection is cut while the client waits for a statement result, only the server notices. The client has nothing to send, so its TCP stack never retransmits and the call hangs forever. Callers such as dbt-altertable get the fix without any change on their side.

Channel options added to FlightClient:

  • grpc.keepalive_time_ms=30000 and grpc.keepalive_timeout_ms=20000.
  • grpc.http2.ping_timeout_ms=20000: the gRPC bundled with recent pyarrow releases times keepalive pings out with this option (default 60s), not keepalive_timeout_ms.
  • grpc.http2.max_pings_without_data=0: by default gRPC stops pinging after two pings while the client sends no data, which is exactly the long-statement case.
  • grpc.keepalive_permit_without_calls=1: also detect dead idle connections between statements.

The server's tonic/h2 stack does not reject frequent client pings, so a 30s interval is safe.

Testing

tests/test_keepalive.py runs a minimal grpcio Flight server behind a TCP proxy. It uses grpcio rather than pyarrow's server because older pyarrow servers (21, used on Python 3.9) reject frequent pings with too_many_pings, unlike the production tonic server. It keeps the connection healthy for 5s while a statement blocks, then silently stops forwarding bytes and asserts the call fails within 15s. The test fails if either ping_timeout_ms or max_pings_without_data is removed.

Made with Cursor

Co-authored-by: Cursor <cursoragent@cursor.com>
@utay utay self-assigned this Oct 16, 2026
utay and others added 2 commits October 16, 2026 10:29
…test

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@utay
utay merged commit 3247b66 into main Oct 16, 2026
6 checks passed
@utay
utay deleted the fix/client-grpc-keepalive branch October 16, 2026 08:37
utay pushed a commit that referenced this pull request Oct 16, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.4.1](v0.4.0...v0.4.1)
(2026-10-16)


### Bug Fixes

* **client:** detect dead connections with gRPC keepalive
([#23](#23))
([3247b66](3247b66))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
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