@query-farm/vgi 0.31.0 → 0.32.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/catalog/item-encoding.d.ts +17 -0
- package/dist/catalog/read-only.d.ts +16 -0
- package/dist/filter-pushdown/evaluate.d.ts +9 -0
- package/dist/filter-pushdown/history.d.ts +66 -0
- package/dist/filter-pushdown/index.d.ts +1 -0
- package/dist/functions/storage-cf-do.d.ts +2 -2
- package/dist/functions/storage.d.ts +39 -3
- package/dist/functions/table-in-out.d.ts +13 -8
- package/dist/functions/types.d.ts +14 -1
- package/dist/http/fetch.d.ts +1 -1
- package/dist/index.core.d.ts +1 -1
- package/dist/index.js +1325 -988
- package/dist/index.js.map +20 -16
- package/dist/protocol/carried-init-request.d.ts +28 -0
- package/dist/protocol/dispatch.d.ts +0 -7
- package/dist/protocol/handlers/function.d.ts +0 -1
- package/dist/protocol/handlers/shared.d.ts +0 -7
- package/dist/protocol/state-serializer.d.ts +29 -2
- package/dist/serve-entry.js +393 -190
- package/dist/serve-entry.js.map +16 -12
- package/dist/util/zstd-sync.d.ts +7 -0
- package/dist/worker-cf-entry.js +5045 -4712
- package/dist/worker-cf-entry.js.map +25 -21
- package/package.json +1 -1
- package/src/catalog/item-encoding.ts +59 -0
- package/src/catalog/read-only.ts +87 -19
- package/src/filter-pushdown/evaluate.ts +55 -8
- package/src/filter-pushdown/history.ts +239 -0
- package/src/filter-pushdown/index.ts +8 -0
- package/src/functions/storage-cf-do.ts +7 -12
- package/src/functions/storage.ts +54 -11
- package/src/functions/table-buffering.ts +0 -1
- package/src/functions/table-in-out.ts +99 -60
- package/src/functions/table.ts +41 -38
- package/src/functions/types.ts +14 -1
- package/src/http/fetch.ts +2 -17
- package/src/index.core.ts +1 -0
- package/src/protocol/carried-init-request.ts +139 -0
- package/src/protocol/dispatch.ts +0 -8
- package/src/protocol/handlers/catalog/admin.ts +5 -2
- package/src/protocol/handlers/function.ts +18 -15
- package/src/protocol/handlers/shared.ts +0 -22
- package/src/protocol/state-serializer.ts +109 -37
- package/src/util/zstd-sync.ts +50 -0
package/dist/serve-entry.js
CHANGED
|
@@ -55,8 +55,8 @@ class FunctionStorageCfDo {
|
|
|
55
55
|
fetch: this._fetch
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
|
-
async workerPut(executionId,
|
|
59
|
-
await this._statePutMany(executionId, NS_WORKER, [[
|
|
58
|
+
async workerPut(executionId, worker, state) {
|
|
59
|
+
await this._statePutMany(executionId, NS_WORKER, [[workerStateKey(worker), state]]);
|
|
60
60
|
}
|
|
61
61
|
async workerCollect(executionId) {
|
|
62
62
|
const rows = await this._statePaged("state_drain", executionId, NS_WORKER, newAttemptId());
|
|
@@ -201,11 +201,6 @@ function newAttemptId() {
|
|
|
201
201
|
s += b[i].toString(16).padStart(2, "0");
|
|
202
202
|
return s;
|
|
203
203
|
}
|
|
204
|
-
function int64Key(v) {
|
|
205
|
-
const b = new Uint8Array(8);
|
|
206
|
-
new DataView(b.buffer).setBigInt64(0, BigInt(v), false);
|
|
207
|
-
return b;
|
|
208
|
-
}
|
|
209
204
|
function int64FromKey(b) {
|
|
210
205
|
if (b.length !== 8)
|
|
211
206
|
return 0;
|
|
@@ -278,11 +273,14 @@ function getDefaultDbPath() {
|
|
|
278
273
|
mkdirSync(stateDir, { recursive: true });
|
|
279
274
|
return join(stateDir, "vgi_storage.db");
|
|
280
275
|
}
|
|
281
|
-
function
|
|
276
|
+
function int64Key(v) {
|
|
282
277
|
const b = new Uint8Array(8);
|
|
283
278
|
new DataView(b.buffer).setBigInt64(0, BigInt(v), false);
|
|
284
279
|
return b;
|
|
285
280
|
}
|
|
281
|
+
function workerStateKey(worker) {
|
|
282
|
+
return typeof worker === "number" ? int64Key(worker) : worker;
|
|
283
|
+
}
|
|
286
284
|
function int64FromKey2(b) {
|
|
287
285
|
if (b.length !== 8)
|
|
288
286
|
return 0;
|
|
@@ -383,8 +381,8 @@ class FunctionStorageSqlite {
|
|
|
383
381
|
total += this._db.prepare("DELETE FROM function_state_log WHERE scope_id = ?").run(eid).changes;
|
|
384
382
|
return total;
|
|
385
383
|
}
|
|
386
|
-
async workerPut(executionId,
|
|
387
|
-
this._db.prepare("INSERT OR REPLACE INTO function_state (scope_id, ns, key, value) VALUES (?, ?, ?, ?)").run(bufferArg(executionId), bufferArg(NS_WORKER2), bufferArg(
|
|
384
|
+
async workerPut(executionId, worker, state) {
|
|
385
|
+
this._db.prepare("INSERT OR REPLACE INTO function_state (scope_id, ns, key, value) VALUES (?, ?, ?, ?)").run(bufferArg(executionId), bufferArg(NS_WORKER2), bufferArg(workerStateKey(worker)), bufferArg(state));
|
|
388
386
|
}
|
|
389
387
|
async workerCollect(executionId) {
|
|
390
388
|
const rows = this._db.prepare("DELETE FROM function_state WHERE scope_id = ? AND ns = ? RETURNING value").all(bufferArg(executionId), bufferArg(NS_WORKER2));
|
|
@@ -421,12 +419,14 @@ class FunctionStorageSqlite {
|
|
|
421
419
|
class BoundStorage {
|
|
422
420
|
_base;
|
|
423
421
|
_executionId;
|
|
424
|
-
|
|
422
|
+
_substreamId;
|
|
423
|
+
constructor(storage, executionId, substreamId = null) {
|
|
425
424
|
this._base = storage;
|
|
426
425
|
this._executionId = executionId;
|
|
426
|
+
this._substreamId = substreamId;
|
|
427
427
|
}
|
|
428
428
|
put(state) {
|
|
429
|
-
return this._base.workerPut(this._executionId, process.pid, state);
|
|
429
|
+
return this._base.workerPut(this._executionId, this._substreamId ?? process.pid, state);
|
|
430
430
|
}
|
|
431
431
|
collect() {
|
|
432
432
|
return this._base.workerCollect(this._executionId);
|
|
@@ -523,8 +523,7 @@ import {
|
|
|
523
523
|
// src/http/fetch.ts
|
|
524
524
|
import {
|
|
525
525
|
AuthContext,
|
|
526
|
-
createHttpHandler
|
|
527
|
-
unpackStateToken
|
|
526
|
+
createHttpHandler
|
|
528
527
|
} from "@query-farm/vgi-rpc";
|
|
529
528
|
|
|
530
529
|
// src/arrow/predicates.ts
|
|
@@ -2354,159 +2353,109 @@ function toUint8Array(val) {
|
|
|
2354
2353
|
}
|
|
2355
2354
|
return new Uint8Array(0);
|
|
2356
2355
|
}
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
var
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
var TEXT_DECODER = new TextDecoder;
|
|
2370
|
-
var LIST_CHILD_NAME = "item";
|
|
2371
|
-
var emptyListElementType = () => utf82();
|
|
2372
|
-
function inferElementType(values) {
|
|
2373
|
-
let elementType = null;
|
|
2374
|
-
let elementSig = "";
|
|
2375
|
-
for (const v of values) {
|
|
2376
|
-
if (v === null || v === undefined)
|
|
2377
|
-
continue;
|
|
2378
|
-
const t = inferFieldType(v);
|
|
2379
|
-
const sig = typeSignature(t);
|
|
2380
|
-
if (elementType === null) {
|
|
2381
|
-
elementType = t;
|
|
2382
|
-
elementSig = sig;
|
|
2383
|
-
continue;
|
|
2384
|
-
}
|
|
2385
|
-
if (sig !== elementSig) {
|
|
2386
|
-
throw new Error(`inferFieldType: arrays in userState must be homogeneous; ` + `found both '${elementSig}' and '${sig}' in the same array ` + `(split the values into separate fields, or normalize them to one type)`);
|
|
2387
|
-
}
|
|
2388
|
-
}
|
|
2389
|
-
return elementType ?? emptyListElementType();
|
|
2390
|
-
}
|
|
2391
|
-
function inferFieldType(value) {
|
|
2392
|
-
if (value === null || value === undefined)
|
|
2393
|
-
return nullType2();
|
|
2394
|
-
switch (typeof value) {
|
|
2395
|
-
case "number":
|
|
2396
|
-
return float642();
|
|
2397
|
-
case "bigint":
|
|
2398
|
-
return int642();
|
|
2399
|
-
case "string":
|
|
2400
|
-
return utf82();
|
|
2401
|
-
case "boolean":
|
|
2402
|
-
return bool2();
|
|
2403
|
-
case "object":
|
|
2404
|
-
if (value instanceof Uint8Array || value instanceof ArrayBuffer)
|
|
2405
|
-
return binary2();
|
|
2406
|
-
if (ArrayBuffer.isView(value))
|
|
2407
|
-
return binary2();
|
|
2408
|
-
if (Array.isArray(value)) {
|
|
2409
|
-
return list2(field2(LIST_CHILD_NAME, inferElementType(value), true));
|
|
2410
|
-
}
|
|
2411
|
-
if (value instanceof Map) {
|
|
2412
|
-
throw new Error(`inferFieldType: Map is not supported in userState (use a plain object instead)`);
|
|
2413
|
-
}
|
|
2414
|
-
if (value instanceof Set) {
|
|
2415
|
-
throw new Error(`inferFieldType: Set is not supported in userState (use an array or plain object instead)`);
|
|
2416
|
-
}
|
|
2417
|
-
if (value instanceof Date) {
|
|
2418
|
-
throw new Error(`inferFieldType: Date is not supported in userState (use a number (epoch ms) or ISO string instead)`);
|
|
2419
|
-
}
|
|
2420
|
-
if (value instanceof RegExp) {
|
|
2421
|
-
throw new Error(`inferFieldType: RegExp is not supported in userState (use a string pattern instead)`);
|
|
2422
|
-
}
|
|
2423
|
-
const fields = Object.entries(value).map(([k, v]) => field2(k, inferFieldType(v), true));
|
|
2424
|
-
return struct2(fields);
|
|
2425
|
-
default:
|
|
2426
|
-
throw new Error(`inferFieldType: unsupported type '${typeof value}'`);
|
|
2427
|
-
}
|
|
2428
|
-
}
|
|
2429
|
-
function serializeUserState(userState) {
|
|
2430
|
-
if (userState == null)
|
|
2356
|
+
// src/filter-pushdown/history.ts
|
|
2357
|
+
var MAX_ENCODED_DELTA_BYTES = 24 << 20;
|
|
2358
|
+
var MAX_DELTA_IPC_BYTES = 17 << 20;
|
|
2359
|
+
var EMPTY_FILTER_HISTORY = Object.freeze({
|
|
2360
|
+
deltas: Object.freeze([]),
|
|
2361
|
+
order: Object.freeze([])
|
|
2362
|
+
});
|
|
2363
|
+
var HISTORY_FORMAT_VERSION = 1;
|
|
2364
|
+
var UTF8_ENCODER = new TextEncoder;
|
|
2365
|
+
var UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
|
|
2366
|
+
function encodeFilterHistory(history) {
|
|
2367
|
+
if (!history || history.deltas.length === 0)
|
|
2431
2368
|
return null;
|
|
2432
|
-
const
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
const
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2369
|
+
const ids = history.order.map((id) => UTF8_ENCODER.encode(id));
|
|
2370
|
+
let size = 1 + 4 + 4;
|
|
2371
|
+
for (const delta of history.deltas)
|
|
2372
|
+
size += 4 + delta.byteLength;
|
|
2373
|
+
for (const id of ids)
|
|
2374
|
+
size += 4 + id.byteLength;
|
|
2375
|
+
const out = new Uint8Array(size);
|
|
2376
|
+
const view = new DataView(out.buffer);
|
|
2377
|
+
let offset = 0;
|
|
2378
|
+
out[offset++] = HISTORY_FORMAT_VERSION;
|
|
2379
|
+
const put = (chunks) => {
|
|
2380
|
+
view.setUint32(offset, chunks.length, true);
|
|
2381
|
+
offset += 4;
|
|
2382
|
+
for (const chunk of chunks) {
|
|
2383
|
+
view.setUint32(offset, chunk.byteLength, true);
|
|
2384
|
+
offset += 4;
|
|
2385
|
+
out.set(chunk, offset);
|
|
2386
|
+
offset += chunk.byteLength;
|
|
2387
|
+
}
|
|
2388
|
+
};
|
|
2389
|
+
put(history.deltas);
|
|
2390
|
+
put(ids);
|
|
2391
|
+
return out;
|
|
2443
2392
|
}
|
|
2444
|
-
function
|
|
2445
|
-
|
|
2393
|
+
function decodeFilterHistory(bytes) {
|
|
2394
|
+
if (!bytes || bytes.byteLength === 0)
|
|
2395
|
+
return EMPTY_FILTER_HISTORY;
|
|
2396
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
2397
|
+
let offset = 0;
|
|
2398
|
+
const need = (n) => {
|
|
2399
|
+
if (offset + n > bytes.byteLength)
|
|
2400
|
+
throw new Error("dynamic filter history in the state token is truncated");
|
|
2401
|
+
};
|
|
2402
|
+
need(1);
|
|
2403
|
+
if (bytes[offset++] !== HISTORY_FORMAT_VERSION) {
|
|
2404
|
+
throw new Error("dynamic filter history in the state token has an unknown format");
|
|
2405
|
+
}
|
|
2406
|
+
const take = () => {
|
|
2407
|
+
need(4);
|
|
2408
|
+
const count = view.getUint32(offset, true);
|
|
2409
|
+
offset += 4;
|
|
2410
|
+
const chunks = [];
|
|
2411
|
+
for (let i = 0;i < count; i++) {
|
|
2412
|
+
need(4);
|
|
2413
|
+
const length = view.getUint32(offset, true);
|
|
2414
|
+
offset += 4;
|
|
2415
|
+
need(length);
|
|
2416
|
+
chunks.push(bytes.slice(offset, offset + length));
|
|
2417
|
+
offset += length;
|
|
2418
|
+
}
|
|
2419
|
+
return chunks;
|
|
2420
|
+
};
|
|
2421
|
+
const deltas = take();
|
|
2422
|
+
const order = take().map((id) => UTF8_DECODER.decode(id));
|
|
2423
|
+
if (offset !== bytes.byteLength)
|
|
2424
|
+
throw new Error("dynamic filter history in the state token has trailing bytes");
|
|
2425
|
+
return { deltas, order };
|
|
2446
2426
|
}
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2427
|
+
|
|
2428
|
+
// src/util/zstd-sync.ts
|
|
2429
|
+
var LEVEL = 3;
|
|
2430
|
+
function resolveZstdSync() {
|
|
2431
|
+
const bun = globalThis.Bun;
|
|
2432
|
+
if (typeof bun?.zstdCompressSync === "function" && typeof bun?.zstdDecompressSync === "function") {
|
|
2433
|
+
return {
|
|
2434
|
+
compress: (data) => new Uint8Array(bun.zstdCompressSync(data, { level: LEVEL })),
|
|
2435
|
+
decompress: (data) => new Uint8Array(bun.zstdDecompressSync(data))
|
|
2436
|
+
};
|
|
2453
2437
|
}
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
if (!col) {
|
|
2460
|
-
result[f.name] = null;
|
|
2461
|
-
continue;
|
|
2462
|
-
}
|
|
2463
|
-
result[f.name] = extractTypedValue(col, 0, f.type);
|
|
2438
|
+
let zlib = null;
|
|
2439
|
+
try {
|
|
2440
|
+
zlib = globalThis.process?.getBuiltinModule?.("node:zlib") ?? null;
|
|
2441
|
+
} catch {
|
|
2442
|
+
zlib = null;
|
|
2464
2443
|
}
|
|
2465
|
-
|
|
2466
|
-
}
|
|
2467
|
-
var arrowStateSerializer = {
|
|
2468
|
-
serialize(state) {
|
|
2469
|
-
const columns = {
|
|
2470
|
-
function_name: [TEXT_ENCODER.encode(state.functionName)],
|
|
2471
|
-
init_request: [state.initRequestIpc],
|
|
2472
|
-
execution_id: [state.executionId],
|
|
2473
|
-
max_workers: [state.maxWorkers],
|
|
2474
|
-
opaque_data: [state.opaqueData ?? null],
|
|
2475
|
-
is_producer: [state.isProducer],
|
|
2476
|
-
user_state: [serializeUserState(state.userState)]
|
|
2477
|
-
};
|
|
2478
|
-
const batch = batchFromColumns(columns, EXCHANGE_STATE_SCHEMA);
|
|
2479
|
-
return serializeBatch(batch);
|
|
2480
|
-
},
|
|
2481
|
-
deserialize(bytes) {
|
|
2482
|
-
const batch = deserializeBatch(bytes);
|
|
2483
|
-
const fieldByName = new Map(batch.schema.fields.map((f) => [f.name, f]));
|
|
2484
|
-
const get = (name) => {
|
|
2485
|
-
const col = batch.getChild(name);
|
|
2486
|
-
const f = fieldByName.get(name);
|
|
2487
|
-
if (!col || !f)
|
|
2488
|
-
return null;
|
|
2489
|
-
const type = f.type;
|
|
2490
|
-
return codecFor(type).canonicalToRich(readCanonicalValue(type, col, 0));
|
|
2491
|
-
};
|
|
2492
|
-
const isProducer = get("is_producer");
|
|
2493
|
-
const opaqueDataRaw = get("opaque_data");
|
|
2494
|
-
const fnNameRaw = get("function_name");
|
|
2444
|
+
if (typeof zlib?.zstdCompressSync === "function" && typeof zlib?.zstdDecompressSync === "function") {
|
|
2445
|
+
const params = { [zlib.constants.ZSTD_c_compressionLevel]: LEVEL };
|
|
2495
2446
|
return {
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
executionId: toUint8Array(get("execution_id")),
|
|
2499
|
-
maxWorkers: Number(get("max_workers")),
|
|
2500
|
-
opaqueData: opaqueDataRaw != null ? toUint8Array(opaqueDataRaw) : null,
|
|
2501
|
-
isProducer,
|
|
2502
|
-
__isProducer: isProducer,
|
|
2503
|
-
userState: deserializeUserState(get("user_state") != null ? toUint8Array(get("user_state")) : null)
|
|
2447
|
+
compress: (data) => new Uint8Array(zlib.zstdCompressSync(data, { params })),
|
|
2448
|
+
decompress: (data) => new Uint8Array(zlib.zstdDecompressSync(data))
|
|
2504
2449
|
};
|
|
2505
2450
|
}
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2451
|
+
return null;
|
|
2452
|
+
}
|
|
2453
|
+
var resolved;
|
|
2454
|
+
function zstdSync() {
|
|
2455
|
+
if (resolved === undefined)
|
|
2456
|
+
resolved = resolveZstdSync();
|
|
2457
|
+
return resolved;
|
|
2458
|
+
}
|
|
2510
2459
|
|
|
2511
2460
|
// src/arguments/arguments.ts
|
|
2512
2461
|
class Arguments {
|
|
@@ -2910,6 +2859,269 @@ function serializeTableCardinality(card) {
|
|
|
2910
2859
|
max: card.max
|
|
2911
2860
|
};
|
|
2912
2861
|
}
|
|
2862
|
+
// src/protocol/carried-init-request.ts
|
|
2863
|
+
var RAW = 0;
|
|
2864
|
+
var ZSTD = 1;
|
|
2865
|
+
function packInitRequest(ipc) {
|
|
2866
|
+
const codec = zstdSync();
|
|
2867
|
+
const compressed = codec ? codec.compress(ipc) : null;
|
|
2868
|
+
const useZstd = compressed !== null && compressed.byteLength < ipc.byteLength;
|
|
2869
|
+
const payload = useZstd ? compressed : ipc;
|
|
2870
|
+
const packed = new Uint8Array(payload.byteLength + 1);
|
|
2871
|
+
packed[0] = useZstd ? ZSTD : RAW;
|
|
2872
|
+
packed.set(payload, 1);
|
|
2873
|
+
return packed;
|
|
2874
|
+
}
|
|
2875
|
+
function unpackInitRequest(packed) {
|
|
2876
|
+
if (packed.byteLength === 0)
|
|
2877
|
+
throw new Error("state token carries no init request");
|
|
2878
|
+
switch (packed[0]) {
|
|
2879
|
+
case RAW:
|
|
2880
|
+
return packed.slice(1);
|
|
2881
|
+
case ZSTD: {
|
|
2882
|
+
const codec = zstdSync();
|
|
2883
|
+
if (!codec) {
|
|
2884
|
+
throw new Error("state token carries a zstd-compressed init request, and this runtime has no synchronous zstd " + "(it was minted by a worker running under Bun or Node >= 22.15 with the same signing key)");
|
|
2885
|
+
}
|
|
2886
|
+
return codec.decompress(packed.subarray(1));
|
|
2887
|
+
}
|
|
2888
|
+
default:
|
|
2889
|
+
throw new Error(`state token carries an init request with unknown codec ${packed[0]}`);
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
var MAX_ENTRIES = 128;
|
|
2893
|
+
var parsed = new Map;
|
|
2894
|
+
var stats = { parses: 0, hits: 0 };
|
|
2895
|
+
function hashBytes(bytes) {
|
|
2896
|
+
let h = 2166136261 ^ bytes.byteLength;
|
|
2897
|
+
for (let i = 0;i < bytes.byteLength; i++) {
|
|
2898
|
+
h ^= bytes[i];
|
|
2899
|
+
h = Math.imul(h, 16777619);
|
|
2900
|
+
}
|
|
2901
|
+
return h >>> 0;
|
|
2902
|
+
}
|
|
2903
|
+
function sameBytes(a, b) {
|
|
2904
|
+
if (a.byteLength !== b.byteLength)
|
|
2905
|
+
return false;
|
|
2906
|
+
for (let i = 0;i < a.byteLength; i++)
|
|
2907
|
+
if (a[i] !== b[i])
|
|
2908
|
+
return false;
|
|
2909
|
+
return true;
|
|
2910
|
+
}
|
|
2911
|
+
function parseCarriedInitRequest(packed) {
|
|
2912
|
+
const key = hashBytes(packed);
|
|
2913
|
+
let entry = parsed.get(key);
|
|
2914
|
+
if (entry && sameBytes(entry.packed, packed)) {
|
|
2915
|
+
parsed.delete(key);
|
|
2916
|
+
parsed.set(key, entry);
|
|
2917
|
+
stats.hits++;
|
|
2918
|
+
} else {
|
|
2919
|
+
const dict = batchToScalarDict(deserializeBatch(unpackInitRequest(packed)));
|
|
2920
|
+
stats.parses++;
|
|
2921
|
+
entry = { packed: packed.slice(), request: deserializeInitRequest(dict), bindCallIpc: dict.bind_call };
|
|
2922
|
+
parsed.delete(key);
|
|
2923
|
+
if (parsed.size >= MAX_ENTRIES)
|
|
2924
|
+
parsed.delete(parsed.keys().next().value);
|
|
2925
|
+
parsed.set(key, entry);
|
|
2926
|
+
}
|
|
2927
|
+
return {
|
|
2928
|
+
request: { ...entry.request, bind_call: { ...entry.request.bind_call } },
|
|
2929
|
+
bindCallIpc: entry.bindCallIpc
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
// src/protocol/state-serializer.ts
|
|
2934
|
+
var EXCHANGE_STATE_SCHEMA = schema2([
|
|
2935
|
+
field2("function_name", binary2(), false),
|
|
2936
|
+
field2("init_request", binary2(), false),
|
|
2937
|
+
field2("execution_id", binary2(), false),
|
|
2938
|
+
field2("max_workers", int642(), false),
|
|
2939
|
+
field2("opaque_data", binary2(), true),
|
|
2940
|
+
field2("is_producer", bool2(), false),
|
|
2941
|
+
field2("user_state", binary2(), true),
|
|
2942
|
+
field2("filter_history", binary2(), true)
|
|
2943
|
+
]);
|
|
2944
|
+
var TEXT_ENCODER = new TextEncoder;
|
|
2945
|
+
var TEXT_DECODER = new TextDecoder;
|
|
2946
|
+
var STATE_FORMAT_VERSION = 1;
|
|
2947
|
+
var FLAG_PRODUCER = 1;
|
|
2948
|
+
var FLAG_OPAQUE_DATA = 2;
|
|
2949
|
+
var FLAG_USER_STATE = 4;
|
|
2950
|
+
var FLAG_FILTER_HISTORY = 8;
|
|
2951
|
+
var LIST_CHILD_NAME = "item";
|
|
2952
|
+
var emptyListElementType = () => utf82();
|
|
2953
|
+
function inferElementType(values) {
|
|
2954
|
+
let elementType = null;
|
|
2955
|
+
let elementSig = "";
|
|
2956
|
+
for (const v of values) {
|
|
2957
|
+
if (v === null || v === undefined)
|
|
2958
|
+
continue;
|
|
2959
|
+
const t = inferFieldType(v);
|
|
2960
|
+
const sig = typeSignature(t);
|
|
2961
|
+
if (elementType === null) {
|
|
2962
|
+
elementType = t;
|
|
2963
|
+
elementSig = sig;
|
|
2964
|
+
continue;
|
|
2965
|
+
}
|
|
2966
|
+
if (sig !== elementSig) {
|
|
2967
|
+
throw new Error(`inferFieldType: arrays in userState must be homogeneous; ` + `found both '${elementSig}' and '${sig}' in the same array ` + `(split the values into separate fields, or normalize them to one type)`);
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
return elementType ?? emptyListElementType();
|
|
2971
|
+
}
|
|
2972
|
+
function inferFieldType(value) {
|
|
2973
|
+
if (value === null || value === undefined)
|
|
2974
|
+
return nullType2();
|
|
2975
|
+
switch (typeof value) {
|
|
2976
|
+
case "number":
|
|
2977
|
+
return float642();
|
|
2978
|
+
case "bigint":
|
|
2979
|
+
return int642();
|
|
2980
|
+
case "string":
|
|
2981
|
+
return utf82();
|
|
2982
|
+
case "boolean":
|
|
2983
|
+
return bool2();
|
|
2984
|
+
case "object":
|
|
2985
|
+
if (value instanceof Uint8Array || value instanceof ArrayBuffer)
|
|
2986
|
+
return binary2();
|
|
2987
|
+
if (ArrayBuffer.isView(value))
|
|
2988
|
+
return binary2();
|
|
2989
|
+
if (Array.isArray(value)) {
|
|
2990
|
+
return list2(field2(LIST_CHILD_NAME, inferElementType(value), true));
|
|
2991
|
+
}
|
|
2992
|
+
if (value instanceof Map) {
|
|
2993
|
+
throw new Error(`inferFieldType: Map is not supported in userState (use a plain object instead)`);
|
|
2994
|
+
}
|
|
2995
|
+
if (value instanceof Set) {
|
|
2996
|
+
throw new Error(`inferFieldType: Set is not supported in userState (use an array or plain object instead)`);
|
|
2997
|
+
}
|
|
2998
|
+
if (value instanceof Date) {
|
|
2999
|
+
throw new Error(`inferFieldType: Date is not supported in userState (use a number (epoch ms) or ISO string instead)`);
|
|
3000
|
+
}
|
|
3001
|
+
if (value instanceof RegExp) {
|
|
3002
|
+
throw new Error(`inferFieldType: RegExp is not supported in userState (use a string pattern instead)`);
|
|
3003
|
+
}
|
|
3004
|
+
const fields = Object.entries(value).map(([k, v]) => field2(k, inferFieldType(v), true));
|
|
3005
|
+
return struct2(fields);
|
|
3006
|
+
default:
|
|
3007
|
+
throw new Error(`inferFieldType: unsupported type '${typeof value}'`);
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
function serializeUserState(userState) {
|
|
3011
|
+
if (userState == null)
|
|
3012
|
+
return null;
|
|
3013
|
+
const entries = Object.entries(userState);
|
|
3014
|
+
if (entries.length === 0) {
|
|
3015
|
+
return serializeBatch(batchFromColumns({}, schema2([])));
|
|
3016
|
+
}
|
|
3017
|
+
const fields = entries.map(([k, v]) => field2(k, inferFieldType(v), true));
|
|
3018
|
+
const sch = schema2(fields);
|
|
3019
|
+
const columns = {};
|
|
3020
|
+
for (const [key, val] of entries) {
|
|
3021
|
+
columns[key] = [val];
|
|
3022
|
+
}
|
|
3023
|
+
return serializeBatch(batchFromColumns(columns, sch));
|
|
3024
|
+
}
|
|
3025
|
+
function extractTypedValue(col, index, type) {
|
|
3026
|
+
return codecFor(type).canonicalToRich(readCanonicalValue(type, col, index));
|
|
3027
|
+
}
|
|
3028
|
+
function deserializeUserState(bytes) {
|
|
3029
|
+
if (bytes == null)
|
|
3030
|
+
return null;
|
|
3031
|
+
const batch = deserializeBatch(bytes);
|
|
3032
|
+
if (batch.numRows === 0 && batch.schema.fields.length === 0) {
|
|
3033
|
+
return {};
|
|
3034
|
+
}
|
|
3035
|
+
if (batch.numRows === 0)
|
|
3036
|
+
return null;
|
|
3037
|
+
const result = {};
|
|
3038
|
+
for (const f of batch.schema.fields) {
|
|
3039
|
+
const col = batch.getChild(f.name);
|
|
3040
|
+
if (!col) {
|
|
3041
|
+
result[f.name] = null;
|
|
3042
|
+
continue;
|
|
3043
|
+
}
|
|
3044
|
+
result[f.name] = extractTypedValue(col, 0, f.type);
|
|
3045
|
+
}
|
|
3046
|
+
return result;
|
|
3047
|
+
}
|
|
3048
|
+
var arrowStateSerializer = {
|
|
3049
|
+
serialize(state) {
|
|
3050
|
+
const functionName = TEXT_ENCODER.encode(state.functionName ?? "");
|
|
3051
|
+
const initRequest = state.initRequestPacked ?? packInitRequest(state.initRequestIpc);
|
|
3052
|
+
const executionId = toUint8Array(state.executionId);
|
|
3053
|
+
const opaqueData = state.opaqueData != null ? toUint8Array(state.opaqueData) : null;
|
|
3054
|
+
const userState = serializeUserState(state.userState);
|
|
3055
|
+
const filterHistory = encodeFilterHistory(state.filterHistory);
|
|
3056
|
+
const flags = (state.isProducer ? FLAG_PRODUCER : 0) | (opaqueData ? FLAG_OPAQUE_DATA : 0) | (userState ? FLAG_USER_STATE : 0) | (filterHistory ? FLAG_FILTER_HISTORY : 0);
|
|
3057
|
+
const chunks = [functionName, initRequest, executionId];
|
|
3058
|
+
if (opaqueData)
|
|
3059
|
+
chunks.push(opaqueData);
|
|
3060
|
+
if (userState)
|
|
3061
|
+
chunks.push(userState);
|
|
3062
|
+
if (filterHistory)
|
|
3063
|
+
chunks.push(filterHistory);
|
|
3064
|
+
let size = 2 + 8;
|
|
3065
|
+
for (const chunk of chunks)
|
|
3066
|
+
size += 4 + chunk.byteLength;
|
|
3067
|
+
const out = new Uint8Array(size);
|
|
3068
|
+
const view = new DataView(out.buffer);
|
|
3069
|
+
out[0] = STATE_FORMAT_VERSION;
|
|
3070
|
+
out[1] = flags;
|
|
3071
|
+
view.setFloat64(2, Number(state.maxWorkers ?? 1), true);
|
|
3072
|
+
let offset = 10;
|
|
3073
|
+
for (const chunk of chunks) {
|
|
3074
|
+
view.setUint32(offset, chunk.byteLength, true);
|
|
3075
|
+
out.set(chunk, offset + 4);
|
|
3076
|
+
offset += 4 + chunk.byteLength;
|
|
3077
|
+
}
|
|
3078
|
+
return out;
|
|
3079
|
+
},
|
|
3080
|
+
deserialize(bytes) {
|
|
3081
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
3082
|
+
if (bytes.byteLength < 10 || bytes[0] !== STATE_FORMAT_VERSION) {
|
|
3083
|
+
throw new Error("exchange state has an unknown format (minted by another build of this worker?)");
|
|
3084
|
+
}
|
|
3085
|
+
const flags = bytes[1];
|
|
3086
|
+
const maxWorkers = view.getFloat64(2, true);
|
|
3087
|
+
let offset = 10;
|
|
3088
|
+
const take = () => {
|
|
3089
|
+
if (offset + 4 > bytes.byteLength)
|
|
3090
|
+
throw new Error("exchange state is truncated");
|
|
3091
|
+
const length = view.getUint32(offset, true);
|
|
3092
|
+
offset += 4;
|
|
3093
|
+
if (offset + length > bytes.byteLength)
|
|
3094
|
+
throw new Error("exchange state is truncated");
|
|
3095
|
+
const chunk = bytes.slice(offset, offset + length);
|
|
3096
|
+
offset += length;
|
|
3097
|
+
return chunk;
|
|
3098
|
+
};
|
|
3099
|
+
const functionName = TEXT_DECODER.decode(take());
|
|
3100
|
+
const initRequestPacked = take();
|
|
3101
|
+
const executionId = take();
|
|
3102
|
+
const opaqueData = flags & FLAG_OPAQUE_DATA ? take() : null;
|
|
3103
|
+
const userStateBytes = flags & FLAG_USER_STATE ? take() : null;
|
|
3104
|
+
const filterHistoryBytes = flags & FLAG_FILTER_HISTORY ? take() : null;
|
|
3105
|
+
if (offset !== bytes.byteLength)
|
|
3106
|
+
throw new Error("exchange state has trailing bytes");
|
|
3107
|
+
const isProducer = (flags & FLAG_PRODUCER) !== 0;
|
|
3108
|
+
return {
|
|
3109
|
+
functionName,
|
|
3110
|
+
initRequestPacked,
|
|
3111
|
+
executionId,
|
|
3112
|
+
maxWorkers,
|
|
3113
|
+
opaqueData,
|
|
3114
|
+
isProducer,
|
|
3115
|
+
__isProducer: isProducer,
|
|
3116
|
+
userState: deserializeUserState(userStateBytes),
|
|
3117
|
+
filterHistory: decodeFilterHistory(filterHistoryBytes)
|
|
3118
|
+
};
|
|
3119
|
+
}
|
|
3120
|
+
};
|
|
3121
|
+
|
|
3122
|
+
// src/protocol/dispatch.ts
|
|
3123
|
+
import { Protocol } from "@query-farm/vgi-rpc";
|
|
3124
|
+
|
|
2913
3125
|
// src/util/statistics.ts
|
|
2914
3126
|
function serializeColumnStatistics(stats, _cacheMaxAgeSeconds) {
|
|
2915
3127
|
return serializeBatch(buildStatisticsBatch(stats));
|
|
@@ -3912,17 +4124,6 @@ function overloadContext(req, catalogInterface) {
|
|
|
3912
4124
|
catalogName
|
|
3913
4125
|
};
|
|
3914
4126
|
}
|
|
3915
|
-
function recoverFinalizeState(request, recoverExchangeState) {
|
|
3916
|
-
if (request.phase === "FINALIZE" /* FINALIZE */ && request.init_opaque_data && recoverExchangeState) {
|
|
3917
|
-
try {
|
|
3918
|
-
const recovered = recoverExchangeState(request.init_opaque_data);
|
|
3919
|
-
return recovered?.userState;
|
|
3920
|
-
} catch (e) {
|
|
3921
|
-
throw new Error(`Failed to recover FINALIZE state from init_opaque_data: ${e.message}`);
|
|
3922
|
-
}
|
|
3923
|
-
}
|
|
3924
|
-
return;
|
|
3925
|
-
}
|
|
3926
4127
|
|
|
3927
4128
|
// src/errors.ts
|
|
3928
4129
|
class VgiError extends Error {
|
|
@@ -5086,8 +5287,7 @@ function registerFunctionMethods(protocol, config) {
|
|
|
5086
5287
|
});
|
|
5087
5288
|
const func = registry.get(request.bind_call.function_name, overloadContext(request.bind_call, catalogInterface));
|
|
5088
5289
|
const initResponse = await func.globalInit(request);
|
|
5089
|
-
const
|
|
5090
|
-
const handlers = func.createStreamHandlers(request, initResponse, accumulatedState);
|
|
5290
|
+
const handlers = func.createStreamHandlers(request, initResponse);
|
|
5091
5291
|
let handlerState;
|
|
5092
5292
|
if (handlers.producerInit) {
|
|
5093
5293
|
handlerState = handlers.producerInit();
|
|
@@ -5121,10 +5321,9 @@ function registerFunctionMethods(protocol, config) {
|
|
|
5121
5321
|
handlers = state._handlers;
|
|
5122
5322
|
handlerState = state._handlerState;
|
|
5123
5323
|
} else {
|
|
5124
|
-
const
|
|
5125
|
-
const
|
|
5126
|
-
|
|
5127
|
-
await resolveSplitPayloads(request, { bind_call: initRequestDict.bind_call }, {
|
|
5324
|
+
const carried = parseCarriedInitRequest(state.initRequestPacked);
|
|
5325
|
+
const request = carried.request;
|
|
5326
|
+
await resolveSplitPayloads(request, { bind_call: carried.bindCallIpc }, {
|
|
5128
5327
|
auth: currentRequestAuth(),
|
|
5129
5328
|
signingKey,
|
|
5130
5329
|
catalogInterface,
|
|
@@ -5138,8 +5337,7 @@ function registerFunctionMethods(protocol, config) {
|
|
|
5138
5337
|
max_workers: Number(state.maxWorkers ?? 1),
|
|
5139
5338
|
opaque_data: opaqueData
|
|
5140
5339
|
};
|
|
5141
|
-
|
|
5142
|
-
handlers = func.createStreamHandlers(request, initResponse, recoveredState);
|
|
5340
|
+
handlers = func.createStreamHandlers(request, initResponse, state.filterHistory ?? null);
|
|
5143
5341
|
if (handlers.producerInit) {
|
|
5144
5342
|
handlerState = handlers.producerInit();
|
|
5145
5343
|
} else if (handlers.exchangeInit) {
|
|
@@ -5163,6 +5361,7 @@ function registerFunctionMethods(protocol, config) {
|
|
|
5163
5361
|
if (handlerState?.state !== undefined) {
|
|
5164
5362
|
state.userState = handlerState.state;
|
|
5165
5363
|
}
|
|
5364
|
+
state.filterHistory = handlerState?.filterHistory ?? null;
|
|
5166
5365
|
},
|
|
5167
5366
|
headerSchema: initHeaderSchema,
|
|
5168
5367
|
headerInit: (params, state, ctx) => {
|
|
@@ -5846,6 +6045,19 @@ var encodeSchemaInfo = (v) => encodeASD(SchemaInfoSchema, v);
|
|
|
5846
6045
|
var encodeTableInfo = (v) => encodeASD(TableInfoSchema, v);
|
|
5847
6046
|
var encodeViewInfo = (v) => encodeASD(ViewInfoSchema, v);
|
|
5848
6047
|
|
|
6048
|
+
// src/catalog/item-encoding.ts
|
|
6049
|
+
var ENCODED = new WeakMap;
|
|
6050
|
+
function encodeFunctionInfoOnce(info) {
|
|
6051
|
+
if (!Object.isFrozen(info))
|
|
6052
|
+
return encodeFunctionInfo(info);
|
|
6053
|
+
let encoded = ENCODED.get(info);
|
|
6054
|
+
if (encoded === undefined) {
|
|
6055
|
+
encoded = encodeFunctionInfo(info);
|
|
6056
|
+
ENCODED.set(info, encoded);
|
|
6057
|
+
}
|
|
6058
|
+
return encoded;
|
|
6059
|
+
}
|
|
6060
|
+
|
|
5849
6061
|
// src/catalog/interface.ts
|
|
5850
6062
|
var encodeCopyFromFormatInfo = (v) => encodeASD(CopyFromFormatInfoSchema, {
|
|
5851
6063
|
comment: v.comment ?? null,
|
|
@@ -6053,7 +6265,7 @@ function registerCatalogAdminMethods(protocol, getCatalog, signingKey) {
|
|
|
6053
6265
|
const cat = getCatalog();
|
|
6054
6266
|
const funcs = await cat.schemaContentsFunctions(toUint8Array(params.attach_opaque_data), params.path, decodeDictValue(params.type), params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined);
|
|
6055
6267
|
return wrapResult({
|
|
6056
|
-
items: funcs.map((f) =>
|
|
6268
|
+
items: funcs.map((f) => encodeFunctionInfoOnce(f))
|
|
6057
6269
|
}, CatalogSchemaContentsFunctionsResultSchema);
|
|
6058
6270
|
}
|
|
6059
6271
|
});
|
|
@@ -6347,7 +6559,6 @@ function buildVgiProtocol(config) {
|
|
|
6347
6559
|
const protocol = new Protocol(VGI_PROTOCOL_NAME, { protocolVersion: "2.0.0" });
|
|
6348
6560
|
registerFunctionMethods(protocol, {
|
|
6349
6561
|
registry: config.registry,
|
|
6350
|
-
recoverExchangeState: config.recoverExchangeState,
|
|
6351
6562
|
signingKey: config.signingKey,
|
|
6352
6563
|
catalogInterface: config.catalogInterface
|
|
6353
6564
|
});
|
|
@@ -6496,15 +6707,7 @@ function createVgiFetch(opts) {
|
|
|
6496
6707
|
const serverId = opts.serverId ?? "vgi-cf";
|
|
6497
6708
|
const protocol = buildVgiProtocol({
|
|
6498
6709
|
signingKey: opts.signingKey,
|
|
6499
|
-
...opts.protocol
|
|
6500
|
-
recoverExchangeState: async (opaqueData) => {
|
|
6501
|
-
const tokenString = new TextDecoder().decode(opaqueData);
|
|
6502
|
-
const unpacked = await unpackStateToken(tokenString, opts.signingKey, tokenTtl, {
|
|
6503
|
-
protocol: VGI_PROTOCOL_NAME,
|
|
6504
|
-
principal: undefined
|
|
6505
|
-
});
|
|
6506
|
-
return arrowStateSerializer.deserialize(unpacked.stateBytes);
|
|
6507
|
-
}
|
|
6710
|
+
...opts.protocol
|
|
6508
6711
|
});
|
|
6509
6712
|
const handler = createHttpHandler(protocol, {
|
|
6510
6713
|
prefix,
|
|
@@ -6656,4 +6859,4 @@ export {
|
|
|
6656
6859
|
serveVgiWorker
|
|
6657
6860
|
};
|
|
6658
6861
|
|
|
6659
|
-
//# debugId=
|
|
6862
|
+
//# debugId=CEC41B08D9AF368564756E2164756E21
|