@lightdash/warehouses 1.75.1 → 1.76.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/index.d.ts +0 -2
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +0 -3
  5. package/dist/warehouseClientFromCredentials.d.ts +1 -2
  6. package/dist/warehouseClientFromCredentials.d.ts.map +1 -1
  7. package/dist/warehouseClientFromCredentials.js +2 -2
  8. package/dist/warehouseClients/DuckdbWarehouseClient.d.ts +7 -6
  9. package/dist/warehouseClients/DuckdbWarehouseClient.d.ts.map +1 -1
  10. package/dist/warehouseClients/DuckdbWarehouseClient.js +14 -128
  11. package/dist/warehouseClients/DuckdbWarehouseClient.test.js +0 -307
  12. package/package.json +2 -2
  13. package/dist/warehouseClients/MotherduckErrorClassifier.d.ts +0 -3
  14. package/dist/warehouseClients/MotherduckErrorClassifier.d.ts.map +0 -1
  15. package/dist/warehouseClients/MotherduckErrorClassifier.js +0 -34
  16. package/dist/warehouseClients/MotherduckErrorClassifier.test.d.ts +0 -2
  17. package/dist/warehouseClients/MotherduckErrorClassifier.test.d.ts.map +0 -1
  18. package/dist/warehouseClients/MotherduckErrorClassifier.test.js +0 -31
  19. package/dist/warehouseClients/MotherduckInstanceCache.catalog.test.d.ts +0 -2
  20. package/dist/warehouseClients/MotherduckInstanceCache.catalog.test.d.ts.map +0 -1
  21. package/dist/warehouseClients/MotherduckInstanceCache.catalog.test.js +0 -52
  22. package/dist/warehouseClients/MotherduckInstanceCache.d.ts +0 -58
  23. package/dist/warehouseClients/MotherduckInstanceCache.d.ts.map +0 -1
  24. package/dist/warehouseClients/MotherduckInstanceCache.integration.test.d.ts +0 -2
  25. package/dist/warehouseClients/MotherduckInstanceCache.integration.test.d.ts.map +0 -1
  26. package/dist/warehouseClients/MotherduckInstanceCache.integration.test.js +0 -172
  27. package/dist/warehouseClients/MotherduckInstanceCache.js +0 -316
  28. package/dist/warehouseClients/MotherduckInstanceCache.security.test.d.ts +0 -2
  29. package/dist/warehouseClients/MotherduckInstanceCache.security.test.d.ts.map +0 -1
  30. package/dist/warehouseClients/MotherduckInstanceCache.security.test.js +0 -192
  31. package/dist/warehouseClients/MotherduckInstanceCache.test.d.ts +0 -2
  32. package/dist/warehouseClients/MotherduckInstanceCache.test.d.ts.map +0 -1
  33. package/dist/warehouseClients/MotherduckInstanceCache.test.js +0 -302
@@ -1,46 +1,11 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
38
5
  Object.defineProperty(exports, "__esModule", { value: true });
39
6
  const common_1 = require("@lightdash/common");
40
- const crypto_1 = require("crypto");
41
7
  const promises_1 = __importDefault(require("fs/promises"));
42
8
  const DuckdbWarehouseClient_1 = require("./DuckdbWarehouseClient");
43
- const MotherduckInstanceCache = __importStar(require("./MotherduckInstanceCache"));
44
9
  const createInstanceMock = vi.fn();
