ydb-qdrant 4.7.1 → 4.7.2
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 +3 -0
- package/dist/config/env.js +9 -0
- package/dist/ydb/client.d.ts +12 -0
- package/dist/ydb/client.js +106 -4
- package/package.json +1 -1
package/dist/config/env.d.ts
CHANGED
|
@@ -19,3 +19,6 @@ export declare const SEARCH_MODE: SearchMode;
|
|
|
19
19
|
export declare const OVERFETCH_MULTIPLIER: number;
|
|
20
20
|
export declare const CLIENT_SIDE_SERIALIZATION_ENABLED: boolean;
|
|
21
21
|
export declare const UPSERT_BATCH_SIZE: number;
|
|
22
|
+
export declare const SESSION_POOL_MIN_SIZE: number;
|
|
23
|
+
export declare const SESSION_POOL_MAX_SIZE: number;
|
|
24
|
+
export declare const SESSION_KEEPALIVE_PERIOD_MS: number;
|
package/dist/config/env.js
CHANGED
|
@@ -78,3 +78,12 @@ export const SEARCH_MODE = resolveSearchModeEnv(COLLECTION_STORAGE_MODE);
|
|
|
78
78
|
export const OVERFETCH_MULTIPLIER = parseIntegerEnv(process.env.YDB_QDRANT_OVERFETCH_MULTIPLIER, 10, { min: 1 });
|
|
79
79
|
export const CLIENT_SIDE_SERIALIZATION_ENABLED = parseBooleanEnv(process.env.YDB_QDRANT_CLIENT_SIDE_SERIALIZATION_ENABLED, false);
|
|
80
80
|
export const UPSERT_BATCH_SIZE = parseIntegerEnv(process.env.YDB_QDRANT_UPSERT_BATCH_SIZE, 100, { min: 1 });
|
|
81
|
+
// Session pool configuration
|
|
82
|
+
const RAW_SESSION_POOL_MIN_SIZE = parseIntegerEnv(process.env.YDB_SESSION_POOL_MIN_SIZE, 5, { min: 1, max: 500 });
|
|
83
|
+
const RAW_SESSION_POOL_MAX_SIZE = parseIntegerEnv(process.env.YDB_SESSION_POOL_MAX_SIZE, 100, { min: 1, max: 500 });
|
|
84
|
+
const NORMALIZED_SESSION_POOL_MIN_SIZE = RAW_SESSION_POOL_MIN_SIZE > RAW_SESSION_POOL_MAX_SIZE
|
|
85
|
+
? RAW_SESSION_POOL_MAX_SIZE
|
|
86
|
+
: RAW_SESSION_POOL_MIN_SIZE;
|
|
87
|
+
export const SESSION_POOL_MIN_SIZE = NORMALIZED_SESSION_POOL_MIN_SIZE;
|
|
88
|
+
export const SESSION_POOL_MAX_SIZE = RAW_SESSION_POOL_MAX_SIZE;
|
|
89
|
+
export const SESSION_KEEPALIVE_PERIOD_MS = parseIntegerEnv(process.env.YDB_SESSION_KEEPALIVE_PERIOD_MS, 5000, { min: 1000, max: 60000 });
|
package/dist/ydb/client.d.ts
CHANGED
|
@@ -8,7 +8,19 @@ type DriverConfig = {
|
|
|
8
8
|
authService?: IAuthService;
|
|
9
9
|
};
|
|
10
10
|
export declare function __setDriverForTests(fake: unknown): void;
|
|
11
|
+
export declare function __setDriverFactoryForTests(factory: ((config: unknown) => unknown) | undefined): void;
|
|
12
|
+
export declare function __resetRefreshStateForTests(): void;
|
|
11
13
|
export declare function configureDriver(config: DriverConfig): void;
|
|
12
14
|
export declare function readyOrThrow(): Promise<void>;
|
|
13
15
|
export declare function withSession<T>(fn: (s: Session) => Promise<T>): Promise<T>;
|
|
14
16
|
export declare function isYdbAvailable(timeoutMs?: number): Promise<boolean>;
|
|
17
|
+
/**
|
|
18
|
+
* Destroys the current driver and its session pool.
|
|
19
|
+
* Next call to withSession or readyOrThrow will create a new driver.
|
|
20
|
+
*/
|
|
21
|
+
export declare function destroyDriver(): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Destroys the current driver and immediately creates a fresh one.
|
|
24
|
+
* Use this to recover from session pool exhaustion or zombie sessions.
|
|
25
|
+
*/
|
|
26
|
+
export declare function refreshDriver(): Promise<void>;
|
package/dist/ydb/client.js
CHANGED
|
@@ -1,16 +1,72 @@
|
|
|
1
1
|
import { createRequire } from "module";
|
|
2
|
-
import { YDB_DATABASE, YDB_ENDPOINT } from "../config/env.js";
|
|
2
|
+
import { YDB_DATABASE, YDB_ENDPOINT, SESSION_POOL_MIN_SIZE, SESSION_POOL_MAX_SIZE, SESSION_KEEPALIVE_PERIOD_MS, } from "../config/env.js";
|
|
3
|
+
import { logger } from "../logging/logger.js";
|
|
3
4
|
const require = createRequire(import.meta.url);
|
|
4
5
|
const { Driver, getCredentialsFromEnv, Types, TypedValues, TableDescription, Column, } = require("ydb-sdk");
|
|
5
6
|
export { Types, TypedValues, TableDescription, Column };
|
|
6
7
|
const DRIVER_READY_TIMEOUT_MS = 15000;
|
|
7
8
|
const TABLE_SESSION_TIMEOUT_MS = 20000;
|
|
8
9
|
const YDB_HEALTHCHECK_READY_TIMEOUT_MS = 5000;
|
|
10
|
+
const DRIVER_REFRESH_COOLDOWN_MS = 30000;
|
|
9
11
|
let overrideConfig;
|
|
10
12
|
let driver;
|
|
13
|
+
let lastDriverRefreshAt = 0;
|
|
14
|
+
let driverRefreshInFlight = null;
|
|
15
|
+
// Test-only: allows injecting a mock Driver factory
|
|
16
|
+
let driverFactoryOverride;
|
|
17
|
+
function shouldTriggerDriverRefresh(error) {
|
|
18
|
+
if (!(error instanceof Error)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const msg = error.message ?? "";
|
|
22
|
+
if (/No session became available within timeout/i.test(msg)) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if (/SESSION_POOL_EMPTY|session pool empty/i.test(msg)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
if (/SessionExpired|SESSION_EXPIRED|session.*expired/i.test(msg)) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
async function maybeRefreshDriverOnSessionError(error) {
|
|
34
|
+
if (!shouldTriggerDriverRefresh(error)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
if (now - lastDriverRefreshAt < DRIVER_REFRESH_COOLDOWN_MS) {
|
|
39
|
+
logger.warn({ lastDriverRefreshAt, cooldownMs: DRIVER_REFRESH_COOLDOWN_MS }, "YDB driver refresh skipped due to cooldown");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (driverRefreshInFlight) {
|
|
43
|
+
logger.warn({ lastDriverRefreshAt, cooldownMs: DRIVER_REFRESH_COOLDOWN_MS }, "YDB driver refresh already in flight; skipping");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
lastDriverRefreshAt = now;
|
|
47
|
+
logger.warn({ err: error }, "YDB session-related error detected; refreshing driver");
|
|
48
|
+
try {
|
|
49
|
+
const refreshPromise = refreshDriver();
|
|
50
|
+
driverRefreshInFlight = refreshPromise;
|
|
51
|
+
await refreshPromise;
|
|
52
|
+
}
|
|
53
|
+
catch (refreshErr) {
|
|
54
|
+
logger.error({ err: refreshErr }, "YDB driver refresh failed; keeping current driver");
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
driverRefreshInFlight = null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
11
60
|
export function __setDriverForTests(fake) {
|
|
12
61
|
driver = fake;
|
|
13
62
|
}
|
|
63
|
+
export function __setDriverFactoryForTests(factory) {
|
|
64
|
+
driverFactoryOverride = factory;
|
|
65
|
+
}
|
|
66
|
+
export function __resetRefreshStateForTests() {
|
|
67
|
+
lastDriverRefreshAt = 0;
|
|
68
|
+
driverRefreshInFlight = null;
|
|
69
|
+
}
|
|
14
70
|
export function configureDriver(config) {
|
|
15
71
|
if (driver) {
|
|
16
72
|
// Driver already created; keep existing connection settings.
|
|
@@ -28,10 +84,23 @@ function getOrCreateDriver() {
|
|
|
28
84
|
endpoint: overrideConfig?.endpoint ?? YDB_ENDPOINT,
|
|
29
85
|
database: overrideConfig?.database ?? YDB_DATABASE,
|
|
30
86
|
};
|
|
31
|
-
|
|
87
|
+
const driverConfig = {
|
|
32
88
|
...base,
|
|
33
89
|
authService: overrideConfig?.authService ?? getCredentialsFromEnv(),
|
|
34
|
-
|
|
90
|
+
poolSettings: {
|
|
91
|
+
minLimit: SESSION_POOL_MIN_SIZE,
|
|
92
|
+
maxLimit: SESSION_POOL_MAX_SIZE,
|
|
93
|
+
keepAlivePeriod: SESSION_KEEPALIVE_PERIOD_MS,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
driver = driverFactoryOverride
|
|
97
|
+
? driverFactoryOverride(driverConfig)
|
|
98
|
+
: new Driver(driverConfig);
|
|
99
|
+
logger.info({
|
|
100
|
+
poolMinSize: SESSION_POOL_MIN_SIZE,
|
|
101
|
+
poolMaxSize: SESSION_POOL_MAX_SIZE,
|
|
102
|
+
keepAlivePeriodMs: SESSION_KEEPALIVE_PERIOD_MS,
|
|
103
|
+
}, "YDB driver created with session pool settings");
|
|
35
104
|
return driver;
|
|
36
105
|
}
|
|
37
106
|
export async function readyOrThrow() {
|
|
@@ -43,7 +112,13 @@ export async function readyOrThrow() {
|
|
|
43
112
|
}
|
|
44
113
|
export async function withSession(fn) {
|
|
45
114
|
const d = getOrCreateDriver();
|
|
46
|
-
|
|
115
|
+
try {
|
|
116
|
+
return await d.tableClient.withSession(fn, TABLE_SESSION_TIMEOUT_MS);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
void maybeRefreshDriverOnSessionError(err);
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
47
122
|
}
|
|
48
123
|
export async function isYdbAvailable(timeoutMs = YDB_HEALTHCHECK_READY_TIMEOUT_MS) {
|
|
49
124
|
const d = getOrCreateDriver();
|
|
@@ -54,3 +129,30 @@ export async function isYdbAvailable(timeoutMs = YDB_HEALTHCHECK_READY_TIMEOUT_M
|
|
|
54
129
|
return false;
|
|
55
130
|
}
|
|
56
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Destroys the current driver and its session pool.
|
|
134
|
+
* Next call to withSession or readyOrThrow will create a new driver.
|
|
135
|
+
*/
|
|
136
|
+
export async function destroyDriver() {
|
|
137
|
+
if (!driver) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
logger.info("Destroying YDB driver and session pool");
|
|
141
|
+
try {
|
|
142
|
+
await driver.destroy();
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
logger.warn({ err }, "Error during driver destruction (ignored)");
|
|
146
|
+
}
|
|
147
|
+
driver = undefined;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Destroys the current driver and immediately creates a fresh one.
|
|
151
|
+
* Use this to recover from session pool exhaustion or zombie sessions.
|
|
152
|
+
*/
|
|
153
|
+
export async function refreshDriver() {
|
|
154
|
+
logger.info("Refreshing YDB driver");
|
|
155
|
+
await destroyDriver();
|
|
156
|
+
await readyOrThrow();
|
|
157
|
+
logger.info("YDB driver refreshed successfully");
|
|
158
|
+
}
|