ydb-qdrant 7.0.1 → 8.1.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.
- package/README.md +2 -2
- package/dist/config/env.d.ts +0 -8
- package/dist/config/env.js +2 -29
- package/dist/package/api.d.ts +5 -2
- package/dist/package/api.js +2 -2
- package/dist/qdrant/QdrantRestTypes.d.ts +35 -0
- package/dist/repositories/collectionsRepo.d.ts +1 -2
- package/dist/repositories/collectionsRepo.js +62 -103
- package/dist/repositories/collectionsRepo.one-table.js +103 -47
- package/dist/repositories/collectionsRepo.shared.d.ts +2 -0
- package/dist/repositories/collectionsRepo.shared.js +32 -0
- package/dist/repositories/pointsRepo.d.ts +4 -8
- package/dist/repositories/pointsRepo.one-table/Delete.js +122 -67
- package/dist/repositories/pointsRepo.one-table/PathSegmentsFilter.d.ts +5 -2
- package/dist/repositories/pointsRepo.one-table/PathSegmentsFilter.js +7 -6
- package/dist/repositories/pointsRepo.one-table/Search.d.ts +4 -0
- package/dist/repositories/pointsRepo.one-table/Search.js +208 -0
- package/dist/repositories/pointsRepo.one-table/Upsert.d.ts +2 -2
- package/dist/repositories/pointsRepo.one-table/Upsert.js +51 -66
- package/dist/repositories/pointsRepo.one-table.d.ts +1 -1
- package/dist/repositories/pointsRepo.one-table.js +1 -1
- package/dist/routes/collections.js +7 -61
- package/dist/routes/points.js +15 -66
- package/dist/services/PointsService.d.ts +3 -8
- package/dist/services/PointsService.js +19 -23
- package/dist/types.d.ts +23 -33
- package/dist/types.js +18 -20
- package/dist/utils/normalization.js +13 -14
- package/dist/utils/retry.js +19 -29
- package/dist/utils/vectorBinary.js +10 -5
- package/dist/ydb/bootstrapMetaTable.d.ts +7 -0
- package/dist/ydb/bootstrapMetaTable.js +75 -0
- package/dist/ydb/client.d.ts +23 -17
- package/dist/ydb/client.js +82 -423
- package/dist/ydb/schema.js +88 -148
- package/package.json +2 -10
- package/dist/qdrant/QdrantTypes.d.ts +0 -19
- package/dist/repositories/pointsRepo.one-table/Search/Approximate.d.ts +0 -18
- package/dist/repositories/pointsRepo.one-table/Search/Approximate.js +0 -119
- package/dist/repositories/pointsRepo.one-table/Search/Exact.d.ts +0 -17
- package/dist/repositories/pointsRepo.one-table/Search/Exact.js +0 -101
- package/dist/repositories/pointsRepo.one-table/Search/index.d.ts +0 -8
- package/dist/repositories/pointsRepo.one-table/Search/index.js +0 -30
- package/dist/utils/typeGuards.d.ts +0 -1
- package/dist/utils/typeGuards.js +0 -3
- package/dist/ydb/QueryDiagnostics.d.ts +0 -6
- package/dist/ydb/QueryDiagnostics.js +0 -52
- package/dist/ydb/SessionPool.d.ts +0 -36
- package/dist/ydb/SessionPool.js +0 -248
- package/dist/ydb/bulkUpsert.d.ts +0 -6
- package/dist/ydb/bulkUpsert.js +0 -52
- /package/dist/qdrant/{QdrantTypes.js → QdrantRestTypes.js} +0 -0
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { StatsMode } from "@ydbjs/api/query";
|
|
2
|
-
import { QUERY_RETRY_LOG_ENABLED, QUERY_STATS_MODE, QueryStatsMode, } from "../config/env.js";
|
|
3
|
-
import { logger } from "../logging/logger.js";
|
|
4
|
-
import { isRecord } from "../utils/typeGuards.js";
|
|
5
|
-
function mapStatsMode(mode) {
|
|
6
|
-
if (mode === QueryStatsMode.Basic)
|
|
7
|
-
return StatsMode.BASIC;
|
|
8
|
-
if (mode === QueryStatsMode.Full)
|
|
9
|
-
return StatsMode.FULL;
|
|
10
|
-
if (mode === QueryStatsMode.Profile)
|
|
11
|
-
return StatsMode.PROFILE;
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
function pickStatsSummary(stats) {
|
|
15
|
-
if (!isRecord(stats)) {
|
|
16
|
-
return {};
|
|
17
|
-
}
|
|
18
|
-
const phase = stats.queryPhaseStats;
|
|
19
|
-
if (!isRecord(phase)) {
|
|
20
|
-
return {};
|
|
21
|
-
}
|
|
22
|
-
return {
|
|
23
|
-
cpuTimeUs: phase.cpuTimeUs,
|
|
24
|
-
durationUs: phase.durationUs,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
export function attachQueryDiagnostics(q, context) {
|
|
28
|
-
const statsMode = mapStatsMode(QUERY_STATS_MODE);
|
|
29
|
-
let out = q;
|
|
30
|
-
if (statsMode) {
|
|
31
|
-
out = out.withStats(statsMode);
|
|
32
|
-
out.on("stats", (stats) => {
|
|
33
|
-
logger.info({
|
|
34
|
-
...context,
|
|
35
|
-
queryStatsMode: QUERY_STATS_MODE,
|
|
36
|
-
...pickStatsSummary(stats),
|
|
37
|
-
}, `${context.operation}: stats`);
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
if (QUERY_RETRY_LOG_ENABLED) {
|
|
41
|
-
out.on("retry", (ctx) => {
|
|
42
|
-
logger.warn({
|
|
43
|
-
...context,
|
|
44
|
-
attempt: ctx.attempt,
|
|
45
|
-
err: ctx.error instanceof Error
|
|
46
|
-
? ctx.error
|
|
47
|
-
: new Error(String(ctx.error)),
|
|
48
|
-
}, `${context.operation}: retry`);
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
return out;
|
|
52
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { Driver } from "@ydbjs/core";
|
|
2
|
-
export type PooledQuerySession = {
|
|
3
|
-
nodeId: bigint;
|
|
4
|
-
sessionId: string;
|
|
5
|
-
};
|
|
6
|
-
export declare class SessionPool {
|
|
7
|
-
private readonly driver;
|
|
8
|
-
private readonly available;
|
|
9
|
-
private readonly inUse;
|
|
10
|
-
private readonly waiters;
|
|
11
|
-
private keepaliveTimer;
|
|
12
|
-
private isClosed;
|
|
13
|
-
constructor(driver: Driver);
|
|
14
|
-
/**
|
|
15
|
-
* Test-only escape hatch to inspect and drive internal state without unsafe casts.
|
|
16
|
-
* Not part of the public API surface.
|
|
17
|
-
*/
|
|
18
|
-
__getInternalsForTests(): {
|
|
19
|
-
available: Array<{
|
|
20
|
-
sessionId: string;
|
|
21
|
-
lastCheckedAtMs: number;
|
|
22
|
-
}>;
|
|
23
|
-
keepaliveTick: () => Promise<void>;
|
|
24
|
-
inUse: Set<string>;
|
|
25
|
-
};
|
|
26
|
-
start(): void;
|
|
27
|
-
close(): Promise<void>;
|
|
28
|
-
warmup(signal: AbortSignal): Promise<void>;
|
|
29
|
-
acquire(signal: AbortSignal): Promise<PooledQuerySession>;
|
|
30
|
-
release(session: PooledQuerySession): void;
|
|
31
|
-
discard(session: PooledQuerySession): Promise<void>;
|
|
32
|
-
private totalSize;
|
|
33
|
-
private createAndAttachSession;
|
|
34
|
-
private deleteSessionBestEffort;
|
|
35
|
-
private keepaliveTick;
|
|
36
|
-
}
|
package/dist/ydb/SessionPool.js
DELETED
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
import { StatusIds_StatusCode } from "@ydbjs/api/operation";
|
|
2
|
-
import { QueryServiceDefinition } from "@ydbjs/api/query";
|
|
3
|
-
import { YDBError } from "@ydbjs/error";
|
|
4
|
-
import { logger } from "../logging/logger.js";
|
|
5
|
-
import { SESSION_KEEPALIVE_PERIOD_MS, SESSION_POOL_MAX_SIZE, SESSION_POOL_MIN_SIZE, STARTUP_PROBE_SESSION_TIMEOUT_MS, } from "../config/env.js";
|
|
6
|
-
function isSuccessStatus(status) {
|
|
7
|
-
return status === StatusIds_StatusCode.SUCCESS;
|
|
8
|
-
}
|
|
9
|
-
function asAttachSessionResponse(value) {
|
|
10
|
-
if (typeof value === "object" && value !== null) {
|
|
11
|
-
return value;
|
|
12
|
-
}
|
|
13
|
-
return {};
|
|
14
|
-
}
|
|
15
|
-
function toStatusCode(status) {
|
|
16
|
-
return typeof status === "number"
|
|
17
|
-
? status
|
|
18
|
-
: StatusIds_StatusCode.STATUS_CODE_UNSPECIFIED;
|
|
19
|
-
}
|
|
20
|
-
const EMPTY_ISSUES = [];
|
|
21
|
-
function nowMs() {
|
|
22
|
-
return Date.now();
|
|
23
|
-
}
|
|
24
|
-
export class SessionPool {
|
|
25
|
-
driver;
|
|
26
|
-
available = [];
|
|
27
|
-
inUse = new Set();
|
|
28
|
-
waiters = [];
|
|
29
|
-
keepaliveTimer = null;
|
|
30
|
-
isClosed = false;
|
|
31
|
-
constructor(driver) {
|
|
32
|
-
this.driver = driver;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Test-only escape hatch to inspect and drive internal state without unsafe casts.
|
|
36
|
-
* Not part of the public API surface.
|
|
37
|
-
*/
|
|
38
|
-
__getInternalsForTests() {
|
|
39
|
-
return {
|
|
40
|
-
available: this.available,
|
|
41
|
-
keepaliveTick: () => this.keepaliveTick(),
|
|
42
|
-
inUse: this.inUse,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
start() {
|
|
46
|
-
if (this.keepaliveTimer)
|
|
47
|
-
return;
|
|
48
|
-
this.keepaliveTimer = setInterval(() => {
|
|
49
|
-
void this.keepaliveTick();
|
|
50
|
-
}, SESSION_KEEPALIVE_PERIOD_MS);
|
|
51
|
-
// Don't keep the Node process alive just because of keepalive.
|
|
52
|
-
this.keepaliveTimer.unref();
|
|
53
|
-
}
|
|
54
|
-
async close() {
|
|
55
|
-
this.isClosed = true;
|
|
56
|
-
if (this.keepaliveTimer) {
|
|
57
|
-
clearInterval(this.keepaliveTimer);
|
|
58
|
-
this.keepaliveTimer = null;
|
|
59
|
-
}
|
|
60
|
-
for (const w of this.waiters.splice(0)) {
|
|
61
|
-
w.reject(new Error("SessionPool is closed"));
|
|
62
|
-
}
|
|
63
|
-
const toDelete = this.available.splice(0);
|
|
64
|
-
for (const s of toDelete) {
|
|
65
|
-
await this.deleteSessionBestEffort(s);
|
|
66
|
-
}
|
|
67
|
-
this.inUse.clear();
|
|
68
|
-
}
|
|
69
|
-
async warmup(signal) {
|
|
70
|
-
const target = SESSION_POOL_MIN_SIZE;
|
|
71
|
-
if (target <= 0)
|
|
72
|
-
return;
|
|
73
|
-
while (!this.isClosed && this.totalSize() < target) {
|
|
74
|
-
const s = await this.createAndAttachSession(signal);
|
|
75
|
-
this.available.push({
|
|
76
|
-
...s,
|
|
77
|
-
lastUsedAtMs: nowMs(),
|
|
78
|
-
lastCheckedAtMs: 0,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
async acquire(signal) {
|
|
83
|
-
if (this.isClosed) {
|
|
84
|
-
throw new Error("SessionPool is closed");
|
|
85
|
-
}
|
|
86
|
-
// Prefer an existing idle session.
|
|
87
|
-
const existing = this.available.pop();
|
|
88
|
-
if (existing) {
|
|
89
|
-
this.inUse.add(existing.sessionId);
|
|
90
|
-
existing.lastUsedAtMs = nowMs();
|
|
91
|
-
return { nodeId: existing.nodeId, sessionId: existing.sessionId };
|
|
92
|
-
}
|
|
93
|
-
// Create a new one if we are under the max.
|
|
94
|
-
if (this.totalSize() < SESSION_POOL_MAX_SIZE) {
|
|
95
|
-
const created = await this.createAndAttachSession(signal);
|
|
96
|
-
const internal = {
|
|
97
|
-
...created,
|
|
98
|
-
lastUsedAtMs: nowMs(),
|
|
99
|
-
lastCheckedAtMs: 0,
|
|
100
|
-
};
|
|
101
|
-
this.inUse.add(internal.sessionId);
|
|
102
|
-
return { nodeId: internal.nodeId, sessionId: internal.sessionId };
|
|
103
|
-
}
|
|
104
|
-
// Otherwise, wait for a release.
|
|
105
|
-
return await new Promise((resolve, reject) => {
|
|
106
|
-
const cleanup = () => {
|
|
107
|
-
signal.removeEventListener("abort", onAbort);
|
|
108
|
-
};
|
|
109
|
-
const waiter = {
|
|
110
|
-
fulfill: (s) => {
|
|
111
|
-
cleanup();
|
|
112
|
-
this.inUse.add(s.sessionId);
|
|
113
|
-
s.lastUsedAtMs = nowMs();
|
|
114
|
-
resolve({ nodeId: s.nodeId, sessionId: s.sessionId });
|
|
115
|
-
},
|
|
116
|
-
reject: (err) => {
|
|
117
|
-
cleanup();
|
|
118
|
-
reject(err);
|
|
119
|
-
},
|
|
120
|
-
cleanup,
|
|
121
|
-
};
|
|
122
|
-
const onAbort = () => {
|
|
123
|
-
const idx = this.waiters.indexOf(waiter);
|
|
124
|
-
if (idx >= 0) {
|
|
125
|
-
this.waiters.splice(idx, 1);
|
|
126
|
-
}
|
|
127
|
-
waiter.reject(new Error("SessionPool acquire aborted"));
|
|
128
|
-
};
|
|
129
|
-
if (signal.aborted)
|
|
130
|
-
return onAbort();
|
|
131
|
-
signal.addEventListener("abort", onAbort);
|
|
132
|
-
this.waiters.push(waiter);
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
release(session) {
|
|
136
|
-
if (this.isClosed) {
|
|
137
|
-
this.inUse.delete(session.sessionId);
|
|
138
|
-
void this.deleteSessionBestEffort(session);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
if (!this.inUse.has(session.sessionId)) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
this.inUse.delete(session.sessionId);
|
|
145
|
-
const internal = {
|
|
146
|
-
...session,
|
|
147
|
-
lastUsedAtMs: nowMs(),
|
|
148
|
-
lastCheckedAtMs: 0,
|
|
149
|
-
};
|
|
150
|
-
const waiter = this.waiters.shift();
|
|
151
|
-
if (waiter) {
|
|
152
|
-
waiter.fulfill(internal);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
this.available.push(internal);
|
|
156
|
-
// Soft cap: if we exceed max (shouldn't happen), evict extras.
|
|
157
|
-
while (this.totalSize() > SESSION_POOL_MAX_SIZE &&
|
|
158
|
-
this.available.length > 0) {
|
|
159
|
-
const victim = this.available.shift();
|
|
160
|
-
if (victim) {
|
|
161
|
-
void this.deleteSessionBestEffort(victim);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
async discard(session) {
|
|
166
|
-
this.inUse.delete(session.sessionId);
|
|
167
|
-
await this.deleteSessionBestEffort(session);
|
|
168
|
-
}
|
|
169
|
-
totalSize() {
|
|
170
|
-
return this.available.length + this.inUse.size;
|
|
171
|
-
}
|
|
172
|
-
async createAndAttachSession(signal) {
|
|
173
|
-
await this.driver.ready(signal);
|
|
174
|
-
const client = this.driver.createClient(QueryServiceDefinition);
|
|
175
|
-
const sessionResponse = await client.createSession({}, { signal });
|
|
176
|
-
if (!isSuccessStatus(sessionResponse.status)) {
|
|
177
|
-
throw new YDBError(sessionResponse.status, sessionResponse.issues);
|
|
178
|
-
}
|
|
179
|
-
const nodeId = sessionResponse.nodeId;
|
|
180
|
-
const sessionId = sessionResponse.sessionId;
|
|
181
|
-
const nodeClient = this.driver.createClient(QueryServiceDefinition, nodeId);
|
|
182
|
-
const attachStream = nodeClient.attachSession({ sessionId }, { signal });
|
|
183
|
-
const attach = attachStream[Symbol.asyncIterator]();
|
|
184
|
-
const first = await attach.next();
|
|
185
|
-
const attachResp = asAttachSessionResponse(first.value);
|
|
186
|
-
const code = toStatusCode(attachResp.status);
|
|
187
|
-
if (!isSuccessStatus(code)) {
|
|
188
|
-
throw new YDBError(code, EMPTY_ISSUES);
|
|
189
|
-
}
|
|
190
|
-
return { nodeId, sessionId };
|
|
191
|
-
}
|
|
192
|
-
async deleteSessionBestEffort(session) {
|
|
193
|
-
try {
|
|
194
|
-
const client = this.driver.createClient(QueryServiceDefinition, session.nodeId);
|
|
195
|
-
await client.deleteSession({ sessionId: session.sessionId }, { signal: AbortSignal.timeout(STARTUP_PROBE_SESSION_TIMEOUT_MS) });
|
|
196
|
-
}
|
|
197
|
-
catch (err) {
|
|
198
|
-
logger.warn({ err }, "SessionPool: failed to delete session (ignored)");
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
async keepaliveTick() {
|
|
202
|
-
if (this.isClosed)
|
|
203
|
-
return;
|
|
204
|
-
if (this.available.length === 0)
|
|
205
|
-
return;
|
|
206
|
-
// Probe at most one idle session per tick to bound overhead.
|
|
207
|
-
const idx = this.available.reduce((best, cur, i, arr) => {
|
|
208
|
-
const bestAt = arr[best]?.lastCheckedAtMs ?? 0;
|
|
209
|
-
return cur.lastCheckedAtMs < bestAt ? i : best;
|
|
210
|
-
}, 0);
|
|
211
|
-
const s = this.available[idx];
|
|
212
|
-
if (!s)
|
|
213
|
-
return;
|
|
214
|
-
const sessionId = s.sessionId;
|
|
215
|
-
const nodeId = s.nodeId;
|
|
216
|
-
const now = nowMs();
|
|
217
|
-
if (now - s.lastCheckedAtMs < SESSION_KEEPALIVE_PERIOD_MS) {
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
s.lastCheckedAtMs = now;
|
|
221
|
-
try {
|
|
222
|
-
const signal = AbortSignal.timeout(STARTUP_PROBE_SESSION_TIMEOUT_MS);
|
|
223
|
-
const client = this.driver.createClient(QueryServiceDefinition, s.nodeId);
|
|
224
|
-
const attachStream = client.attachSession({ sessionId: s.sessionId }, { signal });
|
|
225
|
-
const attach = attachStream[Symbol.asyncIterator]();
|
|
226
|
-
const first = await attach.next();
|
|
227
|
-
const attachResp = asAttachSessionResponse(first.value);
|
|
228
|
-
const code = toStatusCode(attachResp.status);
|
|
229
|
-
if (!isSuccessStatus(code)) {
|
|
230
|
-
throw new YDBError(code, EMPTY_ISSUES);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
catch {
|
|
234
|
-
// Session likely dead; evict from pool and delete best-effort.
|
|
235
|
-
// Important: `this.available` may have been modified while awaiting the probe.
|
|
236
|
-
// Avoid using the pre-await index, and never delete a session that is currently leased.
|
|
237
|
-
if (this.inUse.has(sessionId)) {
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
const availableIdx = this.available.findIndex((x) => x.sessionId === sessionId);
|
|
241
|
-
if (availableIdx === -1) {
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
this.available.splice(availableIdx, 1);
|
|
245
|
-
void this.deleteSessionBestEffort({ nodeId, sessionId });
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
package/dist/ydb/bulkUpsert.d.ts
DELETED
package/dist/ydb/bulkUpsert.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { create } from "@bufbuild/protobuf";
|
|
2
|
-
import { StatusIds_StatusCode } from "@ydbjs/api/operation";
|
|
3
|
-
import { OperationServiceDefinition } from "@ydbjs/api/operation";
|
|
4
|
-
import { TableServiceDefinition } from "@ydbjs/api/table";
|
|
5
|
-
import { TypedValueSchema } from "@ydbjs/api/value";
|
|
6
|
-
import { YDBError } from "@ydbjs/error";
|
|
7
|
-
import { setTimeout as sleep } from "node:timers/promises";
|
|
8
|
-
import { __getDriverForInternalUse } from "./client.js";
|
|
9
|
-
function tablePath(database, tableName) {
|
|
10
|
-
const db = database.endsWith("/") ? database.slice(0, -1) : database;
|
|
11
|
-
const t = tableName.startsWith("/") ? tableName : `/${tableName}`;
|
|
12
|
-
return `${db}${t}`;
|
|
13
|
-
}
|
|
14
|
-
async function waitOperationReady(args) {
|
|
15
|
-
for (;;) {
|
|
16
|
-
const resp = await args.operationClient.getOperation({ id: args.operationId }, { signal: args.signal });
|
|
17
|
-
const op = resp.operation;
|
|
18
|
-
if (!op) {
|
|
19
|
-
throw new Error("BulkUpsert: getOperation returned no operation");
|
|
20
|
-
}
|
|
21
|
-
if (op.ready) {
|
|
22
|
-
return { status: op.status, issues: op.issues };
|
|
23
|
-
}
|
|
24
|
-
// Small delay before next poll.
|
|
25
|
-
await sleep(25, undefined, { signal: args.signal });
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
export async function bulkUpsertRowsOnce(args) {
|
|
29
|
-
const d = __getDriverForInternalUse();
|
|
30
|
-
const fullTablePath = tablePath(d.database, args.tableName);
|
|
31
|
-
const signal = AbortSignal.timeout(args.timeoutMs);
|
|
32
|
-
await d.ready(signal);
|
|
33
|
-
const tableClient = d.createClient(TableServiceDefinition);
|
|
34
|
-
const operationClient = d.createClient(OperationServiceDefinition);
|
|
35
|
-
const typedRows = create(TypedValueSchema, {
|
|
36
|
-
type: args.rowsValue.type.encode(),
|
|
37
|
-
value: args.rowsValue.encode(),
|
|
38
|
-
});
|
|
39
|
-
const resp = await tableClient.bulkUpsert({ table: fullTablePath, rows: typedRows }, { signal });
|
|
40
|
-
const op = resp.operation;
|
|
41
|
-
if (!op) {
|
|
42
|
-
throw new Error("BulkUpsert: response has no operation");
|
|
43
|
-
}
|
|
44
|
-
const final = op.ready
|
|
45
|
-
? { status: op.status, issues: op.issues }
|
|
46
|
-
: op.id
|
|
47
|
-
? await waitOperationReady({ operationClient, operationId: op.id, signal })
|
|
48
|
-
: { status: op.status, issues: op.issues };
|
|
49
|
-
if (final.status !== StatusIds_StatusCode.SUCCESS) {
|
|
50
|
-
throw new YDBError(final.status, final.issues);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
File without changes
|