Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- `DatabaseMetaData.getColumns(...)` with a `null` catalog now issues a single `SHOW COLUMNS IN ALL CATALOGS` statement (consistent with `getSchemas`/`getTables`) instead of enumerating every catalog and issuing a per-catalog `SHOW COLUMNS`. Older DBR versions that do not support the syntax transparently fall back to the previous enumerate-and-fan-out behavior.

### Fixed
- Fixed long-running queries on the Thrift client path (`UseThriftClient=1`) intermittently failing with `Query has been timed out due to inactivity` under sustained concurrency. A transient transport-level failure (stale pooled connection, connection reset, load-balancer idle drop) on a `GetOperationStatus` poll previously abandoned the still-running server operation after a single blip, and the same failure class on `CloseOperation`/`CancelOperation` could leak completed operations until the server reaped them. These idempotent RPCs now transparently reconnect and retry on a fresh connection with bounded, jittered exponential backoff before surfacing the error. Only genuinely transient failures are retried — connection-level errors and transient gateway responses (408/502/504); permanent HTTP errors (401/403/404, and 500) surface immediately, and rate-limit/unavailable responses (429/503) are left to the existing HTTP-layer retry rather than retried again. The retry also honors the statement's `queryTimeout` so a failing poll cannot overshoot its deadline, and `CloseOperation`/`CancelOperation` use a tighter retry budget so cleanup on shutdown cannot stall. Statement submission is deliberately excluded from the retry path to avoid double-execution. The SEA client path is unchanged.
- Fixed `IdleConnectionEvictor` thread leak in long-running applications. Driver-side resources (HTTP client, background threads) are now always released when `Connection.close()` is called, even if statement cleanup or server-side session termination fails.

- Throw `DatabricksSQLException` instead of an unchecked `ClassCastException` when a complex-type getter (`getArray`, `getStruct`, `getMap`) is called on a column of a different complex type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ public static void checkHTTPError(HttpResponse response)
return;
}
LOGGER.error(errorReason);
throw new DatabricksHttpException(errorReason, DEFAULT_HTTP_EXCEPTION_SQLSTATE);
int statusCode = response.getStatusLine().getStatusCode();
throw new DatabricksHttpException(errorReason, DEFAULT_HTTP_EXCEPTION_SQLSTATE, statusCode);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,25 @@ public void checkTimeout() throws DatabricksTimeoutException {
}
}

/**
* Returns the time remaining, in milliseconds, before {@link #checkTimeout()} would actually
* enforce the deadline. Returns {@link Long#MAX_VALUE} when no timeout is configured ({@code
* timeoutSeconds <= 0}), and may return a negative value once the deadline has passed.
*
* <p>{@code checkTimeout()} compares whole (truncated) seconds and throws only once elapsed time
* <em>exceeds</em> {@code timeoutSeconds}, i.e. at {@code (timeoutSeconds + 1) * 1000} ms. This
* method reports the remaining time to that same enforcement point (not the nominal
* {@code timeoutSeconds * 1000}), so a caller capping a backoff sleep on it does not collapse the
* sleep to zero — and then busy-spin — during the sub-second window before enforcement fires.
*/
public long getRemainingMillis() {
if (timeoutSeconds <= 0) {
return Long.MAX_VALUE;
}
long elapsedMillis = System.currentTimeMillis() - startTimeMillis;
return TimeUnit.SECONDS.toMillis(timeoutSeconds + 1L) - elapsedMillis;
}

/**
* Factory method to create a timeout handler for a databricks client with a statement ID. This
* works with any client that implements {@link IDatabricksClient}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,10 @@ private static void throwHttpException(Exception e, HttpUriRequest request)
"Caught error while executing http request: [%s]. Error Message: [%s]",
RequestSanitizer.sanitizeRequest(request), e);
LOGGER.error(e, errorMsg);
throw new DatabricksHttpException(errorMsg, DEFAULT_HTTP_EXCEPTION_SQLSTATE);
// Preserve the original cause (typically a connection-level IOException) so callers can
// distinguish a genuine transport failure from an HTTP-status error and decide whether the
// operation is safe to retry.
throw new DatabricksHttpException(errorMsg, e, DEFAULT_HTTP_EXCEPTION_SQLSTATE);
}

@VisibleForTesting
Expand Down
Loading