45
10
  const duckdbS3Credentials = {
46
11
  type: 'duckdb_s3',
@@ -193,12 +158,6 @@ describe('DuckdbWarehouseClient', () => {
193
158
  beforeEach(() => {
194
159
  vi.clearAllMocks();
195
160
  DuckdbWarehouseClient_1.DuckdbWarehouseClient.resetSharedDuckdbStateForTesting();
196
- MotherduckInstanceCache.resetForTesting();
197
- MotherduckInstanceCache.configure({
198
- idleTtlMs: 60000,
199
- maxAgeMs: 60000,
200
- maxEntries: 8,
201
- });
202
161
  });
203
162
  it('should return query rows and mapped fields', async () => {
204
163
  const rows = [
@@ -294,272 +253,6 @@ describe('DuckdbWarehouseClient', () => {
294
253
  await client.runQuery('SELECT 1 AS val', undefined, 'Europe/London');
295
254
  expect(logger.info).toHaveBeenCalledWith(expect.stringMatching(/Europe\/London.*MotherDuck.*saas_mode.*server default zone.*configured zone/));
296
255
  });
297
- describe('MotherDuck instance caching', () => {
298
- const credentials = {
299
- type: common_1.WarehouseTypes.DUCKDB,
300
- connectionType: common_1.DuckdbConnectionType.MOTHERDUCK,
301
- database: 'analytics',
302
- schema: 'main',
303
- token: 'token-a',
304
- };
305
- it('reuses an instance without issuing SET or PRAGMA statements and reports connect timing', async () => {
306
- const runMock = vi.fn();
307
- const streamMock = vi.fn(async () => getMockStreamResult([[{ val: 1 }]], [DUCKDB_TYPE_IDS.INTEGER]));
308
- const onPhaseTiming = vi.fn();
309
- const events = [];
310
- MotherduckInstanceCache.setObserver((event) => events.push(event));
311
- createInstanceMock.mockResolvedValue(createMockConnection(streamMock, runMock));
312
- const client = new DuckdbWarehouseClient_1.DuckdbWarehouseClient(credentials, {
313
- enableInstanceCache: true,
314
- projectUuid: 'project-a',
315
- });
316
- await client.streamQuery('SELECT 1 AS val', vi.fn(), {
317
- onPhaseTiming,
318
- });
319
- await client.streamQuery('SELECT 1 AS val', vi.fn(), {
320
- onPhaseTiming,
321
- });
322
- expect(createInstanceMock).toHaveBeenCalledOnce();
323
- expect(runMock).not.toHaveBeenCalledWith(expect.stringMatching(/^(?:SET|PRAGMA)\b/i));
324
- const acquisitions = events.filter((event) => event.type === 'acquire');
325
- expect(acquisitions).toHaveLength(2);
326
- acquisitions.forEach((event) => {
327
- expect(onPhaseTiming).toHaveBeenCalledWith('connect', event.waitMs + event.instanceCreateMs + event.connectMs);
328
- });
329
- });
330
- it('reports instance creation and connection as direct connect timing', async () => {
331
- const streamMock = vi.fn(async () => getMockStreamResult([[{ val: 1 }]], [DUCKDB_TYPE_IDS.INTEGER]));
332
- const onPhaseTiming = vi.fn();
333
- const performanceNow = vi
334
- .spyOn(performance, 'now')
335
- .mockReturnValueOnce(0)
336
- .mockReturnValueOnce(10)
337
- .mockReturnValueOnce(30)
338
- .mockReturnValueOnce(40)
339
- .mockReturnValueOnce(45)
340
- .mockReturnValue(45);
341
- createInstanceMock.mockResolvedValue(createMockConnection(streamMock));
342
- const client = new DuckdbWarehouseClient_1.DuckdbWarehouseClient(credentials);
343
- try {
344
- await client.streamQuery('SELECT 1 AS val', vi.fn(), {
345
- onPhaseTiming,
346
- });
347
- }
348
- finally {
349
- performanceNow.mockRestore();
350
- }
351
- expect(onPhaseTiming).toHaveBeenCalledWith('connect', 25);
352
- });
353
- it('falls back to direct sessions when user credentials are required', async () => {
354
- const streamMock = vi.fn(async () => getMockStreamResult([[{ val: 1 }]], [DUCKDB_TYPE_IDS.INTEGER]));
355
- createInstanceMock.mockImplementation(async () => createMockConnection(streamMock));
356
- const client = new DuckdbWarehouseClient_1.DuckdbWarehouseClient({ ...credentials, requireUserCredentials: true }, { enableInstanceCache: true, projectUuid: 'project-a' });
357
- await client.runQuery('SELECT 1 AS val');
358
- await client.runQuery('SELECT 1 AS val');
359
- expect(createInstanceMock).toHaveBeenCalledTimes(2);
360
- });
361
- it('retries one stale failure before rows are emitted and replaces only the failed entry', async () => {
362
- const events = [];
363
- MotherduckInstanceCache.setObserver((event) => events.push(event));
364
- const staleError = new Error('Connection Error: Connection has already been closed');
365
- const firstStream = vi.fn().mockRejectedValue(staleError);
366
- const recoveredStream = vi.fn(async () => getMockStreamResult([[{ val: 1 }]], [DUCKDB_TYPE_IDS.INTEGER]));
367
- const firstInstance = createMockConnection(firstStream);
368
- const recoveredInstance = createMockConnection(recoveredStream);
369
- createInstanceMock
370
- .mockResolvedValueOnce(firstInstance)
371
- .mockResolvedValueOnce(recoveredInstance);
372
- const client = new DuckdbWarehouseClient_1.DuckdbWarehouseClient(credentials, {
373
- enableInstanceCache: true,
374
- projectUuid: 'project-a',
375
- });
376
- await expect(client.runQuery('SELECT 1 AS val')).resolves.toEqual(expect.objectContaining({ rows: [{ val: 1 }] }));
377
- expect(createInstanceMock).toHaveBeenCalledTimes(2);
378
- expect(firstInstance.closeSync).toHaveBeenCalledOnce();
379
- expect(events).toContainEqual(expect.objectContaining({
380
- type: 'evict',
381
- reason: 'stale',
382
- }));
383
- expect(events).toContainEqual(expect.objectContaining({
384
- type: 'retry',
385
- outcome: 'recovered',
386
- }));
387
- });
388
- it('reports a failed retry and does not loop on a second stale failure', async () => {
389
- const events = [];
390
- MotherduckInstanceCache.setObserver((event) => events.push(event));
391
- const staleError = new Error('Connection Error: Connection has already been closed');
392
- const firstInstance = createMockConnection(vi.fn().mockRejectedValue(staleError));
393
- const secondInstance = createMockConnection(vi.fn().mockRejectedValue(staleError));
394
- createInstanceMock
395
- .mockResolvedValueOnce(firstInstance)
396
- .mockResolvedValueOnce(secondInstance);
397
- const client = new DuckdbWarehouseClient_1.DuckdbWarehouseClient(credentials, {
398
- enableInstanceCache: true,
399
- projectUuid: 'project-a',
400
- });
401
- await expect(client.runQuery('SELECT 1 AS val')).rejects.toThrow(staleError);
402
- expect(createInstanceMock).toHaveBeenCalledTimes(2);
403
- expect(firstInstance.closeSync).toHaveBeenCalledOnce();
404
- expect(secondInstance.closeSync).toHaveBeenCalledOnce();
405
- expect(events).toContainEqual(expect.objectContaining({
406
- type: 'retry',
407
- outcome: 'failed',
408
- }));
409
- });
410
- it('does not retry a stale failure after rows reached the consumer', async () => {
411
- const events = [];
412
- MotherduckInstanceCache.setObserver((event) => events.push(event));
413
- const staleError = new Error('Invalid Input Error: Cannot execute statement of closed connection');
414
- const streamMock = vi.fn(async () => {
415
- let hasYielded = false;
416
- const iterator = {
417
- next: async () => {
418
- if (!hasYielded) {
419
- hasYielded = true;
420
- return { done: false, value: [{ val: 1 }] };
421
- }
422
- throw staleError;
423
- },
424
- [Symbol.asyncIterator]() {
425
- return this;
426
- },
427
- };
428
- return {
429
- columnCount: 1,
430
- columnNames: () => ['val'],
431
- columnTypeId: () => DUCKDB_TYPE_IDS.INTEGER,
432
- yieldRowObjectJson: () => iterator,
433
- };
434
- });
435
- const instance = createMockConnection(streamMock);
436
- createInstanceMock.mockResolvedValue(instance);
437
- const streamCallback = vi.fn();
438
- const client = new DuckdbWarehouseClient_1.DuckdbWarehouseClient(credentials, {
439
- enableInstanceCache: true,
440
- projectUuid: 'project-a',
441
- });
442
- await expect(client.streamQuery('SELECT 1 AS val', streamCallback)).rejects.toThrow(staleError);
443
- expect(streamCallback).toHaveBeenCalledOnce();
444
- expect(createInstanceMock).toHaveBeenCalledOnce();
445
- expect(instance.closeSync).toHaveBeenCalledOnce();
446
- expect(events).toContainEqual(expect.objectContaining({
447
- type: 'evict',
448
- reason: 'stale',
449
- }));
450
- expect(events).not.toContainEqual(expect.objectContaining({ type: 'retry' }));
451
- });
452
- it('invalidates authentication failures without retrying', async () => {
453
- const events = [];
454
- MotherduckInstanceCache.setObserver((event) => events.push(event));
455
- const authError = new Error('Invalid Input Error: MD Authentication Error: Invalid token');
456
- const streamMock = vi.fn().mockRejectedValue(authError);
457
- const instance = createMockConnection(streamMock);
458
- createInstanceMock.mockResolvedValue(instance);
459
- const client = new DuckdbWarehouseClient_1.DuckdbWarehouseClient(credentials, {
460
- enableInstanceCache: true,
461
- projectUuid: 'project-a',
462
- });
463
- await expect(client.runQuery('SELECT 1 AS val')).rejects.toThrow(authError);
464
- expect(createInstanceMock).toHaveBeenCalledOnce();
465
- expect(instance.closeSync).toHaveBeenCalledOnce();
466
- expect(events).toContainEqual(expect.objectContaining({
467
- type: 'evict',
468
- reason: 'auth',
469
- }));
470
- expect(events).not.toContainEqual(expect.objectContaining({ type: 'retry' }));
471
- });
472
- it('never includes the cache digest in cached-instance log lines or metadata', async () => {
473
- const streamMock = vi.fn(async () => getMockStreamResult([[{ val: 1 }]], [DUCKDB_TYPE_IDS.INTEGER]));
474
- const logger = { info: vi.fn() };
475
- createInstanceMock.mockResolvedValue(createMockConnection(streamMock));
476
- const client = new DuckdbWarehouseClient_1.DuckdbWarehouseClient(credentials, {
477
- enableInstanceCache: true,
478
- projectUuid: 'project-a',
479
- logger,
480
- });
481
- await client.runQuery('SELECT 1 AS val');
482
- const connectionString = createInstanceMock.mock.calls[0][0];
483
- if (typeof connectionString !== 'string') {
484
- throw new Error('Expected a MotherDuck connection string');
485
- }
486
- const digest = (0, crypto_1.createHash)('sha256')
487
- .update(JSON.stringify({ connectionString, v: 1 }))
488
- .digest('hex');
489
- expect(JSON.stringify(logger.info.mock.calls)).not.toContain(digest);
490
- });
491
- });
492
- describe('session strategy routing', () => {
493
- const createSuccessfulInstance = () => createMockConnection(vi.fn(async () => getMockStreamResult([[{ val: 1 }]], [DUCKDB_TYPE_IDS.INTEGER])), vi.fn());
494
- it('keeps embedded playground databases on direct read-only sessions', async () => {
495
- createInstanceMock.mockResolvedValue(createSuccessfulInstance());
496
- const onPhaseTiming = vi.fn();
497
- const client = new DuckdbWarehouseClient_1.DuckdbWarehouseClient({
498
- type: common_1.WarehouseTypes.DUCKDB,
499
- connectionType: common_1.DuckdbConnectionType.EMBEDDED,
500
- dataset: 'jaffle_shop',
501
- });
502
- await client.streamQuery('SELECT 1 AS val', vi.fn(), {
503
- onPhaseTiming,
504
- });
505
- expect(createInstanceMock).toHaveBeenCalledWith(expect.stringContaining('jaffle_shop.duckdb'), expect.objectContaining({ access_mode: 'READ_ONLY' }));
506
- expect(onPhaseTiming).toHaveBeenCalledWith('connect', expect.any(Number));
507
- });
508
- it('routes resource-limited in-memory clients to isolated sessions', async () => {
509
- createInstanceMock.mockResolvedValue(createSuccessfulInstance());
510
- const client = new DuckdbWarehouseClient_1.DuckdbWarehouseClient(undefined, {
511
- resourceLimits: { memoryLimit: '64MB', threads: 1 },
512
- });
513
- await client.runQuery('SELECT 1 AS val');
514
- await client.runQuery('SELECT 1 AS val');
515
- expect(createInstanceMock).toHaveBeenCalledTimes(2);
516
- expect(createInstanceMock).toHaveBeenNthCalledWith(1, ':memory:');
517
- expect(createInstanceMock).toHaveBeenNthCalledWith(2, ':memory:');
518
- });
519
- it('routes explicit cache keys to shared sessions', async () => {
520
- createInstanceMock.mockResolvedValue(createSuccessfulInstance());
521
- const client = new DuckdbWarehouseClient_1.DuckdbWarehouseClient(undefined, {
522
- instanceCacheKey: 'routing-shared-instance',
523
- });
524
- await client.runQuery('SELECT 1 AS val');
525
- await client.runQuery('SELECT 1 AS val');
526
- expect(createInstanceMock).toHaveBeenCalledExactlyOnceWith(':memory:');
527
- });
528
- it('keeps DuckLake on its existing shared session strategy', async () => {
529
- createInstanceMock.mockResolvedValue(createSuccessfulInstance());
530
- const client = new DuckdbWarehouseClient_1.DuckdbWarehouseClient({
531
- type: common_1.WarehouseTypes.DUCKDB,
532
- connectionType: common_1.DuckdbConnectionType.DUCKLAKE,
533
- schema: 'main',
534
- catalogAlias: 'ducklake',
535
- catalog: {
536
- type: common_1.DucklakeCatalogType.POSTGRES,
537
- host: 'pg.example.com',
538
- port: 5432,
539
- database: 'catalog',
540
- user: 'ducklake_user',
541
- password: 'password',
542
- },
543
- dataPath: {
544
- type: common_1.DucklakeDataPathType.S3,
545
- url: 's3://bucket/path/',
546
- region: 'us-east-1',
547
- },
548
- });
549
- await client.runQuery('SELECT 1 AS val');
550
- await client.runQuery('SELECT 1 AS val');
551
- expect(createInstanceMock).toHaveBeenCalledExactlyOnceWith(':memory:');
552
- });
553
- it('keeps default in-memory clients on ephemeral sessions', async () => {
554
- createInstanceMock.mockResolvedValue(createSuccessfulInstance());
555
- const client = new DuckdbWarehouseClient_1.DuckdbWarehouseClient();
556
- await client.runQuery('SELECT 1 AS val');
557
- await client.runQuery('SELECT 1 AS val');
558
- expect(createInstanceMock).toHaveBeenCalledTimes(2);
559
- expect(createInstanceMock).toHaveBeenNthCalledWith(1, ':memory:');
560
- expect(createInstanceMock).toHaveBeenNthCalledWith(2, ':memory:');
561
- });
562
- });
563
256
  it('sets timezone for in-memory and embedded DuckDB queries', async () => {
564
257
  const runMock = vi.fn();
565
258
  const streamMock = vi.fn(async () => getMockStreamResult([[{ val: 1 }]], [DUCKDB_TYPE_IDS.INTEGER]));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightdash/warehouses",
3
- "version": "1.75.1",
3
+ "version": "1.76.1",
4
4
  "description": "Warehouse connectors for Lightdash",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,7 +29,7 @@
29
29
  "snowflake-sdk": "2.3.4",
30
30
  "ssh2": "1.14.0",
31
31
  "trino-client": "0.2.9",
32
- "@lightdash/common": "1.75.1"
32
+ "@lightdash/common": "1.76.1"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/big.js": "6.2.2",
@@ -1,3 +0,0 @@
1
- export type MotherduckErrorClass = 'auth' | 'stale' | 'other';
2
- export declare const classifyMotherduckError: (error: unknown) => MotherduckErrorClass;
3
- //# sourceMappingURL=MotherduckErrorClassifier.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MotherduckErrorClassifier.d.ts","sourceRoot":"","sources":["../../src/warehouseClients/MotherduckErrorClassifier.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAuB9D,eAAO,MAAM,uBAAuB,UACzB,OAAO,KACf,oBASF,CAAC"}
@@ -1,34 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.classifyMotherduckError = void 0;
4
- const common_1 = require("@lightdash/common");
5
- const AUTH_ERROR_PATTERNS = [
6
- /authentication failed/i,
7
- /authentication error.*invalid token/i,
8
- /invalid motherduck token/i,
9
- /motherduck token.*(?:expired|invalid|rejected)/i,
10
- /not authenticated/i,
11
- /check your motherduck token/i,
12
- /unauthorized/i,
13
- /permission_denied/i,
14
- /could not connect to motherduck/i,
15
- ];
16
- const STALE_ERROR_PATTERNS = [
17
- /connection.*(?:already )?closed/i,
18
- /closed.*connection/i,
19
- /database.*detached/i,
20
- /detached.*database/i,
21
- /instance.*closed/i,
22
- /invalidated.*database/i,
23
- ];
24
- const classifyMotherduckError = (error) => {
25
- const message = (0, common_1.getErrorMessage)(error);
26
- if (AUTH_ERROR_PATTERNS.some((pattern) => pattern.test(message))) {
27
- return 'auth';
28
- }
29
- if (STALE_ERROR_PATTERNS.some((pattern) => pattern.test(message))) {
30
- return 'stale';
31
- }
32
- return 'other';
33
- };
34
- exports.classifyMotherduckError = classifyMotherduckError;
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=MotherduckErrorClassifier.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MotherduckErrorClassifier.test.d.ts","sourceRoot":"","sources":["../../src/warehouseClients/MotherduckErrorClassifier.test.ts"],"names":[],"mappings":""}
@@ -1,31 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const MotherduckErrorClassifier_1 = require("./MotherduckErrorClassifier");
4
- describe('classifyMotherduckError', () => {
5
- it.each([
6
- 'Connection Error: Connection has already been closed',
7
- 'Invalid Input Error: Cannot execute statement of closed connection',
8
- 'Catalog Error: Database analytics was detached',
9
- 'Invalid Input Error: database was invalidated because the instance was closed',
10
- ])('classifies stale DuckDB handles: %s', (message) => {
11
- expect((0, MotherduckErrorClassifier_1.classifyMotherduckError)(new Error(message))).toBe('stale');
12
- });
13
- it.each([
14
- 'MotherDuck Authentication failed: token rejected',
15
- 'Invalid Input Error: MD Authentication Error: Invalid token',
16
- 'Your request is not authenticated. Please check your MotherDuck token...',
17
- 'HTTP Error: Unauthorized',
18
- "Could not connect to MotherDuck... (PERMISSION_DENIED, RPC 'CREATE_SLT')",
19
- 'RPC failed with PeRmIsSiOn_DeNiEd',
20
- 'COULD NOT CONNECT TO MOTHERDUCK',
21
- ])('classifies MotherDuck authentication failures: %s', (message) => {
22
- expect((0, MotherduckErrorClassifier_1.classifyMotherduckError)(new Error(message))).toBe('auth');
23
- });
24
- it.each([
25
- 'Binder Error: Referenced column missing_column not found',
26
- 'Catalog Error: Table with name missing_table does not exist',
27
- 'Invalid Input Error: configuration has been locked',
28
- ])('leaves SQL and configuration errors untouched: %s', (message) => {
29
- expect((0, MotherduckErrorClassifier_1.classifyMotherduckError)(new Error(message))).toBe('other');
30
- });
31
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=MotherduckInstanceCache.catalog.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MotherduckInstanceCache.catalog.test.d.ts","sourceRoot":"","sources":["../../src/warehouseClients/MotherduckInstanceCache.catalog.test.ts"],"names":[],"mappings":""}
@@ -1,52 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const MotherduckInstanceCache_1 = require("./MotherduckInstanceCache");
4
- describe('MotherduckInstanceCache catalog freshness regression', () => {
5
- beforeEach(() => {
6
- (0, MotherduckInstanceCache_1.resetForTesting)();
7
- (0, MotherduckInstanceCache_1.configure)({
8
- idleTtlMs: 60000,
9
- maxAgeMs: 60000,
10
- maxEntries: 1,
11
- });
12
- });
13
- afterEach(async () => {
14
- await (0, MotherduckInstanceCache_1.closeAll)('shutdown');
15
- });
16
- it('serves a new table and column through both an already-warm connection and a fresh connection on the held instance', async () => {
17
- let entryId = '';
18
- await (0, MotherduckInstanceCache_1.withInstance)(':memory:', {}, async (instance, acquiredEntryId) => {
19
- entryId = acquiredEntryId;
20
- const warmConnection = await instance.connect();
21
- const ddlConnection = await instance.connect();
22
- try {
23
- await warmConnection.run('SELECT * FROM information_schema.columns');
24
- await ddlConnection.run('CREATE TABLE catalog_freshness (initial_column INTEGER)');
25
- await ddlConnection.run('ALTER TABLE catalog_freshness ADD COLUMN added_column VARCHAR');
26
- const warmResult = await warmConnection.run("SELECT column_name FROM information_schema.columns WHERE table_name = 'catalog_freshness' ORDER BY ordinal_position");
27
- await expect(warmResult.getRowObjects()).resolves.toEqual([
28
- { column_name: 'initial_column' },
29
- { column_name: 'added_column' },
30
- ]);
31
- }
32
- finally {
33
- ddlConnection.closeSync();
34
- warmConnection.closeSync();
35
- }
36
- });
37
- await (0, MotherduckInstanceCache_1.withInstance)(':memory:', {}, async (instance, acquiredEntryId) => {
38
- expect(acquiredEntryId).toBe(entryId);
39
- const freshConnection = await instance.connect();
40
- try {
41
- const freshResult = await freshConnection.run("SELECT column_name FROM information_schema.columns WHERE table_name = 'catalog_freshness' ORDER BY ordinal_position");
42
- await expect(freshResult.getRowObjects()).resolves.toEqual([
43
- { column_name: 'initial_column' },
44
- { column_name: 'added_column' },
45
- ]);
46
- }
47
- finally {
48
- freshConnection.closeSync();
49
- }
50
- });
51
- });
52
- });
@@ -1,58 +0,0 @@
1
- import { DuckDBInstance } from '@duckdb/node-api';
2
- export type DuckdbInstance = Awaited<ReturnType<typeof DuckDBInstance.create>>;
3
- export type EvictionReason = 'idle_ttl' | 'max_age' | 'lru' | 'stale' | 'auth' | 'failures' | 'credentials_updated' | 'shutdown';
4
- export type MotherduckCacheEvent = {
5
- type: 'acquire';
6
- result: 'hit' | 'miss';
7
- entryId: string;
8
- projectUuid?: string;
9
- waitMs: number;
10
- instanceCreateMs: number;
11
- connectMs: number;
12
- } | {
13
- type: 'evict';
14
- entryId: string;
15
- projectUuid?: string;
16
- reason: EvictionReason;
17
- ageMs: number;
18
- } | {
19
- type: 'retry';
20
- entryId: string;
21
- outcome: 'recovered' | 'failed';
22
- } | {
23
- type: 'size';
24
- entries: number;
25
- };
26
- type CacheOptions = {
27
- idleTtlMs: number;
28
- maxAgeMs: number;
29
- maxEntries: number;
30
- maxConsecutiveFailures?: number;
31
- };
32
- export type MotherduckCacheAcquisitionTiming = {
33
- waitMs: number;
34
- instanceCreateMs: number;
35
- connectMs: number;
36
- };
37
- export declare const configure: (nextOptions: CacheOptions) => void;
38
- export declare const setObserver: (nextObserver: (event: MotherduckCacheEvent) => void) => void;
39
- export declare const withInstance: <T>(connectionString: string, ctx: {
40
- projectUuid?: string;
41
- }, fn: (instance: DuckdbInstance, entryId: string, timing: MotherduckCacheAcquisitionTiming) => Promise<T>) => Promise<T>;
42
- export declare const invalidate: (entryId: string, reason: EvictionReason) => Promise<void>;
43
- export declare const invalidateByConnectionString: (connectionString: string, reason: EvictionReason) => void;
44
- export declare const closeAll: (reason: EvictionReason) => Promise<void>;
45
- export declare const recordRetry: (entryId: string, outcome: 'recovered' | 'failed') => void;
46
- export declare const resetForTesting: () => void;
47
- export declare const MotherduckInstanceCache: {
48
- configure: typeof configure;
49
- setObserver: typeof setObserver;
50
- withInstance: typeof withInstance;
51
- invalidate: typeof invalidate;
52
- invalidateByConnectionString: typeof invalidateByConnectionString;
53
- recordRetry: typeof recordRetry;
54
- closeAll: typeof closeAll;
55
- resetForTesting: typeof resetForTesting;
56
- };
57
- export {};
58
- //# sourceMappingURL=MotherduckInstanceCache.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MotherduckInstanceCache.d.ts","sourceRoot":"","sources":["../../src/warehouseClients/MotherduckInstanceCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGlD,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;AAE/E,MAAM,MAAM,cAAc,GACpB,UAAU,GACV,SAAS,GACT,KAAK,GACL,OAAO,GACP,MAAM,GACN,UAAU,GACV,qBAAqB,GACrB,UAAU,CAAC;AAEjB,MAAM,MAAM,oBAAoB,GAC1B;IACI,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACrB,GACD;IACI,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACjB,GACD;IACI,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,GAAG,QAAQ,CAAC;CACnC,GACD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,KAAK,YAAY,GAAG;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,gCAAgC,GAAG;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACrB,CAAC;AAkRF,eAAO,MAAM,SAAS,gBAAiB,YAAY,KAAG,IAQrD,CAAC;AAEF,eAAO,MAAM,WAAW,iBACN,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,KACpD,IAGF,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,CAAC,oBACd,MAAM,OACnB;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,MACzB,CACA,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,gCAAgC,KACvC,OAAO,CAAC,CAAC,CAAC,KAChB,OAAO,CAAC,CAAC,CAsDX,CAAC;AAEF,eAAO,MAAM,UAAU,YACV,MAAM,UACP,cAAc,KACvB,OAAO,CAAC,IAAI,CAOd,CAAC;AAEF,eAAO,MAAM,4BAA4B,qBACnB,MAAM,UAChB,cAAc,KACvB,IAWF,CAAC;AAEF,eAAO,MAAM,QAAQ,WAAkB,cAAc,KAAG,OAAO,CAAC,IAAI,CAQnE,CAAC;AAEF,eAAO,MAAM,WAAW,YACX,MAAM,WACN,WAAW,GAAG,QAAQ,KAChC,IAEF,CAAC;AAEF,eAAO,MAAM,eAAe,QAAO,IAelC,CAAC;AAEF,eAAO,MAAM,uBAAuB;;;;;;;;;CASnC,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=MotherduckInstanceCache.integration.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MotherduckInstanceCache.integration.test.d.ts","sourceRoot":"","sources":["../../src/warehouseClients/MotherduckInstanceCache.integration.test.ts"],"names":[],"mappings":""}