@mastra/pg 0.2.6-alpha.2 → 0.2.6-alpha.3

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.
@@ -1,23 +1,23 @@
1
1
 
2
- > @mastra/pg@0.2.6-alpha.2 build /home/runner/work/mastra/mastra/stores/pg
2
+ > @mastra/pg@0.2.6-alpha.3 build /home/runner/work/mastra/mastra/stores/pg
3
3
  > tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
4
4
 
5
5
  CLI Building entry: src/index.ts
6
6
  CLI Using tsconfig: tsconfig.json
7
7
  CLI tsup v8.4.0
8
8
  TSC Build start
9
- TSC ⚡️ Build success in 10254ms
9
+ TSC ⚡️ Build success in 10734ms
10
10
  DTS Build start
11
11
  CLI Target: es2022
12
12
  Analysis will use the bundled TypeScript version 5.8.2
13
13
  Writing package typings: /home/runner/work/mastra/mastra/stores/pg/dist/_tsup-dts-rollup.d.ts
14
14
  Analysis will use the bundled TypeScript version 5.8.2
15
15
  Writing package typings: /home/runner/work/mastra/mastra/stores/pg/dist/_tsup-dts-rollup.d.cts
16
- DTS ⚡️ Build success in 12251ms
16
+ DTS ⚡️ Build success in 11829ms
17
17
  CLI Cleaning output folder
18
18
  ESM Build start
19
19
  CJS Build start
20
- ESM dist/index.js 34.40 KB
21
- ESM ⚡️ Build success in 1257ms
22
- CJS dist/index.cjs 34.80 KB
23
- CJS ⚡️ Build success in 1259ms
20
+ CJS dist/index.cjs 36.78 KB
21
+ CJS ⚡️ Build success in 1386ms
22
+ ESM dist/index.js 36.37 KB
23
+ ESM ⚡️ Build success in 1387ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @mastra/pg
2
2
 
3
+ ## 0.2.6-alpha.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 7172059: Update PG Vector to use handle concurrent createIndex
8
+ - Updated dependencies [b3b34f5]
9
+ - Updated dependencies [a4686e8]
10
+ - @mastra/core@0.7.0-alpha.3
11
+
3
12
  ## 0.2.6-alpha.2
4
13
 
5
14
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var crypto$1 = require('crypto');
3
4
  var vector = require('@mastra/core/vector');
4
5
  var pg = require('pg');
5
6
  var filter = require('@mastra/core/vector/filter');
