Skip to content

fix(adbc): re-authenticate on Unauthenticated instead of failing permanently#73

Merged
lukekim merged 4 commits into
trunkfrom
phillip/adbc-reauth-on-session-expiry
Jul 13, 2026
Merged

fix(adbc): re-authenticate on Unauthenticated instead of failing permanently#73
lukekim merged 4 commits into
trunkfrom
phillip/adbc-reauth-on-session-expiry

Conversation

@phillipleblanc

Copy link
Copy Markdown
Contributor

Problem

SqlWithParams/QueryWithParams run over the ADBC FlightSQL connection, which authenticates only once, when it is opened: the Basic-auth handshake (OptionKeyUsername/OptionKeyPassword) yields a server-side session token that is then reused for every prepared statement on that long-lived connection.

If the server invalidates that session — most commonly when it expires after a period of inactivity — every subsequent prepared statement fails:

error preparing statement: Unauthenticated: [FlightSQL] Invalid credentials (Unauthenticated; Prepare)

SqlWithParams classified Unauthenticated as a backoff.Permanent error, so the connection never recovered on its own. The only workaround was to recreate the SpiceClient (i.e. restart the consuming process). The credentials are still valid — it is the cached session that is stale.

The plain (non-parameterized) Sql/Query path is unaffected because it re-runs AuthenticateBasicToken on every call, so it was already immune to session expiry.

Fix

  • Detect ADBC auth failures via errors.As on adbc.Error (StatusUnauthenticated / StatusUnauthorized), with a message-substring fallback for cases where the typed error is not propagated.
  • On an auth failure, re-open the ADBC connection (fresh handshake → new session) and retry the query once. A genuinely-bad credential therefore fails after one re-auth attempt rather than looping.
  • Re-initialization is guarded by a new adbcMu mutex plus a staleness check (the connection a caller observed failing), so when many goroutines hit the expired session simultaneously the connection is re-opened at most once and the rest reuse it.
  • Refactored the existing backoff/transient-retry logic into execADBCWithBackoff (no behavior change for non-auth errors).

Tests

  • New unit test TestIsADBCAuthError (no server required) covering typed, wrapped, fallback, and non-auth cases.
  • go build, go vet, gofmt -l, and go test -race on the new logic all pass.
  • The only failing tests locally are the pre-existing local-runtime integration tests (taxi_trips dataset / :8090 runtime) that require the CI spiced harness — they fail identically on clean trunk, unrelated to this change.

Release / downstream

Needs a tagged release (e.g. v8.0.2) so consumers can pick it up; the immediate motivation is a downstream service whose long-lived control-plane ADBC connection went idle past the server's session TTL and then failed every parameterized query until restart.

…anently

The ADBC FlightSQL connection authenticates only once, when it is opened: the
Basic-auth handshake yields a server-side session token that is then reused for
every prepared statement on that connection. If the server invalidates that
session (e.g. it expires after a period of inactivity), every subsequent
prepared statement fails with Unauthenticated. Because SqlWithParams classified
Unauthenticated as a permanent error, the connection never recovered on its own
— the only remedy was to recreate the SpiceClient (restart the process).

Detect authentication failures (adbc.StatusUnauthenticated / Unauthorized, with
a message fallback) and, when one occurs, re-open the ADBC connection to perform
a fresh handshake and retry the query once. Re-initialization is guarded by a
mutex and a staleness check so concurrent callers re-open the connection at most
once.

The plain (non-parameterized) Sql/Query path is unaffected: it re-authenticates
on every call, so it was already immune.
@lukekim lukekim requested a review from Copilot July 13, 2026 03:53
@lukekim lukekim assigned lukekim and phillipleblanc and unassigned lukekim Jul 13, 2026
@lukekim lukekim added the bug Something isn't working label Jul 13, 2026

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

Pull request overview

This PR improves resiliency of the parameterized ADBC FlightSQL query path (SqlWithParams/QueryWithParams) by detecting authentication/session-expiry failures, re-opening the long-lived ADBC connection, and retrying the query once so callers don’t need to recreate the SpiceClient.

Changes:

  • Add auth-failure detection (isADBCAuthError) and retry-once reauthentication behavior for SqlWithParams.
  • Refactor the transient retry/backoff logic into execADBCWithBackoff (intended to preserve existing behavior for non-auth errors).
  • Add a unit test for auth-error classification (TestIsADBCAuthError) and introduce an adbcMu mutex to coordinate reinitialization.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
client.go Adds an adbcMu mutex field intended to coordinate ADBC connection reinitialization.
adbc.go Implements auth-error detection, reinit-on-auth-failure retry behavior, and refactors ADBC backoff logic.
adbc_reauth_test.go Adds unit tests for isADBCAuthError behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread adbc.go Outdated
Comment thread adbc.go
Comment thread adbc.go Outdated
Comment thread adbc_reauth_test.go
lukekim added 2 commits July 13, 2026 15:02
- Fix the data race on c.adbcClient: capture the connection once under adbcMu
  via ensureADBC() and thread it through execADBCWithBackoff /
  queryADBCWithParams / bindParameters, so queries never re-read c.adbcClient
  while reinitADBC may be replacing it. reinitADBC returns the fresh connection.
- isADBCAuthError now matches both adbc.Error and *adbc.Error before the message
  fallback (new isADBCAuthStatus helper).
- reinitADBC logs a warning when closeADBC() fails instead of discarding it.
- Add *adbc.Error (pointer) cases to TestIsADBCAuthError.
@lukekim lukekim merged commit dddb26a into trunk Jul 13, 2026
14 of 15 checks passed
@lukekim lukekim deleted the phillip/adbc-reauth-on-session-expiry branch July 13, 2026 06:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants