RecoverAbortedTransaction rolls back and retries once when a DuckLake-backed connection is stuck in "Current transaction is aborted" state. This is only safe when the caller owns the transaction lifecycle (autocommit / no active user transaction). Callers should pass canRollback=false for explicit u
( err error, canRollback bool, rollback func() error, retry func() (T, error), )
| 77 | // user transaction). Callers should pass canRollback=false for explicit user |
| 78 | // transactions so the original error is surfaced unchanged. |
| 79 | func RecoverAbortedTransaction[T any]( |
| 80 | err error, |
| 81 | canRollback bool, |
| 82 | rollback func() error, |
| 83 | retry func() (T, error), |
| 84 | ) (T, error, bool) { |
| 85 | var zero T |
| 86 | if err == nil || !canRollback || !isTransactionAborted(err) { |
| 87 | return zero, err, false |
| 88 | } |
| 89 | |
| 90 | slog.Warn("DuckLake connection hit aborted transaction state; issuing ROLLBACK before retry.", "error", err) |
| 91 | if rollbackErr := rollback(); rollbackErr != nil { |
| 92 | return zero, fmt.Errorf("DuckLake aborted transaction recovery rollback failed: %w (original error: %v)", rollbackErr, err), true |
| 93 | } |
| 94 | |
| 95 | result, retryErr := retry() |
| 96 | if retryErr == nil { |
| 97 | slog.Info("DuckLake aborted transaction recovery succeeded.") |
| 98 | } else { |
| 99 | slog.Warn("DuckLake aborted transaction recovery retry failed.", "error", retryErr) |
| 100 | } |
| 101 | return result, retryErr, true |
| 102 | } |
| 103 | |
| 104 | func recoverAbortedTransaction[T any]( |
| 105 | err error, |
no test coverage detected