@@ -420,8 +421,28 @@ var PgVector = class extends vector.MastraVector {
420
421
  if (!extensionCheck.rows[0].exists) {
421
422
  throw new Error("PostgreSQL vector extension is not available. Please install it first.");
422
423
  }
423
- await client.query("CREATE EXTENSION IF NOT EXISTS vector");
424
- await client.query(`
424
+ const hash = crypto$1.createHash("sha256").update(indexName).digest("hex");
425
+ const lockId = BigInt("0x" + hash.slice(0, 8)) % BigInt(2 ** 31);
426
+ const acquired = await client.query("SELECT pg_try_advisory_lock($1)", [lockId]);
427
+ if (!acquired.rows[0].pg_try_advisory_lock) {
428
+ const exists = await client.query(
429
+ `
430
+ SELECT 1 FROM pg_class c
431
+ JOIN pg_namespace n ON n.oid = c.relnamespace
432
+ WHERE c.relname = $1
433
+ AND n.nspname = 'public'
434
+ `,
435
+ [indexName]
436
+ );
437
+ if (exists.rows.length > 0) {
438
+ console.log(`Table ${indexName} already exists, skipping creation`);
439
+ return;
440
+ }
441
+ await client.query("SELECT pg_advisory_lock($1)", [lockId]);
442
+ }
443
+ try {
444
+ await client.query("CREATE EXTENSION IF NOT EXISTS vector");
445
+ await client.query(`
425
446
  CREATE TABLE IF NOT EXISTS ${indexName} (
426
447
  id SERIAL PRIMARY KEY,
427
448
  vector_id TEXT UNIQUE NOT NULL,
@@ -429,6 +450,9 @@ var PgVector = class extends vector.MastraVector {
429
450
  metadata JSONB DEFAULT '{}'::jsonb
430
451
  );
431
452
  `);
453
+ } finally {
454
+ await client.query("SELECT pg_advisory_unlock($1)", [lockId]);
455
+ }
432
456
  if (buildIndex) {
433
457
  await this.buildIndex({ indexName, metric, indexConfig });
434
458
  }
@@ -453,15 +477,39 @@ var PgVector = class extends vector.MastraVector {
453
477
  const { indexName, metric = "cosine", indexConfig } = params;
454
478
  const client = await this.pool.connect();
455
479
  try {
456
- await client.query(`DROP INDEX IF EXISTS ${indexName}_vector_idx`);
457
- if (indexConfig.type === "flat") return;
458
- const metricOp = metric === "cosine" ? "vector_cosine_ops" : metric === "euclidean" ? "vector_l2_ops" : "vector_ip_ops";
459
- let indexSQL;
460
- if (indexConfig.type === "hnsw") {
461
- const m = indexConfig.hnsw?.m ?? 8;
462
- const efConstruction = indexConfig.hnsw?.efConstruction ?? 32;
463
- indexSQL = `
464
- CREATE INDEX ${indexName}_vector_idx
480
+ const hash = crypto$1.createHash("sha256").update("build:" + indexName).digest("hex");
481
+ const lockId = BigInt("0x" + hash.slice(0, 8)) % BigInt(2 ** 31);
482
+ const acquired = await client.query("SELECT pg_try_advisory_lock($1)", [lockId]);
483
+ if (!acquired.rows[0].pg_try_advisory_lock) {
484
+ const exists = await client.query(
485
+ `
486
+ SELECT 1 FROM pg_class c
487
+ JOIN pg_namespace n ON n.oid = c.relnamespace
488
+ WHERE c.relname = $1
489
+ AND n.nspname = 'public'
490
+ `,
491
+ [`${indexName}_vector_idx`]
492
+ );
493
+ if (exists.rows.length > 0) {
494
+ console.log(`Index ${indexName}_vector_idx already exists, skipping creation`);
495
+ this.indexCache.delete(indexName);
496
+ return;
497
+ }
498
+ await client.query("SELECT pg_advisory_lock($1)", [lockId]);
499
+ }
500
+ try {
501
+ await client.query(`DROP INDEX IF EXISTS ${indexName}_vector_idx`);
502
+ if (indexConfig.type === "flat") {
503
+ this.indexCache.delete(indexName);
504
+ return;
505
+ }
506
+ const metricOp = metric === "cosine" ? "vector_cosine_ops" : metric === "euclidean" ? "vector_l2_ops" : "vector_ip_ops";
507
+ let indexSQL;
508
+ if (indexConfig.type === "hnsw") {
509
+ const m = indexConfig.hnsw?.m ?? 8;
510
+ const efConstruction = indexConfig.hnsw?.efConstruction ?? 32;
511
+ indexSQL = `
512
+ CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
465
513
  ON ${indexName}
466
514
  USING hnsw (embedding ${metricOp})
467
515
  WITH (
@@ -469,23 +517,26 @@ var PgVector = class extends vector.MastraVector {
469
517
  ef_construction = ${efConstruction}
470
518
  )
471
519
  `;
472
- } else {
473
- let lists;
474
- if (indexConfig.ivf?.lists) {
475
- lists = indexConfig.ivf.lists;
476
520
  } else {
477
- const size = (await client.query(`SELECT COUNT(*) FROM ${indexName}`)).rows[0].count;
478
- lists = Math.max(100, Math.min(4e3, Math.floor(Math.sqrt(size) * 2)));
479
- }
480
- indexSQL = `
481
- CREATE INDEX ${indexName}_vector_idx
521
+ let lists;
522
+ if (indexConfig.ivf?.lists) {
523
+ lists = indexConfig.ivf.lists;
524
+ } else {
525
+ const size = (await client.query(`SELECT COUNT(*) FROM ${indexName}`)).rows[0].count;
526
+ lists = Math.max(100, Math.min(4e3, Math.floor(Math.sqrt(size) * 2)));
527
+ }
528
+ indexSQL = `
529
+ CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
482
530
  ON ${indexName}
483
531
  USING ivfflat (embedding ${metricOp})
484
532
  WITH (lists = ${lists});
485
533
  `;
534
+ }
535
+ await client.query(indexSQL);
536
+ this.indexCache.delete(indexName);
537
+ } finally {
538
+ await client.query("SELECT pg_advisory_unlock($1)", [lockId]);
486
539
  }
487
- await client.query(indexSQL);
488
- this.indexCache.delete(indexName);
489
540
  } finally {
490
541
  client.release();
491
542
  }
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createHash } from 'crypto';
1
2
  import { MastraVector } from '@mastra/core/vector';
2
3
  import pg from 'pg';
3
4
  import { BaseFilterTranslator } from '@mastra/core/vector/filter';
@@ -413,8 +414,28 @@ var PgVector = class extends MastraVector {
413
414
  if (!extensionCheck.rows[0].exists) {
414
415
  throw new Error("PostgreSQL vector extension is not available. Please install it first.");
415
416
  }
416
- await client.query("CREATE EXTENSION IF NOT EXISTS vector");
417
- await client.query(`
417
+ const hash = createHash("sha256").update(indexName).digest("hex");
418
+ const lockId = BigInt("0x" + hash.slice(0, 8)) % BigInt(2 ** 31);
419
+ const acquired = await client.query("SELECT pg_try_advisory_lock($1)", [lockId]);
420
+ if (!acquired.rows[0].pg_try_advisory_lock) {
421
+ const exists = await client.query(
422
+ `
423
+ SELECT 1 FROM pg_class c
424
+ JOIN pg_namespace n ON n.oid = c.relnamespace
425
+ WHERE c.relname = $1
426
+ AND n.nspname = 'public'
427
+ `,
428
+ [indexName]
429
+ );
430
+ if (exists.rows.length > 0) {
431
+ console.log(`Table ${indexName} already exists, skipping creation`);
432
+ return;
433
+ }
434
+ await client.query("SELECT pg_advisory_lock($1)", [lockId]);
435
+ }
436
+ try {
437
+ await client.query("CREATE EXTENSION IF NOT EXISTS vector");
438
+ await client.query(`
418
439
  CREATE TABLE IF NOT EXISTS ${indexName} (
419
440
  id SERIAL PRIMARY KEY,
420
441
  vector_id TEXT UNIQUE NOT NULL,
@@ -422,6 +443,9 @@ var PgVector = class extends MastraVector {
422
443
  metadata JSONB DEFAULT '{}'::jsonb
423
444
  );
424
445
  `);
446
+ } finally {
447
+ await client.query("SELECT pg_advisory_unlock($1)", [lockId]);
448
+ }
425
449
  if (buildIndex) {
426
450
  await this.buildIndex({ indexName, metric, indexConfig });
427
451
  }
@@ -446,15 +470,39 @@ var PgVector = class extends MastraVector {
446
470
  const { indexName, metric = "cosine", indexConfig } = params;
447
471
  const client = await this.pool.connect();
448
472
  try {
449
- await client.query(`DROP INDEX IF EXISTS ${indexName}_vector_idx`);
450
- if (indexConfig.type === "flat") return;
451
- const metricOp = metric === "cosine" ? "vector_cosine_ops" : metric === "euclidean" ? "vector_l2_ops" : "vector_ip_ops";
452
- let indexSQL;
453
- if (indexConfig.type === "hnsw") {
454
- const m = indexConfig.hnsw?.m ?? 8;
455
- const efConstruction = indexConfig.hnsw?.efConstruction ?? 32;
456
- indexSQL = `
457
- CREATE INDEX ${indexName}_vector_idx
473
+ const hash = createHash("sha256").update("build:" + indexName).digest("hex");
474
+ const lockId = BigInt("0x" + hash.slice(0, 8)) % BigInt(2 ** 31);
475
+ const acquired = await client.query("SELECT pg_try_advisory_lock($1)", [lockId]);
476
+ if (!acquired.rows[0].pg_try_advisory_lock) {
477
+ const exists = await client.query(
478
+ `
479
+ SELECT 1 FROM pg_class c
480
+ JOIN pg_namespace n ON n.oid = c.relnamespace
481
+ WHERE c.relname = $1
482
+ AND n.nspname = 'public'
483
+ `,
484
+ [`${indexName}_vector_idx`]
485
+ );
486
+ if (exists.rows.length > 0) {
487
+ console.log(`Index ${indexName}_vector_idx already exists, skipping creation`);
488
+ this.indexCache.delete(indexName);
489
+ return;
490
+ }
491
+ await client.query("SELECT pg_advisory_lock($1)", [lockId]);
492
+ }
493
+ try {
494
+ await client.query(`DROP INDEX IF EXISTS ${indexName}_vector_idx`);
495
+ if (indexConfig.type === "flat") {
496
+ this.indexCache.delete(indexName);
497
+ return;
498
+ }
499
+ const metricOp = metric === "cosine" ? "vector_cosine_ops" : metric === "euclidean" ? "vector_l2_ops" : "vector_ip_ops";
500
+ let indexSQL;
501
+ if (indexConfig.type === "hnsw") {
502
+ const m = indexConfig.hnsw?.m ?? 8;
503
+ const efConstruction = indexConfig.hnsw?.efConstruction ?? 32;
504
+ indexSQL = `
505
+ CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
458
506
  ON ${indexName}
459
507
  USING hnsw (embedding ${metricOp})
460
508
  WITH (
@@ -462,23 +510,26 @@ var PgVector = class extends MastraVector {
462
510
  ef_construction = ${efConstruction}
463
511
  )
464
512
  `;
465
- } else {
466
- let lists;
467
- if (indexConfig.ivf?.lists) {
468
- lists = indexConfig.ivf.lists;
469
513
  } else {
470
- const size = (await client.query(`SELECT COUNT(*) FROM ${indexName}`)).rows[0].count;
471
- lists = Math.max(100, Math.min(4e3, Math.floor(Math.sqrt(size) * 2)));
472
- }
473
- indexSQL = `
474
- CREATE INDEX ${indexName}_vector_idx
514
+ let lists;
515
+ if (indexConfig.ivf?.lists) {
516
+ lists = indexConfig.ivf.lists;
517
+ } else {
518
+ const size = (await client.query(`SELECT COUNT(*) FROM ${indexName}`)).rows[0].count;
519
+ lists = Math.max(100, Math.min(4e3, Math.floor(Math.sqrt(size) * 2)));
520
+ }
521
+ indexSQL = `
522
+ CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
475
523
  ON ${indexName}
476
524
  USING ivfflat (embedding ${metricOp})
477
525
  WITH (lists = ${lists});
478
526
  `;
527
+ }
528
+ await client.query(indexSQL);
529
+ this.indexCache.delete(indexName);
530
+ } finally {
531
+ await client.query("SELECT pg_advisory_unlock($1)", [lockId]);
479
532
  }
480
- await client.query(indexSQL);
481
- this.indexCache.delete(indexName);
482
533
  } finally {
483
534
  client.release();
484
535
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/pg",
3
- "version": "0.2.6-alpha.2",
3
+ "version": "0.2.6-alpha.3",
4
4
  "description": "Postgres provider for Mastra - includes both vector and db storage capabilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -21,7 +21,7 @@
21
21
  "dependencies": {
22
22
  "pg": "^8.13.3",
23
23
  "pg-promise": "^11.11.0",
24
- "@mastra/core": "^0.7.0-alpha.2"
24
+ "@mastra/core": "^0.7.0-alpha.3"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@microsoft/api-extractor": "^7.52.1",
@@ -1,7 +1,7 @@
1
+ import type { QueryResult } from '@mastra/core';
1
2
  import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest';
2
3
 
3
4
  import { PgVector } from '.';
4
- import { type QueryResult } from '@mastra/core';
5
5
 
6
6
  describe('PgVector', () => {
7
7
  let vectorDB: PgVector;
@@ -1812,4 +1812,47 @@ describe('PgVector', () => {
1812
1812
  await expect(vectorDB.buildIndex(indexName, 'cosine', { type: 'flat' })).resolves.not.toThrow();
1813
1813
  });
1814
1814
  });
1815
+
1816
+ describe('Concurrent Operations', () => {
1817
+ it('should handle concurrent index creation attempts', async () => {
1818
+ const indexName = 'concurrent_test_index';
1819
+ const dimension = 384;
1820
+
1821
+ // Create multiple promises trying to create the same index
1822
+ const promises = Array(5)
1823
+ .fill(null)
1824
+ .map(() => vectorDB.createIndex({ indexName, dimension }));
1825
+
1826
+ // All should resolve without error - subsequent attempts should be no-ops
1827
+ await expect(Promise.all(promises)).resolves.not.toThrow();
1828
+
1829
+ // Verify only one index was actually created
1830
+ const stats = await vectorDB.describeIndex(indexName);
1831
+ expect(stats.dimension).toBe(dimension);
1832
+
1833
+ await vectorDB.deleteIndex(indexName);
1834
+ });
1835
+
1836
+ it('should handle concurrent buildIndex attempts', async () => {
1837
+ const indexName = 'concurrent_build_test';
1838
+ await vectorDB.createIndex({ indexName, dimension: 384 });
1839
+
1840
+ const promises = Array(5)
1841
+ .fill(null)
1842
+ .map(() =>
1843
+ vectorDB.buildIndex({
1844
+ indexName,
1845
+ metric: 'cosine',
1846
+ indexConfig: { type: 'ivfflat', ivf: { lists: 100 } },
1847
+ }),
1848
+ );
1849
+
1850
+ await expect(Promise.all(promises)).resolves.not.toThrow();
1851
+
1852
+ const stats = await vectorDB.describeIndex(indexName);
1853
+ expect(stats.type).toBe('ivfflat');
1854
+
1855
+ await vectorDB.deleteIndex(indexName);
1856
+ });
1857
+ });
1815
1858
  });
@@ -1,3 +1,4 @@
1
+ import { createHash } from 'crypto';
1
2
  import { MastraVector } from '@mastra/core/vector';
2
3
  import type {
3
4
  IndexStats,
@@ -215,11 +216,39 @@ export class PgVector extends MastraVector {
215
216
  throw new Error('PostgreSQL vector extension is not available. Please install it first.');
216
217
  }
217
218
 
218
- // Try to create extension
219
- await client.query('CREATE EXTENSION IF NOT EXISTS vector');
219
+ // Get advisory lock using hash of index name
220
+ const hash = createHash('sha256').update(indexName).digest('hex');
221
+ const lockId = BigInt('0x' + hash.slice(0, 8)) % BigInt(2 ** 31); // Take first 8 chars and convert to number
222
+ const acquired = await client.query('SELECT pg_try_advisory_lock($1)', [lockId]);
223
+
224
+ if (!acquired.rows[0].pg_try_advisory_lock) {
225
+ // Check if table already exists
226
+ const exists = await client.query(
227
+ `
228
+ SELECT 1 FROM pg_class c
229
+ JOIN pg_namespace n ON n.oid = c.relnamespace
230
+ WHERE c.relname = $1
231
+ AND n.nspname = 'public'
232
+ `,
233
+ [indexName],
234
+ );
235
+
236
+ if (exists.rows.length > 0) {
237
+ // Table exists so return early
238
+ console.log(`Table ${indexName} already exists, skipping creation`);
239
+ return;
240
+ }
241
+
242
+ // Table doesn't exist, wait for lock
243
+ await client.query('SELECT pg_advisory_lock($1)', [lockId]);
244
+ }
220
245
 
221
- // Create the table with explicit schema
222
- await client.query(`
246
+ try {
247
+ // Try to create extension
248
+ await client.query('CREATE EXTENSION IF NOT EXISTS vector');
249
+
250
+ // Create the table with explicit schema
251
+ await client.query(`
223
252
  CREATE TABLE IF NOT EXISTS ${indexName} (
224
253
  id SERIAL PRIMARY KEY,
225
254
  vector_id TEXT UNIQUE NOT NULL,
@@ -227,6 +256,10 @@ export class PgVector extends MastraVector {
227
256
  metadata JSONB DEFAULT '{}'::jsonb
228
257
  );
229
258
  `);
259
+ } finally {
260
+ // Always release lock
261
+ await client.query('SELECT pg_advisory_unlock($1)', [lockId]);
262
+ }
230
263
 
231
264
  if (buildIndex) {
232
265
  await this.buildIndex({ indexName, metric, indexConfig });
@@ -260,20 +293,53 @@ export class PgVector extends MastraVector {
260
293
 
261
294
  const client = await this.pool.connect();
262
295
  try {
263
- await client.query(`DROP INDEX IF EXISTS ${indexName}_vector_idx`);
296
+ // Use a different hash prefix for buildIndex locks to avoid conflicts with createIndex
297
+ const hash = createHash('sha256')
298
+ .update('build:' + indexName)
299
+ .digest('hex');
300
+ const lockId = BigInt('0x' + hash.slice(0, 8)) % BigInt(2 ** 31);
301
+ const acquired = await client.query('SELECT pg_try_advisory_lock($1)', [lockId]);
302
+
303
+ if (!acquired.rows[0].pg_try_advisory_lock) {
304
+ // Check if index already exists
305
+ const exists = await client.query(
306
+ `
307
+ SELECT 1 FROM pg_class c
308
+ JOIN pg_namespace n ON n.oid = c.relnamespace
309
+ WHERE c.relname = $1
310
+ AND n.nspname = 'public'
311
+ `,
312
+ [`${indexName}_vector_idx`],
313
+ );
314
+
315
+ if (exists.rows.length > 0) {
316
+ console.log(`Index ${indexName}_vector_idx already exists, skipping creation`);
317
+ this.indexCache.delete(indexName); // Still clear cache since we checked
318
+ return;
319
+ }
320
+
321
+ // Index doesn't exist, wait for lock
322
+ await client.query('SELECT pg_advisory_lock($1)', [lockId]);
323
+ }
264
324
 
265
- if (indexConfig.type === 'flat') return;
325
+ try {
326
+ await client.query(`DROP INDEX IF EXISTS ${indexName}_vector_idx`);
327
+
328
+ if (indexConfig.type === 'flat') {
329
+ this.indexCache.delete(indexName);
330
+ return;
331
+ }
266
332
 
267
- const metricOp =
268
- metric === 'cosine' ? 'vector_cosine_ops' : metric === 'euclidean' ? 'vector_l2_ops' : 'vector_ip_ops';
333
+ const metricOp =
334
+ metric === 'cosine' ? 'vector_cosine_ops' : metric === 'euclidean' ? 'vector_l2_ops' : 'vector_ip_ops';
269
335
 
270
- let indexSQL: string;
271
- if (indexConfig.type === 'hnsw') {
272
- const m = indexConfig.hnsw?.m ?? 8;
273
- const efConstruction = indexConfig.hnsw?.efConstruction ?? 32;
336
+ let indexSQL: string;
337
+ if (indexConfig.type === 'hnsw') {
338
+ const m = indexConfig.hnsw?.m ?? 8;
339
+ const efConstruction = indexConfig.hnsw?.efConstruction ?? 32;
274
340
 
275
- indexSQL = `
276
- CREATE INDEX ${indexName}_vector_idx
341
+ indexSQL = `
342
+ CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
277
343
  ON ${indexName}
278
344
  USING hnsw (embedding ${metricOp})
279
345
  WITH (
@@ -281,24 +347,27 @@ export class PgVector extends MastraVector {
281
347
  ef_construction = ${efConstruction}
282
348
  )
283
349
  `;
284
- } else {
285
- let lists: number;
286
- if (indexConfig.ivf?.lists) {
287
- lists = indexConfig.ivf.lists;
288
350
  } else {
289
- const size = (await client.query(`SELECT COUNT(*) FROM ${indexName}`)).rows[0].count;
290
- lists = Math.max(100, Math.min(4000, Math.floor(Math.sqrt(size) * 2)));
291
- }
292
- indexSQL = `
293
- CREATE INDEX ${indexName}_vector_idx
351
+ let lists: number;
352
+ if (indexConfig.ivf?.lists) {
353
+ lists = indexConfig.ivf.lists;
354
+ } else {
355
+ const size = (await client.query(`SELECT COUNT(*) FROM ${indexName}`)).rows[0].count;
356
+ lists = Math.max(100, Math.min(4000, Math.floor(Math.sqrt(size) * 2)));
357
+ }
358
+ indexSQL = `
359
+ CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
294
360
  ON ${indexName}
295
361
  USING ivfflat (embedding ${metricOp})
296
362
  WITH (lists = ${lists});
297
363
  `;
298
- }
364
+ }
299
365
 
300
- await client.query(indexSQL);
301
- this.indexCache.delete(indexName);
366
+ await client.query(indexSQL);
367
+ this.indexCache.delete(indexName);
368
+ } finally {
369
+ await client.query('SELECT pg_advisory_unlock($1)', [lockId]);
370
+ }
302
371
  } finally {
303
372
  client.release();
304
373
  }