Skip to content

Commit 012f53f

Browse files
committed
Merge branch 'main' into token-federation-pr3-examples
# Conflicts: # lib/connection/auth/tokenProvider/FederationProvider.ts
2 parents 1155e42 + 6f49f6c commit 012f53f

File tree

11 files changed

+901
-8
lines changed

11 files changed

+901
-8
lines changed

.github/CODEOWNERS

Lines changed: 0 additions & 1 deletion
This file was deleted.

lib/DBSQLSession.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ export default class DBSQLSession implements IDBSQLSession {
232232
}
233233

234234
if (ProtocolVersion.supportsArrowCompression(this.serverProtocolVersion) && request.canDownloadResult !== true) {
235-
request.canDecompressLZ4Result = (options.useLZ4Compression ?? clientConfig.useLZ4Compression) && Boolean(LZ4);
235+
request.canDecompressLZ4Result = (options.useLZ4Compression ?? clientConfig.useLZ4Compression) && Boolean(LZ4());
236236
}
237237

238238
const operationPromise = driver.executeStatement(request);

lib/connection/auth/tokenProvider/FederationProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ export default class FederationProvider implements ITokenProvider {
225225
}
226226

227227
// Exponential backoff: 1s, 2s, 4s
228-
const delay = RETRY_BASE_DELAY_MS * (2 ** attempt);
228+
const delay = RETRY_BASE_DELAY_MS * 2 ** attempt;
229229
await new Promise<void>((resolve) => {
230230
setTimeout(resolve, delay);
231231
});

lib/contracts/IClientContext.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ export interface ClientConfig {
2222

2323
useLZ4Compression: boolean;
2424
enableMetricViewMetadata?: boolean;
25+
26+
// Telemetry configuration
27+
telemetryEnabled?: boolean;
28+
telemetryBatchSize?: number;
29+
telemetryFlushIntervalMs?: number;
30+
telemetryMaxRetries?: number;
31+
telemetryAuthenticatedExport?: boolean;
32+
telemetryCircuitBreakerThreshold?: number;
33+
telemetryCircuitBreakerTimeout?: number;
2534
}
2635

2736
export default interface IClientContext {

lib/result/ArrowResultHandler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default class ArrowResultHandler implements IResultsProvider<ArrowBatch>
2626
this.arrowSchema = arrowSchema ?? hiveSchemaToArrowSchema(schema);
2727
this.isLZ4Compressed = lz4Compressed ?? false;
2828

29-
if (this.isLZ4Compressed && !LZ4) {
29+
if (this.isLZ4Compressed && !LZ4()) {
3030
throw new HiveDriverError('Cannot handle LZ4 compressed result: module `lz4` not installed');
3131
}
3232
}
@@ -52,7 +52,7 @@ export default class ArrowResultHandler implements IResultsProvider<ArrowBatch>
5252
let totalRowCount = 0;
5353
rowSet?.arrowBatches?.forEach(({ batch, rowCount }) => {
5454
if (batch) {
55-
batches.push(this.isLZ4Compressed ? LZ4!.decode(batch) : batch);
55+
batches.push(this.isLZ4Compressed ? LZ4()!.decode(batch) : batch);
5656
totalRowCount += rowCount.toNumber(true);
5757
}
5858
});

lib/result/CloudFetchResultHandler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export default class CloudFetchResultHandler implements IResultsProvider<ArrowBa
2727
this.source = source;
2828
this.isLZ4Compressed = lz4Compressed ?? false;
2929

30-
if (this.isLZ4Compressed && !LZ4) {
30+
if (this.isLZ4Compressed && !LZ4()) {
3131
throw new HiveDriverError('Cannot handle LZ4 compressed result: module `lz4` not installed');
3232
}
3333
}
@@ -64,7 +64,7 @@ export default class CloudFetchResultHandler implements IResultsProvider<ArrowBa
6464
}
6565

6666
if (this.isLZ4Compressed) {
67-
batch.batches = batch.batches.map((buffer) => LZ4!.decode(buffer));
67+
batch.batches = batch.batches.map((buffer) => LZ4()!.decode(buffer));
6868
}
6969
return batch;
7070
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/**
2+
* Copyright (c) 2025 Databricks Contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import AuthenticationError from '../errors/AuthenticationError';
18+
import RetryError from '../errors/RetryError';
19+
20+
/**
21+
* Classifies exceptions as terminal (unrecoverable) vs retryable.
22+
*
23+
* Terminal exceptions should be flushed immediately to telemetry,
24+
* while retryable exceptions are buffered until statement completion.
25+
*
26+
* This follows the JDBC driver pattern of smart exception flushing
27+
* to optimize telemetry export efficiency while ensuring critical
28+
* errors are reported immediately.
29+
*/
30+
export default class ExceptionClassifier {
31+
/**
32+
* Determines if an exception is terminal (non-retryable).
33+
*
34+
* Terminal exceptions indicate unrecoverable failures that should
35+
* be reported immediately, such as authentication failures, invalid
36+
* requests, or resource not found errors.
37+
*
38+
* @param error - The error to classify
39+
* @returns true if the error is terminal, false otherwise
40+
*/
41+
static isTerminal(error: Error): boolean {
42+
// Check for AuthenticationError (terminal)
43+
if (error instanceof AuthenticationError) {
44+
return true;
45+
}
46+
47+
// Check for HTTP status codes in error properties
48+
// Supporting both 'statusCode' and 'status' property names for flexibility
49+
const statusCode = (error as any).statusCode ?? (error as any).status;
50+
51+
if (typeof statusCode === 'number') {
52+
// Terminal HTTP status codes:
53+
// 400 - Bad Request (invalid request format)
54+
// 401 - Unauthorized (authentication required)
55+
// 403 - Forbidden (permission denied)
56+
// 404 - Not Found (resource does not exist)
57+
return statusCode === 400 || statusCode === 401 || statusCode === 403 || statusCode === 404;
58+
}
59+
60+
// Default to false for unknown error types
61+
return false;
62+
}
63+
64+
/**
65+
* Determines if an exception is retryable.
66+
*
67+
* Retryable exceptions indicate transient failures that may succeed
68+
* on retry, such as rate limiting, server errors, or network timeouts.
69+
*
70+
* @param error - The error to classify
71+
* @returns true if the error is retryable, false otherwise
72+
*/
73+
static isRetryable(error: Error): boolean {
74+
// Check for RetryError (explicitly retryable)
75+
if (error instanceof RetryError) {
76+
return true;
77+
}
78+
79+
// Check for network timeout errors
80+
if (error.name === 'TimeoutError' || error.message.includes('timeout')) {
81+
return true;
82+
}
83+
84+
// Check for HTTP status codes in error properties
85+
// Supporting both 'statusCode' and 'status' property names for flexibility
86+
const statusCode = (error as any).statusCode ?? (error as any).status;
87+
88+
if (typeof statusCode === 'number') {
89+
// Retryable HTTP status codes:
90+
// 429 - Too Many Requests (rate limiting)
91+
// 500 - Internal Server Error
92+
// 502 - Bad Gateway
93+
// 503 - Service Unavailable
94+
// 504 - Gateway Timeout
95+
return statusCode === 429 || statusCode === 500 || statusCode === 502 || statusCode === 503 || statusCode === 504;
96+
}
97+
98+
// Default to false for unknown error types
99+
return false;
100+
}
101+
}

0 commit comments

Comments
 (0)