Skip to content

GH-49818: [C++][FlightRPC] Don't call Finish() twice on handshake stream - #54685

Open
kou wants to merge 1 commit into
apache:mainfrom
kou:flight-handshake-finish-once
Open

kou wants to merge 1 commit into
apache:mainfrom
kou:flight-handshake-finish-once

Conversation

@kou

@kou kou commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Rationale for this change

arrow-flight-sql-odbc-test sometimes aborts with GRPC_CALL_ERROR_TOO_MANY_OPERATIONS in ClientReaderWriter::Finish() of the handshake stream.

This happens when a server finishes a handshake before the client writes or reads. For example, NoOpAuthHandler on the server side and Dremio do this. In this case:

  1. GrpcClientAuthSender::Write() (or GrpcClientAuthReader::Read()) fails and calls Finish() on the handshake stream.
  2. The returned status is OK, so the client auth handler and AuthenticateInternal() continue.
  3. AuthenticateInternal() calls WritesDone(), drains the stream and calls Finish() again.
  4. gRPC rejects the second RECV_STATUS_ON_CLIENT with GRPC_CALL_ERROR_TOO_MANY_OPERATIONS and aborts the process.

GRPC_TRACE=api shows this sequence:

SEND_MESSAGE                                   -> ERROR: Attempt to send message after stream was closed.
RECV_INITIAL_METADATA + RECV_STATUS_ON_CLIENT  -> OK  (Finish() by GrpcClientAuthSender::Write())
SEND_CLOSE_FROM_CLIENT                                (WritesDone())
RECV_MESSAGE                                          (drain)
RECV_STATUS_ON_CLIENT                          -> GRPC_CALL_ERROR_TOO_MANY_OPERATIONS (Finish() by AuthenticateInternal())

Whether the server finishes before the client writes is a race. So it's flaky.

What changes are included in this PR?

  • Add GrpcClientHandshakeStream that wraps the handshake stream:
    • Finish() is called at most once. The second and later calls return the status of the first call.
    • Read(), Write() and WritesDone() return false without calling gRPC after the stream is finished.
  • Return the status in AuthenticateInternal() without WritesDone() and draining when the stream is already finished.

Are these changes tested?

Yes.

Are there any user-facing changes?

Yes. Flight clients don't abort the process when a server finishes a handshake early.

Was AI used for this PR?

In accordance to the AI generation guidelines, please disclose below whether and how AI was used in this PR.

PR code and description written by:

  • Human
  • AI

Reviewed before submission by:

  • Human
  • AI
  • Not reviewed

@kou kou added the CI: Extra: C++ Run extra C++ CI label Oct 20, 2026
@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #49818 has been automatically assigned in GitHub to PR creator.

…ke stream

If a server finishes a handshake before the client writes or reads,
GrpcClientAuthSender::Write() or GrpcClientAuthReader::Read() calls
Finish() on the handshake stream. If the returned status is OK,
AuthenticateInternal() continues and calls Finish() again. gRPC
reports GRPC_CALL_ERROR_TOO_MANY_OPERATIONS for the second Finish()
and aborts the process.

This wraps the handshake stream to call Finish() at most once and
skips WritesDone()/draining when the stream is already finished.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kou
kou force-pushed the flight-handshake-finish-once branch from 4768fd6 to 20e9a6d Compare October 20, 2026 06:00
@kou
kou marked this pull request as ready for review October 20, 2026 06:01
@kou
kou requested a review from lidavidm as a code owner October 20, 2026 06:01
Copilot AI balanced review requested due to automatic review settings October 20, 2026 06:01

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@lidavidm lidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks!

@github-actions github-actions Bot added awaiting merge Awaiting merge and removed awaiting committer review Awaiting committer review labels Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants