handleConnectionIsolated handles a connection with process isolation. The parent handles SSL request and cancel requests, then spawns a child process for TLS handshake, authentication, and query execution.
(conn net.Conn, remoteAddr net.Addr)
| 2470 | |
| 2471 | // S3ProviderForConfig returns the effective S3 provider for the given DuckLake config. |
| 2472 | func S3ProviderForConfig(dlCfg DuckLakeConfig) string { |
| 2473 | provider := dlCfg.S3Provider |
| 2474 | if provider == "" { |
| 2475 | if dlCfg.S3AccessKey != "" { |
| 2476 | provider = "config" |
| 2477 | } else { |
| 2478 | provider = "credential_chain" |
| 2479 | } |
| 2480 | } |
| 2481 | return provider |
| 2482 | } |
| 2483 | |
| 2484 | // needsCredentialRefresh returns true if the DuckLake config uses temporary credentials |
| 2485 | // that need periodic refresh (credential_chain or aws_sdk provider with an S3 object store). |
| 2486 | func needsCredentialRefresh(dlCfg DuckLakeConfig) bool { |
| 2487 | if dlCfg.ObjectStore == "" { |
| 2488 | return false |
| 2489 | } |
| 2490 | p := S3ProviderForConfig(dlCfg) |
| 2491 | return p == "credential_chain" || p == "aws_sdk" || dlCfg.S3SessionToken != "" |
| 2492 | } |
| 2493 | |
| 2494 | // isTransactionAborted returns true if the error indicates DuckDB's connection |
| 2495 | // is stuck in an aborted transaction state (requires ROLLBACK to recover). |
| 2496 | func isTransactionAborted(err error) bool { |
| 2497 | return err != nil && strings.Contains(err.Error(), "Current transaction is aborted") |
| 2498 | } |
| 2499 | |
| 2500 | // sqlExecer is satisfied by both *sql.DB and *sql.Conn, allowing |
| 2501 | // StartCredentialRefresh to work with either a connection pool or a pinned connection. |
| 2502 | type sqlExecer interface { |
| 2503 | ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) |
| 2504 | } |
| 2505 | |
| 2506 | // StartCredentialRefresh starts a background goroutine that periodically refreshes |
| 2507 | // S3 credentials for long-lived DuckDB connections using the credential_chain provider. |
| 2508 | // This prevents credential expiration when running on EC2 with IAM instance roles, |
| 2509 | // STS assume-role, or other temporary credential sources. |
| 2510 | // |
| 2511 | // The execer parameter accepts either *sql.DB (standalone mode) or *sql.Conn (worker |
| 2512 | // mode where the pool's only connection is pinned by the session). |
| 2513 | // |
| 2514 | // The optional isTxActive callback reports whether the caller currently has an active |
| 2515 | // user transaction on this connection. When provided and returning false, aborted |
| 2516 | // transaction errors are auto-recovered by issuing ROLLBACK and retrying once. |
| 2517 | // When omitted (or returning true), automatic rollback is skipped to avoid rolling |
| 2518 | // back caller-owned transactions. |
| 2519 | // |
| 2520 | // Note: ExecContext serializes behind any running query (pool contention for *sql.DB, |
| 2521 | // internal mutex for *sql.Conn). This means credentials are refreshed between queries, |
| 2522 | // not during them. A query that runs longer than the credential TTL (~6h for instance |
| 2523 | // roles) could still fail if DuckDB makes S3 requests with stale cached credentials. |
| 2524 | // |
| 2525 | // Returns a stop function that cancels the refresh goroutine. The caller must call |
| 2526 | // the stop function when the connection is closed to prevent goroutine leaks. |
| 2527 | // If credential refresh is not needed (static credentials, no S3, etc.), returns a no-op. |
| 2528 | func StartCredentialRefresh(execer sqlExecer, dlCfg DuckLakeConfig, isTxActive ...func() bool) func() { |
| 2529 | if !needsCredentialRefresh(dlCfg) { |