Skip to content
Closed
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
2 changes: 1 addition & 1 deletion KERNEL_REV
Original file line number Diff line number Diff line change
@@ -1 +1 @@
80b68e1eef3b613910183a50dfa4dace854d50dd
fcc459bbf3f39bf57e2ee02f14b99c0ec7a70123
41 changes: 41 additions & 0 deletions lib/sea/SeaAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,20 @@ export interface SeaSessionDefaults {
* integer within the napi `u32` range by `buildSeaConnectionOptions`.
*/
maxConnections?: number;
/**
* Retry/backoff tuning forwarded to the kernel (which owns the retry loop
* on the SEA path). These mirror the driver's `ClientConfig` retry knobs —
* the same ones the Thrift `HttpRetryPolicy` uses — converted from the
* connector's milliseconds to the kernel's whole seconds, so a single
* retry config governs both backends. Unset ⇒ kernel default policy.
* Map onto the napi `ConnectionOptions.retry{Min,Max}WaitSecs` /
* `retryMaxAttempts` / `retryOverallTimeoutSecs` (see `buildSeaRetryOptions`).
*/
retryMinWaitSecs?: number;
retryMaxWaitSecs?: number;
/** **Total** attempts (kernel converts to retries-after-first internally). */
retryMaxAttempts?: number;
retryOverallTimeoutSecs?: number;
}

/**
Expand Down Expand Up @@ -274,6 +288,33 @@ export function buildSeaTlsOptions(options: ConnectionOptions): SeaTlsOptions {
* - `HiveDriverError` for unsupported auth modes / Azure-direct /
* custom persistence / ambiguous combinations.
*/
/**
* Convert the driver's `ClientConfig` retry knobs (milliseconds, total-attempt
* count) into the kernel's `ConnectionOptions` retry kwargs (whole seconds).
* The kernel owns the retry loop on the SEA path, so forwarding these keeps SEA
* and Thrift governed by one retry config. `retryMaxAttempts` is a TOTAL attempt
* count on both sides (the kernel converts to retries-after-first internally),
* so it passes through directly. Sub-second delays round to the nearest second
* (the kernel's granularity); all values are clamped into the napi `u32` range.
*/
export function buildSeaRetryOptions(config: {
retryMaxAttempts: number;
retriesTimeout: number;
retryDelayMin: number;
retryDelayMax: number;
}): Required<
Pick<SeaSessionDefaults, 'retryMinWaitSecs' | 'retryMaxWaitSecs' | 'retryMaxAttempts' | 'retryOverallTimeoutSecs'>
> {
const msToSecs = (ms: number): number => Math.min(MAX_U32, Math.max(0, Math.round(ms / 1000)));
const clampU32 = (n: number): number => Math.min(MAX_U32, Math.max(0, Math.trunc(n)));
return {
retryMinWaitSecs: msToSecs(config.retryDelayMin),
retryMaxWaitSecs: msToSecs(config.retryDelayMax),
retryMaxAttempts: clampU32(config.retryMaxAttempts),
retryOverallTimeoutSecs: msToSecs(config.retriesTimeout),
};
}

export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNativeConnectionOptions {
const { authType } = options as { authType?: string };

Expand Down
11 changes: 9 additions & 2 deletions lib/sea/SeaBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { LogLevel } from '../contracts/IDBSQLLogger';
import HiveDriverError from '../errors/HiveDriverError';
import { getSeaNative, SeaNativeBinding, SeaConnection } from './SeaNativeLoader';
import { decodeNapiKernelError } from './SeaErrorMapping';
import { buildSeaConnectionOptions, SeaNativeConnectionOptions } from './SeaAuth';
import { buildSeaConnectionOptions, buildSeaRetryOptions, SeaNativeConnectionOptions } from './SeaAuth';
import { installKernelLogBridge } from './SeaLogging';
import SeaSessionBackend from './SeaSessionBackend';

Expand Down Expand Up @@ -85,7 +85,14 @@ export default class SeaBackend implements IBackend {
// Validate PAT auth + capture the napi-binding option shape.
// Any non-PAT mode (or a missing/empty token) throws here, before
// we ever touch the native binding.
this.nativeOptions = buildSeaConnectionOptions(options);
// Forward the driver's retry config to the kernel, which owns the retry
// loop on the SEA path. This keeps SEA and Thrift governed by one retry
// config (the same `ClientConfig` knobs the Thrift `HttpRetryPolicy` reads),
// converted from the connector's milliseconds to the kernel's whole seconds.
this.nativeOptions = {
...buildSeaConnectionOptions(options),
...buildSeaRetryOptions(this.context.getConfig()),
};

// Bridge the Rust kernel's `tracing` logs into the SAME `DBSQLLogger` the
// driver logs through, so logs from all three layers (driver, napi shim,
Expand Down
28 changes: 28 additions & 0 deletions native/sea/index.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 42 additions & 2 deletions tests/unit/sea/connectionOptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
// limitations under the License.

import { expect } from 'chai';
import { buildSeaConnectionOptions, buildSeaTlsOptions } from '../../../lib/sea/SeaAuth';
import { buildSeaConnectionOptions, buildSeaTlsOptions, buildSeaRetryOptions } from '../../../lib/sea/SeaAuth';
import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient';
import HiveDriverError from '../../../lib/errors/HiveDriverError';

const PAT = { host: 'h.databricks.com', path: '/sql/1.0/warehouses/abc', token: 'dapi-x' };

// Cast helper: the SEA connection-tuning/TLS options live on the internal
// surface, so tests build untyped option literals.
const opts = (extra: Record<string, unknown>) => ({ ...PAT, ...extra } as unknown as ConnectionOptions);
const opts = (extra: Record<string, unknown>) => ({ ...PAT, ...extra }) as unknown as ConnectionOptions;

describe('SeaAuth connection options — intervalsAsString default', () => {
it('always sets intervalsAsString:true (thrift-compatible interval rendering)', () => {
Expand Down Expand Up @@ -119,3 +119,43 @@ describe('SeaAuth TLS options (buildSeaTlsOptions)', () => {
expect(native.checkServerCertificate).to.equal(false);
});
});

describe('SeaAuth retry options — buildSeaRetryOptions', () => {
// The driver's ClientConfig retry defaults (ms / total-attempt count).
const defaults = {
retryMaxAttempts: 5,
retriesTimeout: 15 * 60 * 1000,
retryDelayMin: 1000,
retryDelayMax: 60 * 1000,
};

it('converts the connector ms knobs to the kernel whole-second kwargs', () => {
const r = buildSeaRetryOptions(defaults);
expect(r.retryMinWaitSecs).to.equal(1); // 1000ms
expect(r.retryMaxWaitSecs).to.equal(60); // 60000ms
expect(r.retryOverallTimeoutSecs).to.equal(900); // 15min
});

it('passes retryMaxAttempts through as a TOTAL attempt count (kernel converts to retries)', () => {
expect(buildSeaRetryOptions({ ...defaults, retryMaxAttempts: 5 }).retryMaxAttempts).to.equal(5);
expect(buildSeaRetryOptions({ ...defaults, retryMaxAttempts: 0 }).retryMaxAttempts).to.equal(0);
});

it('rounds sub-second delays to the nearest second (kernel granularity)', () => {
const r = buildSeaRetryOptions({ ...defaults, retryDelayMin: 1500, retryDelayMax: 2400 });
expect(r.retryMinWaitSecs).to.equal(2); // 1.5s → 2
expect(r.retryMaxWaitSecs).to.equal(2); // 2.4s → 2
});

it('clamps negative/garbage inputs into the napi u32 range', () => {
const r = buildSeaRetryOptions({
retryMaxAttempts: -3,
retriesTimeout: -1,
retryDelayMin: -1000,
retryDelayMax: 0,
});
expect(r.retryMaxAttempts).to.equal(0);
expect(r.retryMinWaitSecs).to.equal(0);
expect(r.retryOverallTimeoutSecs).to.equal(0);
});
});
Loading