ydb-qdrant 5.0.0 → 5.2.0
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.
- package/dist/config/env.d.ts +4 -0
- package/dist/config/env.js +4 -0
- package/dist/repositories/collectionsRepo.js +17 -8
- package/dist/repositories/collectionsRepo.one-table.js +62 -25
- package/dist/repositories/pointsRepo.one-table.js +17 -5
- package/dist/routes/points.js +30 -4
- package/dist/server.js +18 -1
- package/dist/utils/exit.d.ts +2 -0
- package/dist/utils/exit.js +12 -0
- package/dist/utils/retry.js +8 -1
- package/dist/ydb/client.d.ts +2 -2
- package/dist/ydb/client.js +3 -4
- package/dist/ydb/schema.js +8 -2
- package/package.json +10 -2
package/dist/config/env.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export declare const YDB_DATABASE: string;
|
|
|
4
4
|
export declare const PORT: number;
|
|
5
5
|
export declare const LOG_LEVEL: string;
|
|
6
6
|
export declare const GLOBAL_POINTS_AUTOMIGRATE_ENABLED: boolean;
|
|
7
|
+
export declare const USE_BATCH_DELETE_FOR_COLLECTIONS: boolean;
|
|
7
8
|
export declare enum SearchMode {
|
|
8
9
|
Exact = "exact",
|
|
9
10
|
Approximate = "approximate"
|
|
@@ -16,3 +17,6 @@ export declare const UPSERT_BATCH_SIZE: number;
|
|
|
16
17
|
export declare const SESSION_POOL_MIN_SIZE: number;
|
|
17
18
|
export declare const SESSION_POOL_MAX_SIZE: number;
|
|
18
19
|
export declare const SESSION_KEEPALIVE_PERIOD_MS: number;
|
|
20
|
+
export declare const STARTUP_PROBE_SESSION_TIMEOUT_MS: number;
|
|
21
|
+
export declare const UPSERT_OPERATION_TIMEOUT_MS: number;
|
|
22
|
+
export declare const SEARCH_OPERATION_TIMEOUT_MS: number;
|
package/dist/config/env.js
CHANGED
|
@@ -35,6 +35,7 @@ function parseBooleanEnv(value, defaultValue) {
|
|
|
35
35
|
return true;
|
|
36
36
|
}
|
|
37
37
|
export const GLOBAL_POINTS_AUTOMIGRATE_ENABLED = parseBooleanEnv(process.env.YDB_QDRANT_GLOBAL_POINTS_AUTOMIGRATE, false);
|
|
38
|
+
export const USE_BATCH_DELETE_FOR_COLLECTIONS = parseBooleanEnv(process.env.YDB_QDRANT_USE_BATCH_DELETE, true);
|
|
38
39
|
export var SearchMode;
|
|
39
40
|
(function (SearchMode) {
|
|
40
41
|
SearchMode["Exact"] = "exact";
|
|
@@ -67,3 +68,6 @@ const NORMALIZED_SESSION_POOL_MIN_SIZE = RAW_SESSION_POOL_MIN_SIZE > RAW_SESSION
|
|
|
67
68
|
export const SESSION_POOL_MIN_SIZE = NORMALIZED_SESSION_POOL_MIN_SIZE;
|
|
68
69
|
export const SESSION_POOL_MAX_SIZE = RAW_SESSION_POOL_MAX_SIZE;
|
|
69
70
|
export const SESSION_KEEPALIVE_PERIOD_MS = parseIntegerEnv(process.env.YDB_SESSION_KEEPALIVE_PERIOD_MS, 5000, { min: 1000, max: 60000 });
|
|
71
|
+
export const STARTUP_PROBE_SESSION_TIMEOUT_MS = parseIntegerEnv(process.env.YDB_QDRANT_STARTUP_PROBE_SESSION_TIMEOUT_MS, 5000, { min: 1000 });
|
|
72
|
+
export const UPSERT_OPERATION_TIMEOUT_MS = parseIntegerEnv(process.env.YDB_QDRANT_UPSERT_TIMEOUT_MS, 5000, { min: 1000 });
|
|
73
|
+
export const SEARCH_OPERATION_TIMEOUT_MS = parseIntegerEnv(process.env.YDB_QDRANT_SEARCH_TIMEOUT_MS, 10000, { min: 1000 });
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { TypedValues, withSession, createExecuteQuerySettings, withStartupProbeSession, createExecuteQuerySettingsWithTimeout, } from "../ydb/client.js";
|
|
2
|
+
import { STARTUP_PROBE_SESSION_TIMEOUT_MS } from "../config/env.js";
|
|
2
3
|
import { uidFor } from "../utils/tenant.js";
|
|
3
4
|
import { createCollectionOneTable, deleteCollectionOneTable, } from "./collectionsRepo.one-table.js";
|
|
5
|
+
import { withRetry, isTransientYdbError } from "../utils/retry.js";
|
|
4
6
|
export async function createCollection(metaKey, dim, distance, vectorType) {
|
|
5
7
|
await createCollectionOneTable(metaKey, dim, distance, vectorType);
|
|
6
8
|
}
|
|
@@ -35,15 +37,22 @@ export async function verifyCollectionsQueryCompilationForStartup() {
|
|
|
35
37
|
FROM qdr__collections
|
|
36
38
|
WHERE collection = $collection;
|
|
37
39
|
`;
|
|
38
|
-
await
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
await withRetry(async () => {
|
|
41
|
+
await withStartupProbeSession(async (s) => {
|
|
42
|
+
const settings = createExecuteQuerySettingsWithTimeout({
|
|
43
|
+
keepInCache: true,
|
|
44
|
+
idempotent: true,
|
|
45
|
+
timeoutMs: STARTUP_PROBE_SESSION_TIMEOUT_MS,
|
|
46
|
+
});
|
|
47
|
+
await s.executeQuery(qry, {
|
|
48
|
+
$collection: TypedValues.utf8(probeKey),
|
|
49
|
+
}, undefined, settings);
|
|
43
50
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
}, {
|
|
52
|
+
isTransient: isTransientYdbError,
|
|
53
|
+
maxRetries: 2,
|
|
54
|
+
baseDelayMs: 200,
|
|
55
|
+
context: { probe: "collections_startup_compilation" },
|
|
47
56
|
});
|
|
48
57
|
}
|
|
49
58
|
export async function deleteCollection(metaKey, uid) {
|
|
@@ -2,6 +2,7 @@ import { TypedValues, Types, withSession, createExecuteQuerySettings, } from "..
|
|
|
2
2
|
import { GLOBAL_POINTS_TABLE, ensureGlobalPointsTable } from "../ydb/schema.js";
|
|
3
3
|
import { upsertCollectionMeta } from "./collectionsRepo.shared.js";
|
|
4
4
|
import { withRetry, isTransientYdbError } from "../utils/retry.js";
|
|
5
|
+
import { USE_BATCH_DELETE_FOR_COLLECTIONS } from "../config/env.js";
|
|
5
6
|
const DELETE_COLLECTION_BATCH_SIZE = 10000;
|
|
6
7
|
function isOutOfBufferMemoryYdbError(error) {
|
|
7
8
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -63,32 +64,68 @@ export async function createCollectionOneTable(metaKey, dim, distance, vectorTyp
|
|
|
63
64
|
}
|
|
64
65
|
export async function deleteCollectionOneTable(metaKey, uid) {
|
|
65
66
|
await ensureGlobalPointsTable();
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (!isOutOfBufferMemoryYdbError(err)) {
|
|
79
|
-
throw err;
|
|
67
|
+
if (USE_BATCH_DELETE_FOR_COLLECTIONS) {
|
|
68
|
+
const batchDeletePointsYql = `
|
|
69
|
+
DECLARE $uid AS Utf8;
|
|
70
|
+
BATCH DELETE FROM ${GLOBAL_POINTS_TABLE}
|
|
71
|
+
WHERE uid = $uid;
|
|
72
|
+
`;
|
|
73
|
+
await withRetry(() => withSession(async (s) => {
|
|
74
|
+
const settings = createExecuteQuerySettings();
|
|
75
|
+
try {
|
|
76
|
+
await s.executeQuery(batchDeletePointsYql, {
|
|
77
|
+
$uid: TypedValues.utf8(uid),
|
|
78
|
+
}, undefined, settings);
|
|
80
79
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
},
|
|
91
|
-
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (!isOutOfBufferMemoryYdbError(err)) {
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
// BATCH DELETE already deletes in chunks per partition, but if YDB
|
|
85
|
+
// still reports an out-of-buffer-memory condition, fall back to
|
|
86
|
+
// the same per-uid chunked deletion strategy as the legacy path.
|
|
87
|
+
await deletePointsForUidInChunks(s, uid);
|
|
88
|
+
}
|
|
89
|
+
}), {
|
|
90
|
+
isTransient: isTransientYdbError,
|
|
91
|
+
context: {
|
|
92
|
+
operation: "deleteCollectionOneTable",
|
|
93
|
+
tableName: GLOBAL_POINTS_TABLE,
|
|
94
|
+
metaKey,
|
|
95
|
+
uid,
|
|
96
|
+
mode: "batch_delete",
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const deletePointsYql = `
|
|
102
|
+
DECLARE $uid AS Utf8;
|
|
103
|
+
DELETE FROM ${GLOBAL_POINTS_TABLE} WHERE uid = $uid;
|
|
104
|
+
`;
|
|
105
|
+
await withRetry(() => withSession(async (s) => {
|
|
106
|
+
const settings = createExecuteQuerySettings();
|
|
107
|
+
try {
|
|
108
|
+
await s.executeQuery(deletePointsYql, {
|
|
109
|
+
$uid: TypedValues.utf8(uid),
|
|
110
|
+
}, undefined, settings);
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
if (!isOutOfBufferMemoryYdbError(err)) {
|
|
114
|
+
throw err;
|
|
115
|
+
}
|
|
116
|
+
await deletePointsForUidInChunks(s, uid);
|
|
117
|
+
}
|
|
118
|
+
}), {
|
|
119
|
+
isTransient: isTransientYdbError,
|
|
120
|
+
context: {
|
|
121
|
+
operation: "deleteCollectionOneTable",
|
|
122
|
+
tableName: GLOBAL_POINTS_TABLE,
|
|
123
|
+
metaKey,
|
|
124
|
+
uid,
|
|
125
|
+
mode: "legacy_chunked",
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}
|
|
92
129
|
const delMeta = `
|
|
93
130
|
DECLARE $collection AS Utf8;
|
|
94
131
|
DELETE FROM qdr__collections WHERE collection = $collection;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { TypedValues, Types, withSession, createExecuteQuerySettings, } from "../ydb/client.js";
|
|
1
|
+
import { TypedValues, Types, withSession, createExecuteQuerySettings, createExecuteQuerySettingsWithTimeout, } from "../ydb/client.js";
|
|
2
2
|
import { buildVectorParam, buildVectorBinaryParams } from "../ydb/helpers.js";
|
|
3
3
|
import { mapDistanceToKnnFn, mapDistanceToBitKnnFn, } from "../utils/distance.js";
|
|
4
4
|
import { withRetry, isTransientYdbError } from "../utils/retry.js";
|
|
5
5
|
import { UPSERT_BATCH_SIZE } from "../ydb/schema.js";
|
|
6
|
-
import { CLIENT_SIDE_SERIALIZATION_ENABLED, SearchMode, } from "../config/env.js";
|
|
6
|
+
import { CLIENT_SIDE_SERIALIZATION_ENABLED, SearchMode, UPSERT_OPERATION_TIMEOUT_MS, SEARCH_OPERATION_TIMEOUT_MS, } from "../config/env.js";
|
|
7
7
|
import { logger } from "../logging/logger.js";
|
|
8
8
|
export async function upsertPointsOneTable(tableName, points, dimension, uid) {
|
|
9
9
|
for (const p of points) {
|
|
@@ -24,7 +24,11 @@ export async function upsertPointsOneTable(tableName, points, dimension, uid) {
|
|
|
24
24
|
}
|
|
25
25
|
let upserted = 0;
|
|
26
26
|
await withSession(async (s) => {
|
|
27
|
-
const settings =
|
|
27
|
+
const settings = createExecuteQuerySettingsWithTimeout({
|
|
28
|
+
keepInCache: true,
|
|
29
|
+
idempotent: true,
|
|
30
|
+
timeoutMs: UPSERT_OPERATION_TIMEOUT_MS,
|
|
31
|
+
});
|
|
28
32
|
for (let i = 0; i < points.length; i += UPSERT_BATCH_SIZE) {
|
|
29
33
|
const batch = points.slice(i, i + UPSERT_BATCH_SIZE);
|
|
30
34
|
let ddl;
|
|
@@ -192,7 +196,11 @@ async function searchPointsOneTableExact(tableName, queryVector, top, withPayloa
|
|
|
192
196
|
vectorPreview: queryVector.slice(0, 3),
|
|
193
197
|
},
|
|
194
198
|
}, "one_table search (exact): executing YQL");
|
|
195
|
-
const settings =
|
|
199
|
+
const settings = createExecuteQuerySettingsWithTimeout({
|
|
200
|
+
keepInCache: true,
|
|
201
|
+
idempotent: true,
|
|
202
|
+
timeoutMs: SEARCH_OPERATION_TIMEOUT_MS,
|
|
203
|
+
});
|
|
196
204
|
const rs = await s.executeQuery(yql, params, undefined, settings);
|
|
197
205
|
const rowset = rs.resultSets?.[0];
|
|
198
206
|
const rows = (rowset?.rows ?? []);
|
|
@@ -334,7 +342,11 @@ async function searchPointsOneTableApproximate(tableName, queryVector, top, with
|
|
|
334
342
|
},
|
|
335
343
|
}, "one_table search (approximate): executing YQL");
|
|
336
344
|
}
|
|
337
|
-
const settings =
|
|
345
|
+
const settings = createExecuteQuerySettingsWithTimeout({
|
|
346
|
+
keepInCache: true,
|
|
347
|
+
idempotent: true,
|
|
348
|
+
timeoutMs: SEARCH_OPERATION_TIMEOUT_MS,
|
|
349
|
+
});
|
|
338
350
|
const rs = await s.executeQuery(yql, params, undefined, settings);
|
|
339
351
|
const rowset = rs.resultSets?.[0];
|
|
340
352
|
const rows = (rowset?.rows ?? []);
|
package/dist/routes/points.js
CHANGED
|
@@ -2,6 +2,8 @@ import { Router } from "express";
|
|
|
2
2
|
import { upsertPoints, searchPoints, queryPoints, deletePoints, } from "../services/PointsService.js";
|
|
3
3
|
import { QdrantServiceError } from "../services/errors.js";
|
|
4
4
|
import { logger } from "../logging/logger.js";
|
|
5
|
+
import { isCompilationTimeoutError } from "../ydb/client.js";
|
|
6
|
+
import { scheduleExit } from "../utils/exit.js";
|
|
5
7
|
export const pointsRouter = Router();
|
|
6
8
|
// Qdrant-compatible: PUT /collections/:collection/points (upsert)
|
|
7
9
|
pointsRouter.put("/:collection/points", async (req, res) => {
|
|
@@ -18,8 +20,14 @@ pointsRouter.put("/:collection/points", async (req, res) => {
|
|
|
18
20
|
if (err instanceof QdrantServiceError) {
|
|
19
21
|
return res.status(err.statusCode).json(err.payload);
|
|
20
22
|
}
|
|
21
|
-
logger.error({ err }, "upsert points (PUT) failed");
|
|
22
23
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
24
|
+
if (isCompilationTimeoutError(err)) {
|
|
25
|
+
logger.error({ err }, "YDB compilation error during upsert points (PUT); scheduling process exit");
|
|
26
|
+
res.status(500).json({ status: "error", error: errorMessage });
|
|
27
|
+
scheduleExit(1);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
logger.error({ err }, "upsert points (PUT) failed");
|
|
23
31
|
res.status(500).json({ status: "error", error: errorMessage });
|
|
24
32
|
}
|
|
25
33
|
});
|
|
@@ -37,8 +45,14 @@ pointsRouter.post("/:collection/points/upsert", async (req, res) => {
|
|
|
37
45
|
if (err instanceof QdrantServiceError) {
|
|
38
46
|
return res.status(err.statusCode).json(err.payload);
|
|
39
47
|
}
|
|
40
|
-
logger.error({ err }, "upsert points failed");
|
|
41
48
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
49
|
+
if (isCompilationTimeoutError(err)) {
|
|
50
|
+
logger.error({ err }, "YDB compilation error during upsert points; scheduling process exit");
|
|
51
|
+
res.status(500).json({ status: "error", error: errorMessage });
|
|
52
|
+
scheduleExit(1);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
logger.error({ err }, "upsert points failed");
|
|
42
56
|
res.status(500).json({ status: "error", error: errorMessage });
|
|
43
57
|
}
|
|
44
58
|
});
|
|
@@ -56,8 +70,14 @@ pointsRouter.post("/:collection/points/search", async (req, res) => {
|
|
|
56
70
|
if (err instanceof QdrantServiceError) {
|
|
57
71
|
return res.status(err.statusCode).json(err.payload);
|
|
58
72
|
}
|
|
59
|
-
logger.error({ err }, "search points failed");
|
|
60
73
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
74
|
+
if (isCompilationTimeoutError(err)) {
|
|
75
|
+
logger.error({ err }, "YDB compilation error during search points; scheduling process exit");
|
|
76
|
+
res.status(500).json({ status: "error", error: errorMessage });
|
|
77
|
+
scheduleExit(1);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
logger.error({ err }, "search points failed");
|
|
61
81
|
res.status(500).json({ status: "error", error: errorMessage });
|
|
62
82
|
}
|
|
63
83
|
});
|
|
@@ -76,8 +96,14 @@ pointsRouter.post("/:collection/points/query", async (req, res) => {
|
|
|
76
96
|
if (err instanceof QdrantServiceError) {
|
|
77
97
|
return res.status(err.statusCode).json(err.payload);
|
|
78
98
|
}
|
|
79
|
-
logger.error({ err }, "search points (query) failed");
|
|
80
99
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
100
|
+
if (isCompilationTimeoutError(err)) {
|
|
101
|
+
logger.error({ err }, "YDB compilation error during search points (query); scheduling process exit");
|
|
102
|
+
res.status(500).json({ status: "error", error: errorMessage });
|
|
103
|
+
scheduleExit(1);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
logger.error({ err }, "search points (query) failed");
|
|
81
107
|
res.status(500).json({ status: "error", error: errorMessage });
|
|
82
108
|
}
|
|
83
109
|
});
|
package/dist/server.js
CHANGED
|
@@ -2,11 +2,28 @@ import express from "express";
|
|
|
2
2
|
import { collectionsRouter } from "./routes/collections.js";
|
|
3
3
|
import { pointsRouter } from "./routes/points.js";
|
|
4
4
|
import { requestLogger } from "./middleware/requestLogger.js";
|
|
5
|
-
import { isYdbAvailable } from "./ydb/client.js";
|
|
5
|
+
import { isYdbAvailable, isCompilationTimeoutError } from "./ydb/client.js";
|
|
6
|
+
import { verifyCollectionsQueryCompilationForStartup } from "./repositories/collectionsRepo.js";
|
|
7
|
+
import { logger } from "./logging/logger.js";
|
|
8
|
+
import { scheduleExit } from "./utils/exit.js";
|
|
6
9
|
export async function healthHandler(_req, res) {
|
|
7
10
|
const ok = await isYdbAvailable();
|
|
8
11
|
if (!ok) {
|
|
12
|
+
logger.error("YDB unavailable during health check; scheduling process exit");
|
|
9
13
|
res.status(503).json({ status: "error", error: "YDB unavailable" });
|
|
14
|
+
scheduleExit(1);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
await verifyCollectionsQueryCompilationForStartup();
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
const isTimeout = isCompilationTimeoutError(err);
|
|
22
|
+
logger.error({ err }, isTimeout
|
|
23
|
+
? "YDB compilation timeout during health probe; scheduling process exit"
|
|
24
|
+
: "YDB health probe failed; scheduling process exit");
|
|
25
|
+
res.status(503).json({ status: "error", error: "YDB health probe failed" });
|
|
26
|
+
scheduleExit(1);
|
|
10
27
|
return;
|
|
11
28
|
}
|
|
12
29
|
res.json({ status: "ok" });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
let exitFn = (code) => {
|
|
2
|
+
// Use process.exit in production; this will be overridden in tests.
|
|
3
|
+
process.exit(code);
|
|
4
|
+
};
|
|
5
|
+
export function scheduleExit(code) {
|
|
6
|
+
// Schedule exit on the next tick so HTTP responses can be flushed first.
|
|
7
|
+
setImmediate(() => exitFn(code));
|
|
8
|
+
}
|
|
9
|
+
// Test-only: allow overriding the underlying exit behavior.
|
|
10
|
+
export function __setExitFnForTests(fn) {
|
|
11
|
+
exitFn = fn;
|
|
12
|
+
}
|
package/dist/utils/retry.js
CHANGED
|
@@ -32,7 +32,14 @@ export async function withRetry(fn, options = {}) {
|
|
|
32
32
|
throw e;
|
|
33
33
|
}
|
|
34
34
|
const backoffMs = Math.floor(baseDelayMs * Math.pow(2, attempt) + Math.random() * 100);
|
|
35
|
-
logger.warn({
|
|
35
|
+
logger.warn({
|
|
36
|
+
...context,
|
|
37
|
+
attempt,
|
|
38
|
+
backoffMs,
|
|
39
|
+
err: e instanceof Error
|
|
40
|
+
? e
|
|
41
|
+
: new Error(typeof e === "string" ? e : JSON.stringify(e)),
|
|
42
|
+
}, "operation aborted due to transient error; retrying");
|
|
36
43
|
await new Promise((r) => setTimeout(r, backoffMs));
|
|
37
44
|
attempt += 1;
|
|
38
45
|
}
|
package/dist/ydb/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Session, IAuthService, ExecuteQuerySettings as YdbExecuteQuerySettings } from "ydb-sdk";
|
|
2
|
-
declare const Types: typeof import("ydb-sdk").Types, TypedValues: typeof import("ydb-sdk").TypedValues, TableDescription: typeof import("ydb-sdk").TableDescription, Column: typeof import("ydb-sdk").Column, ExecuteQuerySettings: typeof YdbExecuteQuerySettings;
|
|
3
|
-
export { Types, TypedValues, TableDescription, Column, ExecuteQuerySettings };
|
|
2
|
+
declare const Types: typeof import("ydb-sdk").Types, TypedValues: typeof import("ydb-sdk").TypedValues, TableDescription: typeof import("ydb-sdk").TableDescription, Column: typeof import("ydb-sdk").Column, ExecuteQuerySettings: typeof YdbExecuteQuerySettings, Ydb: typeof import("ydb-sdk-proto").Ydb;
|
|
3
|
+
export { Types, TypedValues, TableDescription, Column, ExecuteQuerySettings, Ydb, };
|
|
4
4
|
export declare function createExecuteQuerySettings(options?: {
|
|
5
5
|
keepInCache?: boolean;
|
|
6
6
|
idempotent?: boolean;
|
package/dist/ydb/client.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createRequire } from "module";
|
|
2
|
-
import { YDB_DATABASE, YDB_ENDPOINT, SESSION_POOL_MIN_SIZE, SESSION_POOL_MAX_SIZE, SESSION_KEEPALIVE_PERIOD_MS, } from "../config/env.js";
|
|
2
|
+
import { YDB_DATABASE, YDB_ENDPOINT, SESSION_POOL_MIN_SIZE, SESSION_POOL_MAX_SIZE, SESSION_KEEPALIVE_PERIOD_MS, STARTUP_PROBE_SESSION_TIMEOUT_MS, } from "../config/env.js";
|
|
3
3
|
import { logger } from "../logging/logger.js";
|
|
4
4
|
const require = createRequire(import.meta.url);
|
|
5
|
-
const { Driver, getCredentialsFromEnv, Types, TypedValues, TableDescription, Column, ExecuteQuerySettings, OperationParams, } = require("ydb-sdk");
|
|
6
|
-
export { Types, TypedValues, TableDescription, Column, ExecuteQuerySettings };
|
|
5
|
+
const { Driver, getCredentialsFromEnv, Types, TypedValues, TableDescription, Column, ExecuteQuerySettings, OperationParams, Ydb, } = require("ydb-sdk");
|
|
6
|
+
export { Types, TypedValues, TableDescription, Column, ExecuteQuerySettings, Ydb, };
|
|
7
7
|
export function createExecuteQuerySettings(options) {
|
|
8
8
|
const { keepInCache = true, idempotent = true } = options ?? {};
|
|
9
9
|
const settings = new ExecuteQuerySettings();
|
|
@@ -30,7 +30,6 @@ const DRIVER_READY_TIMEOUT_MS = 15000;
|
|
|
30
30
|
const TABLE_SESSION_TIMEOUT_MS = 20000;
|
|
31
31
|
const YDB_HEALTHCHECK_READY_TIMEOUT_MS = 5000;
|
|
32
32
|
const DRIVER_REFRESH_COOLDOWN_MS = 30000;
|
|
33
|
-
const STARTUP_PROBE_SESSION_TIMEOUT_MS = 3000;
|
|
34
33
|
let overrideConfig;
|
|
35
34
|
let driver;
|
|
36
35
|
let lastDriverRefreshAt = 0;
|
package/dist/ydb/schema.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { withSession, TableDescription, Column, Types } from "./client.js";
|
|
1
|
+
import { withSession, TableDescription, Column, Types, Ydb } from "./client.js";
|
|
2
2
|
import { logger } from "../logging/logger.js";
|
|
3
3
|
import { GLOBAL_POINTS_AUTOMIGRATE_ENABLED } from "../config/env.js";
|
|
4
4
|
export const GLOBAL_POINTS_TABLE = "qdrant_all_points";
|
|
@@ -41,10 +41,16 @@ export async function ensureGlobalPointsTable() {
|
|
|
41
41
|
tableDescription = await s.describeTable(GLOBAL_POINTS_TABLE);
|
|
42
42
|
}
|
|
43
43
|
catch {
|
|
44
|
-
// Table doesn't exist, create it with all columns using the new schema
|
|
44
|
+
// Table doesn't exist, create it with all columns using the new schema and
|
|
45
|
+
// auto-partitioning enabled.
|
|
45
46
|
const desc = new TableDescription()
|
|
46
47
|
.withColumns(new Column("uid", Types.UTF8), new Column("point_id", Types.UTF8), new Column("embedding", Types.BYTES), new Column("embedding_quantized", Types.BYTES), new Column("payload", Types.JSON_DOCUMENT))
|
|
47
48
|
.withPrimaryKeys("uid", "point_id");
|
|
49
|
+
desc.withPartitioningSettings({
|
|
50
|
+
partitioningByLoad: Ydb.FeatureFlag.Status.ENABLED,
|
|
51
|
+
partitioningBySize: Ydb.FeatureFlag.Status.ENABLED,
|
|
52
|
+
partitionSizeMb: 100,
|
|
53
|
+
});
|
|
48
54
|
await s.createTable(GLOBAL_POINTS_TABLE, desc);
|
|
49
55
|
globalPointsTableReady = true;
|
|
50
56
|
logger.info(`created global points table ${GLOBAL_POINTS_TABLE}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ydb-qdrant",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"main": "dist/package/api.js",
|
|
5
5
|
"types": "dist/package/api.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -50,6 +50,14 @@
|
|
|
50
50
|
"author": "",
|
|
51
51
|
"license": "Apache-2.0",
|
|
52
52
|
"description": "Qdrant-compatible Node.js/TypeScript API that stores/searches embeddings in YDB using a global one-table layout with exact and approximate KNN search over serialized vectors.",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/astandrik/ydb-qdrant.git"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/astandrik/ydb-qdrant#readme",
|
|
58
|
+
"bugs": {
|
|
59
|
+
"url": "https://github.com/astandrik/ydb-qdrant/issues"
|
|
60
|
+
},
|
|
53
61
|
"type": "module",
|
|
54
62
|
"publishConfig": {
|
|
55
63
|
"access": "public"
|
|
@@ -83,4 +91,4 @@
|
|
|
83
91
|
"typescript-eslint": "^8.47.0",
|
|
84
92
|
"vitest": "^4.0.12"
|
|
85
93
|
}
|
|
86
|
-
}
|
|
94
|
+
}
|