@delali/narsil 0.3.1-next.72 → 0.3.1-next.74
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/adapters/filesystem.mjs +1 -1
- package/dist/adapters/indexeddb.mjs +1 -1
- package/dist/{capabilities-D6gjjQyA.d.ts → capabilities-CAMzmkAg.d.ts} +19 -4
- package/dist/{chunk-P2ZZN32C.mjs → chunk-2E35UM3G.mjs} +2 -12
- package/dist/chunk-3DDUNI6N.mjs +48 -0
- package/dist/{chunk-KZTI25D3.mjs → chunk-4MQ5F4IL.mjs} +1 -1
- package/dist/{chunk-HVJYTKK7.mjs → chunk-BVBIQQKG.mjs} +947 -1327
- package/dist/{chunk-2X2ZQOAB.mjs → chunk-CTOSGRM7.mjs} +101 -21
- package/dist/{chunk-7QMTA7AA.mjs → chunk-CUZOX6CN.mjs} +7784 -4276
- package/dist/chunk-I23G3IZC.mjs +593 -0
- package/dist/{chunk-RV77FR2F.mjs → chunk-I64LXKO3.mjs} +0 -1
- package/dist/chunk-KEHK2O6P.mjs +13 -0
- package/dist/{chunk-CVPA5Q7R.mjs → chunk-LX4JERXI.mjs} +9 -6
- package/dist/{chunk-EDMCZLSJ.mjs → chunk-NOZUQM7J.mjs} +193 -13
- package/dist/{chunk-LCCL6EEH.mjs → chunk-PO53FV4Q.mjs} +93 -94
- package/dist/chunk-T5ZWEWVD.mjs +658 -0
- package/dist/{chunk-Y52WZWAU.mjs → chunk-TVFLHM7M.mjs} +4 -4
- package/dist/{chunk-QR4OKRVY.mjs → chunk-VKX2BPXD.mjs} +147 -642
- package/dist/{chunk-3HQPUCCT.mjs → chunk-XW25X6AU.mjs} +66 -237
- package/dist/{chunk-M6XF5HDE.mjs → chunk-Z3DQPOJQ.mjs} +127 -30
- package/dist/{client-C2TiW29y.d.ts → client-C-I70ArH.d.ts} +77 -26
- package/dist/client.d.ts +9 -9
- package/dist/client.mjs +44 -22
- package/dist/{config-BLguB-om.d.ts → config-C6Iw5rCX.d.ts} +11 -4
- package/dist/{config-C7Ip8Ife.d.ts → config-DJPH3IQB.d.ts} +11 -2
- package/dist/direct-executor-KFAGILJC.mjs +17 -0
- package/dist/distribution/coordinator/etcd.d.ts +29 -16
- package/dist/distribution/coordinator/etcd.mjs +162 -75
- package/dist/distribution/coordinator/in-memory.d.ts +2 -2
- package/dist/distribution/transport/grpc.d.ts +2 -2
- package/dist/distribution/transport/grpc.mjs +22 -2
- package/dist/distribution/transport/in-memory.d.ts +2 -2
- package/dist/distribution/transport/in-memory.mjs +6 -3
- package/dist/distribution/transport/tcp.d.ts +2 -2
- package/dist/distribution/transport/tcp.mjs +40 -16
- package/dist/distribution.d.ts +9 -9
- package/dist/distribution.mjs +647 -366
- package/dist/embeddings/openai.mjs +1 -1
- package/dist/{engine-Dm4446j1.d.ts → engine-B8eRPO7m.d.ts} +97 -188
- package/dist/index.browser.mjs +12132 -8592
- package/dist/index.d.ts +27 -8
- package/dist/index.mjs +15 -14
- package/dist/{memory-CKHTU1CL.d.ts → memory-DJEwWoCi.d.ts} +1204 -968
- package/dist/partial-words-DTIHO8G4.d.ts +19 -0
- package/dist/partial-words.d.ts +22 -0
- package/dist/partial-words.mjs +11 -0
- package/dist/pattern.mjs +1 -1
- package/dist/persistence/durability/checkpoint-worker.d.ts +57 -29
- package/dist/persistence/durability/checkpoint-worker.mjs +26 -16
- package/dist/react.d.ts +7 -7
- package/dist/react.mjs +0 -1
- package/dist/{schema-BBOCdhND.d.ts → schema-DgyDijhf.d.ts} +6 -2
- package/dist/server.d.ts +11 -11
- package/dist/server.mjs +212 -32
- package/dist/{tasks-BECd0b__.d.ts → tasks-BVmAKYBh.d.ts} +3 -3
- package/dist/{thread-IBSIZZ6W.mjs → thread-W6DPKEF6.mjs} +82 -21
- package/dist/{types-BQDlIvfS.d.ts → types-B3b68nfF.d.ts} +1 -1
- package/dist/{types-D5OwhtsQ.d.ts → types-Cb9kiY5P.d.ts} +1 -1
- package/dist/{types-BiUmaiWT.d.ts → types-ChrVy4lH.d.ts} +1 -1
- package/dist/vector/search-worker.d.ts +1 -1
- package/dist/vector/search-worker.mjs +4 -3
- package/dist/workers/entry.mjs +4 -4
- package/package.json +15 -7
- package/dist/chunk-3L4U6EJQ.mjs +0 -49
- package/dist/chunk-IFBKLBGB.mjs +0 -7
- package/dist/chunk-XZ4PAREY.mjs +0 -25
- package/dist/direct-executor-SVCHXR74.mjs +0 -14
- package/dist/serialization/crc32-worker.d.ts +0 -20
- package/dist/serialization/crc32-worker.mjs +0 -62
package/dist/distribution.mjs
CHANGED
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
import { MAX_MESSAGE_SIZE_BYTES } from './chunk-UNEEFTEU.mjs';
|
|
2
|
-
import { validateIndexName, indexConfigKey, indexSettingsOf, putIndexMetadata,
|
|
3
|
-
import { requireKnownConfig, resolveRunnableConfig, createEngineCore, startEngineCore, createNarsilFromCore, runEngineQueryStats, runEngineListDocuments, runEngineSuggest, runEnginePreflight, runEngineQuery, createEngineIndex, applyIndexEntry, applyDeleteEntry, validateEntryBatchPayload, createAckMessage, validateEntryPayload, validateForwardBatchPayload, createForwardBatchResultMessage, createForwardBatchMessage, validateForwardBatchResultPayload, createForwardMessage, validateReplicationEntry, computeEntryChecksum, createEntryMessage, createEntryBatchMessage, createInsyncConfirmMessage, validateAckPayload, createInsyncRemoveMessage, validateInsyncConfirmPayload, createInsyncAddMessage } from './chunk-
|
|
4
|
-
import { now, listBindingOf, queryBindingOf, hitsKeptPerGroup, clampLimit, resolveHybridFusion, dedupePinnedEntries, foldGroupReducer } from './chunk-
|
|
2
|
+
import { validateIndexName, indexConfigKey, withPartitionCount, indexSettingsOf, putIndexMetadata, getIndexMetadata, getClusterIndexConfig } from './chunk-TVFLHM7M.mjs';
|
|
3
|
+
import { requireKnownConfig, resolveRunnableConfig, createEngineCore, startEngineCore, createNarsilFromCore, runEngineQueryStats, runEngineListDocuments, runEngineSuggest, runEnginePreflight, runEngineQuery, createEngineIndex, buildArchiveFromMemory, indexMetadataOf, encodeIndexConfiguration, archiveStream, applyIndexEntry, applyDeleteEntry, requireConfigurationWithinSizeLimit, validateEntryBatchPayload, createAckMessage, validateEntryPayload, validateForwardBatchPayload, createForwardBatchResultMessage, memoryCheckpointStore, readSnapshotArchive, createByteReader, openChecksummedEnvelope, loadPartitionSegments, readListedFile, createForwardBatchMessage, validateForwardBatchResultPayload, createForwardMessage, validateReplicationEntry, crc32Init, crc32Final, crc32Update, computeEntryChecksum, createEntryMessage, createEntryBatchMessage, createInsyncConfirmMessage, validateAckPayload, createInsyncRemoveMessage, validateInsyncConfirmPayload, createInsyncAddMessage } from './chunk-CUZOX6CN.mjs';
|
|
4
|
+
import { now, listBindingOf, requireSurfaceFormsForPartialFields, queryBindingOf, hitsKeptPerGroup, clampLimit, resolveHybridFusion, dedupePinnedEntries, foldGroupReducer } from './chunk-CTOSGRM7.mjs';
|
|
5
5
|
import { ReplicationMessageTypes, TransportErrorCodes, ClusterMessageTypes, QueryMessageTypes, TransportError } from './chunk-GCM3JFVA.mjs';
|
|
6
|
-
import { providedDocId, MAX_PARTITION_COUNT, MAX_REPLICATION_FACTOR, WIRE_BATCH_BUDGET } from './chunk-
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { validateSchema, DEFAULT_PAGE_SIZE, MAX_SORT_FIELDS, MAX_CURSOR_LENGTH, MAX_TOLERANCE, MAX_PREFIX_LENGTH, RESULT_WINDOW, MIN_POLYGON_POINTS, isLatitude, MAX_LATITUDE_DEGREES, isLongitude, MAX_LONGITUDE_DEGREES, MAX_NESTING_DEPTH } from './chunk-M6XF5HDE.mjs';
|
|
6
|
+
import { providedDocId, MAX_PARTITION_COUNT, MAX_REPLICATION_FACTOR, WIRE_BATCH_BUDGET } from './chunk-LX4JERXI.mjs';
|
|
7
|
+
import { generateId, decodeVectorFilePayload, bytesToVectors, isDead, FILTER_EXPRESSION_KEYS, FIELD_FILTER_OPERATORS } from './chunk-BVBIQQKG.mjs';
|
|
8
|
+
import { oversampledShardSize, resolveProjection, clampRowCount, sortSignatureOf, requireMatchingCursor, decodePageCursor, normalizeSort, sortModesOf, encodePageCursor, applyProjection, readSortValues, requireWithinResultWindow, fnv1a, sortSignatureEntry, MAX_FILTER_DEPTH, filterTestCount, MAX_FILTER_TESTS, fnv1aUint32s, mergePartitionStats, MAX_FILTER_ARRAY_SIZE } from './chunk-VKX2BPXD.mjs';
|
|
9
|
+
import { getLanguage } from './chunk-4MQ5F4IL.mjs';
|
|
10
|
+
import { DEFAULT_SUGGEST_LIMIT, MAX_SUGGEST_LIMIT, DEFAULT_PAGE_SIZE, MAX_SUGGEST_SCATTER_LIMIT, MAX_INDEX_NAME_LENGTH, MAX_SORT_FIELDS, MAX_DOC_ID_LENGTH, MAX_CURSOR_LENGTH, MAX_TOLERANCE, SUGGEST_OVERSAMPLE_FACTOR, SUGGEST_OVERSAMPLE_PADDING, INDEX_NAME_PATTERN, MAX_PREFIX_LENGTH, RESULT_WINDOW } from './chunk-3DDUNI6N.mjs';
|
|
12
11
|
import './chunk-TGP5M5R7.mjs';
|
|
13
|
-
import './chunk-
|
|
12
|
+
import './chunk-PO53FV4Q.mjs';
|
|
14
13
|
import { MIN_OVERSAMPLE } from './chunk-ZJ4CSAB2.mjs';
|
|
15
|
-
import './chunk-
|
|
16
|
-
import
|
|
17
|
-
import './chunk-IFBKLBGB.mjs';
|
|
18
|
-
import './chunk-3HQPUCCT.mjs';
|
|
14
|
+
import './chunk-2E35UM3G.mjs';
|
|
15
|
+
import './chunk-XW25X6AU.mjs';
|
|
19
16
|
import './chunk-ZKRV5M2P.mjs';
|
|
17
|
+
import './chunk-T5ZWEWVD.mjs';
|
|
18
|
+
import './chunk-I23G3IZC.mjs';
|
|
19
|
+
import { validateSchema, requireCreatableSchema, encodeDocument, MAX_STORED_VECTOR_DIMENSION, MIN_POLYGON_POINTS, isLatitude, MAX_LATITUDE_DEGREES, isLongitude, MAX_LONGITUDE_DEGREES, MAX_NESTING_DEPTH } from './chunk-Z3DQPOJQ.mjs';
|
|
20
|
+
import './chunk-KEHK2O6P.mjs';
|
|
20
21
|
import './chunk-U2BKYEC7.mjs';
|
|
21
22
|
import { compareCodePoints, toComparableSortValue, compareComparableKeys, isSortMode, SORT_MODES, exceedsCodePoints, compareSortValues } from './chunk-LJYTW7GT.mjs';
|
|
22
23
|
import './chunk-POWOTUKJ.mjs';
|
|
23
|
-
import { ErrorCodes, NarsilError, describeError } from './chunk-
|
|
24
|
+
import { ErrorCodes, NarsilError, describeError } from './chunk-I64LXKO3.mjs';
|
|
24
25
|
import { encode, decode } from '@msgpack/msgpack';
|
|
26
|
+
import { randomUUID } from 'crypto';
|
|
27
|
+
import { openSync, writeSync, closeSync, rmSync, readSync } from 'fs';
|
|
28
|
+
import { mkdtemp, rm, mkdir } from 'fs/promises';
|
|
29
|
+
import { tmpdir } from 'os';
|
|
30
|
+
import { join } from 'path';
|
|
25
31
|
|
|
26
32
|
// src/distribution/cluster/constants.ts
|
|
27
33
|
var LEADERSHIP_IMBALANCE_THRESHOLD = 2;
|
|
@@ -1251,7 +1257,6 @@ var REPLICATION_ENTRY_FIXED_OVERHEAD_BYTES = 40;
|
|
|
1251
1257
|
var DEFAULT_LOG_RETENTION_BYTES = 268435456;
|
|
1252
1258
|
var INSYNC_CAS_ATTEMPTS = 5;
|
|
1253
1259
|
var SNAPSHOT_CHUNK_SIZE = 65536;
|
|
1254
|
-
var MAX_SNAPSHOT_SIZE_BYTES = 2 * 1024 * 1024 * 1024;
|
|
1255
1260
|
|
|
1256
1261
|
// src/distribution/replication/insync.ts
|
|
1257
1262
|
var REFUSE = { outcome: "refuse" };
|
|
@@ -2516,6 +2521,119 @@ function createDataNodeLifecycle(config) {
|
|
|
2516
2521
|
};
|
|
2517
2522
|
return handle;
|
|
2518
2523
|
}
|
|
2524
|
+
var SPOOL_FILE_PREFIX = "narsil-snapshot-";
|
|
2525
|
+
var DURABLE_SPOOL_DIRECTORY = ".snapshot-transfer";
|
|
2526
|
+
var TEMPORARY_SPOOL_DIRECTORY_PREFIX = "narsil-snapshot-transfer-";
|
|
2527
|
+
var OWNER_ONLY_FILE_MODE = 384;
|
|
2528
|
+
var OWNER_ONLY_DIRECTORY_MODE = 448;
|
|
2529
|
+
function defaultSpoolDirectory() {
|
|
2530
|
+
return tmpdir();
|
|
2531
|
+
}
|
|
2532
|
+
function spoolFilePath(directory) {
|
|
2533
|
+
return join(directory, `${SPOOL_FILE_PREFIX}${randomUUID()}`);
|
|
2534
|
+
}
|
|
2535
|
+
async function* readSpoolFile(path) {
|
|
2536
|
+
const descriptor = openSync(path, "r");
|
|
2537
|
+
try {
|
|
2538
|
+
for (; ; ) {
|
|
2539
|
+
const buffer = new Uint8Array(SNAPSHOT_CHUNK_SIZE);
|
|
2540
|
+
const bytesRead = readSync(descriptor, buffer, 0, buffer.byteLength, null);
|
|
2541
|
+
if (bytesRead === 0) return;
|
|
2542
|
+
yield bytesRead === buffer.byteLength ? buffer : buffer.subarray(0, bytesRead);
|
|
2543
|
+
}
|
|
2544
|
+
} finally {
|
|
2545
|
+
closeSync(descriptor);
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
function spooledFile(path, totalBytes, checksum) {
|
|
2549
|
+
let discarded = false;
|
|
2550
|
+
return {
|
|
2551
|
+
totalBytes,
|
|
2552
|
+
checksum,
|
|
2553
|
+
read: () => readSpoolFile(path),
|
|
2554
|
+
async discard() {
|
|
2555
|
+
if (discarded) return;
|
|
2556
|
+
discarded = true;
|
|
2557
|
+
removeQuietly(path);
|
|
2558
|
+
}
|
|
2559
|
+
};
|
|
2560
|
+
}
|
|
2561
|
+
function openSnapshotSpoolWriter(directory) {
|
|
2562
|
+
const path = spoolFilePath(directory);
|
|
2563
|
+
let descriptor = openSync(path, "wx", OWNER_ONLY_FILE_MODE);
|
|
2564
|
+
let checksumState = crc32Init();
|
|
2565
|
+
let totalBytes = 0;
|
|
2566
|
+
const close = () => {
|
|
2567
|
+
if (descriptor === null) return;
|
|
2568
|
+
const closing = descriptor;
|
|
2569
|
+
descriptor = null;
|
|
2570
|
+
closeSync(closing);
|
|
2571
|
+
};
|
|
2572
|
+
return {
|
|
2573
|
+
write(bytes) {
|
|
2574
|
+
if (descriptor === null) {
|
|
2575
|
+
throw new NarsilError(ErrorCodes.SNAPSHOT_SYNC_RESTORE_FAILED, "The snapshot spool file is already closed", {
|
|
2576
|
+
path
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
let offset = 0;
|
|
2580
|
+
while (offset < bytes.byteLength) {
|
|
2581
|
+
offset += writeSync(descriptor, bytes, offset, bytes.byteLength - offset);
|
|
2582
|
+
}
|
|
2583
|
+
checksumState = crc32Update(checksumState, bytes);
|
|
2584
|
+
totalBytes += bytes.byteLength;
|
|
2585
|
+
},
|
|
2586
|
+
checksum() {
|
|
2587
|
+
return crc32Final(checksumState);
|
|
2588
|
+
},
|
|
2589
|
+
finish() {
|
|
2590
|
+
close();
|
|
2591
|
+
return spooledFile(path, totalBytes, crc32Final(checksumState));
|
|
2592
|
+
},
|
|
2593
|
+
discard() {
|
|
2594
|
+
try {
|
|
2595
|
+
close();
|
|
2596
|
+
} finally {
|
|
2597
|
+
removeQuietly(path);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
};
|
|
2601
|
+
}
|
|
2602
|
+
function removeQuietly(path) {
|
|
2603
|
+
try {
|
|
2604
|
+
rmSync(path, { force: true });
|
|
2605
|
+
} catch (_) {
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
async function spoolSnapshotStream(directory, stream) {
|
|
2609
|
+
const reader = stream.getReader();
|
|
2610
|
+
let writer = null;
|
|
2611
|
+
try {
|
|
2612
|
+
writer = openSnapshotSpoolWriter(directory);
|
|
2613
|
+
for (; ; ) {
|
|
2614
|
+
const step = await reader.read();
|
|
2615
|
+
if (step.done) break;
|
|
2616
|
+
if (step.value.byteLength > 0) writer.write(step.value);
|
|
2617
|
+
}
|
|
2618
|
+
const spooled = writer.finish();
|
|
2619
|
+
writer = null;
|
|
2620
|
+
return spooled;
|
|
2621
|
+
} catch (err) {
|
|
2622
|
+
writer?.discard();
|
|
2623
|
+
await reader.cancel().catch(() => void 0);
|
|
2624
|
+
throw err;
|
|
2625
|
+
} finally {
|
|
2626
|
+
reader.releaseLock();
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
async function openSnapshotSpoolDirectory(durabilityDirectory) {
|
|
2630
|
+
const path = durabilityDirectory === void 0 ? await mkdtemp(join(tmpdir(), TEMPORARY_SPOOL_DIRECTORY_PREFIX)) : join(durabilityDirectory, DURABLE_SPOOL_DIRECTORY);
|
|
2631
|
+
if (durabilityDirectory !== void 0) {
|
|
2632
|
+
await rm(path, { recursive: true, force: true });
|
|
2633
|
+
await mkdir(path, { recursive: true, mode: OWNER_ONLY_DIRECTORY_MODE });
|
|
2634
|
+
}
|
|
2635
|
+
return { path, dispose: () => rm(path, { recursive: true, force: true }) };
|
|
2636
|
+
}
|
|
2519
2637
|
|
|
2520
2638
|
// src/distribution/cluster-node/local-index-gone.ts
|
|
2521
2639
|
function localIndexIsGone(err) {
|
|
@@ -2583,6 +2701,19 @@ function fail(state, code, message, details = {}) {
|
|
|
2583
2701
|
return;
|
|
2584
2702
|
}
|
|
2585
2703
|
state.failure = { ok: false, code, message, details };
|
|
2704
|
+
releaseSpool(state);
|
|
2705
|
+
}
|
|
2706
|
+
function releaseSpool(state) {
|
|
2707
|
+
const spool = state.spool;
|
|
2708
|
+
if (spool === null) return;
|
|
2709
|
+
state.spool = null;
|
|
2710
|
+
spool.discard();
|
|
2711
|
+
}
|
|
2712
|
+
function spoolFailure(state, err) {
|
|
2713
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
2714
|
+
fail(state, ErrorCodes.SNAPSHOT_SYNC_RESTORE_FAILED, `the snapshot could not be written to local storage: ${cause}`, {
|
|
2715
|
+
cause
|
|
2716
|
+
});
|
|
2586
2717
|
}
|
|
2587
2718
|
function applyErrorEnvelope(state, record) {
|
|
2588
2719
|
const code = typeof record.code === "string" && record.code.length > 0 ? record.code : "UNKNOWN_ERROR";
|
|
@@ -2633,19 +2764,17 @@ function applyStartPayload(state, record) {
|
|
|
2633
2764
|
fail(state, ErrorCodes.SNAPSHOT_SYNC_HEADER_INVALID, "totalBytes must be a non-negative integer");
|
|
2634
2765
|
return;
|
|
2635
2766
|
}
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
});
|
|
2767
|
+
try {
|
|
2768
|
+
state.spool = openSnapshotSpoolWriter(state.expectation.spoolDirectory);
|
|
2769
|
+
} catch (err) {
|
|
2770
|
+
spoolFailure(state, err);
|
|
2641
2771
|
return;
|
|
2642
2772
|
}
|
|
2643
2773
|
state.header = header;
|
|
2644
2774
|
state.totalBytes = start.totalBytes;
|
|
2645
|
-
state.assembled = new Uint8Array(start.totalBytes);
|
|
2646
2775
|
}
|
|
2647
2776
|
function applyChunkPayload(state, payload) {
|
|
2648
|
-
if (state.totalBytes === null || state.
|
|
2777
|
+
if (state.totalBytes === null || state.header === null) {
|
|
2649
2778
|
fail(state, ErrorCodes.SNAPSHOT_SYNC_FRAME_INVALID, "received chunk before SNAPSHOT_START");
|
|
2650
2779
|
return;
|
|
2651
2780
|
}
|
|
@@ -2699,7 +2828,16 @@ function applyChunkPayload(state, payload) {
|
|
|
2699
2828
|
});
|
|
2700
2829
|
return;
|
|
2701
2830
|
}
|
|
2702
|
-
state.
|
|
2831
|
+
if (state.spool === null) {
|
|
2832
|
+
fail(state, ErrorCodes.SNAPSHOT_SYNC_FRAME_INVALID, "received chunk after the snapshot stream closed");
|
|
2833
|
+
return;
|
|
2834
|
+
}
|
|
2835
|
+
try {
|
|
2836
|
+
state.spool.write(payload.data);
|
|
2837
|
+
} catch (err) {
|
|
2838
|
+
spoolFailure(state, err);
|
|
2839
|
+
return;
|
|
2840
|
+
}
|
|
2703
2841
|
state.accumulatedBytes = nextBytes;
|
|
2704
2842
|
state.expectedNextOffset = payload.offset + payload.data.byteLength;
|
|
2705
2843
|
}
|
|
@@ -2777,7 +2915,7 @@ function createSnapshotStreamState(expectation) {
|
|
|
2777
2915
|
expectation,
|
|
2778
2916
|
header: null,
|
|
2779
2917
|
totalBytes: null,
|
|
2780
|
-
|
|
2918
|
+
spool: null,
|
|
2781
2919
|
accumulatedBytes: 0,
|
|
2782
2920
|
expectedNextOffset: 0,
|
|
2783
2921
|
endPayload: null,
|
|
@@ -2832,11 +2970,20 @@ function handleIncomingSnapshotRecord(state, record) {
|
|
|
2832
2970
|
}
|
|
2833
2971
|
fail(state, ErrorCodes.SNAPSHOT_SYNC_FRAME_INVALID, "unrecognised snapshot frame shape");
|
|
2834
2972
|
}
|
|
2973
|
+
function abandonSnapshotStream(state) {
|
|
2974
|
+
fail(state, ErrorCodes.SNAPSHOT_SYNC_ABORTED, "the snapshot stream was abandoned before it finished");
|
|
2975
|
+
releaseSpool(state);
|
|
2976
|
+
}
|
|
2835
2977
|
function finalizeSnapshotStream(state) {
|
|
2978
|
+
const result = settleSnapshotStream(state);
|
|
2979
|
+
if (!result.ok) releaseSpool(state);
|
|
2980
|
+
return result;
|
|
2981
|
+
}
|
|
2982
|
+
function settleSnapshotStream(state) {
|
|
2836
2983
|
if (state.failure !== null) {
|
|
2837
2984
|
return state.failure;
|
|
2838
2985
|
}
|
|
2839
|
-
if (state.header === null || state.totalBytes === null
|
|
2986
|
+
if (state.header === null || state.totalBytes === null) {
|
|
2840
2987
|
return {
|
|
2841
2988
|
ok: false,
|
|
2842
2989
|
code: ErrorCodes.SNAPSHOT_SYNC_CHUNK_MISSING,
|
|
@@ -2868,9 +3015,28 @@ function finalizeSnapshotStream(state) {
|
|
|
2868
3015
|
}
|
|
2869
3016
|
};
|
|
2870
3017
|
}
|
|
3018
|
+
const spool = state.spool;
|
|
3019
|
+
if (spool === null) {
|
|
3020
|
+
return {
|
|
3021
|
+
ok: false,
|
|
3022
|
+
code: ErrorCodes.SNAPSHOT_SYNC_FRAME_INVALID,
|
|
3023
|
+
message: "the snapshot stream was already settled",
|
|
3024
|
+
details: { indexName: state.expectation.indexName }
|
|
3025
|
+
};
|
|
3026
|
+
}
|
|
3027
|
+
const computed = spool.checksum();
|
|
3028
|
+
if (computed !== state.header.checksum) {
|
|
3029
|
+
return {
|
|
3030
|
+
ok: false,
|
|
3031
|
+
code: ErrorCodes.SNAPSHOT_SYNC_CHECKSUM_MISMATCH,
|
|
3032
|
+
message: "computed checksum does not match header checksum",
|
|
3033
|
+
details: { computed, header: state.header.checksum }
|
|
3034
|
+
};
|
|
3035
|
+
}
|
|
3036
|
+
state.spool = null;
|
|
2871
3037
|
return {
|
|
2872
3038
|
ok: true,
|
|
2873
|
-
|
|
3039
|
+
snapshot: spool.finish(),
|
|
2874
3040
|
header: state.header,
|
|
2875
3041
|
end: state.endPayload
|
|
2876
3042
|
};
|
|
@@ -3081,7 +3247,11 @@ async function fetchSnapshotFromTarget(indexName, partitionId, target, deadline,
|
|
|
3081
3247
|
requestId: generateId(),
|
|
3082
3248
|
payload: encode(requestPayload)
|
|
3083
3249
|
};
|
|
3084
|
-
const streamState = createSnapshotStreamState({
|
|
3250
|
+
const streamState = createSnapshotStreamState({
|
|
3251
|
+
indexName,
|
|
3252
|
+
partitionId: null,
|
|
3253
|
+
spoolDirectory: deps.spoolDirectory ?? defaultSpoolDirectory()
|
|
3254
|
+
});
|
|
3085
3255
|
const remainingMs = deadline - Date.now();
|
|
3086
3256
|
if (remainingMs <= 0) {
|
|
3087
3257
|
return {
|
|
@@ -3097,6 +3267,7 @@ async function fetchSnapshotFromTarget(indexName, partitionId, target, deadline,
|
|
|
3097
3267
|
});
|
|
3098
3268
|
await withDeadline(streamWork, remainingMs, indexName, "stream");
|
|
3099
3269
|
} catch (err) {
|
|
3270
|
+
abandonSnapshotStream(streamState);
|
|
3100
3271
|
if (err instanceof NarsilError && err.code === ErrorCodes.SNAPSHOT_SYNC_TIMEOUT) {
|
|
3101
3272
|
return toStreamFailure(err, ErrorCodes.SNAPSHOT_SYNC_TIMEOUT, "snapshot stream deadline exceeded");
|
|
3102
3273
|
}
|
|
@@ -3105,20 +3276,7 @@ async function fetchSnapshotFromTarget(indexName, partitionId, target, deadline,
|
|
|
3105
3276
|
}
|
|
3106
3277
|
return toStreamFailure(err, ErrorCodes.SNAPSHOT_SYNC_PRIMARY_ERROR, "snapshot fetch raised non-transport error");
|
|
3107
3278
|
}
|
|
3108
|
-
|
|
3109
|
-
if (!result.ok) {
|
|
3110
|
-
return result;
|
|
3111
|
-
}
|
|
3112
|
-
const computedChecksum = crc32(result.bytes);
|
|
3113
|
-
if (computedChecksum !== result.header.checksum) {
|
|
3114
|
-
return {
|
|
3115
|
-
ok: false,
|
|
3116
|
-
code: ErrorCodes.SNAPSHOT_SYNC_CHECKSUM_MISMATCH,
|
|
3117
|
-
message: "computed checksum does not match header checksum",
|
|
3118
|
-
details: { computed: computedChecksum, header: result.header.checksum }
|
|
3119
|
-
};
|
|
3120
|
-
}
|
|
3121
|
-
return result;
|
|
3279
|
+
return finalizeSnapshotStream(streamState);
|
|
3122
3280
|
}
|
|
3123
3281
|
async function withDeadline(work, ms, indexName, phase) {
|
|
3124
3282
|
let timer = null;
|
|
@@ -3267,9 +3425,9 @@ function surfaceAborted(deps, indexName, primaryNodeId) {
|
|
|
3267
3425
|
})
|
|
3268
3426
|
);
|
|
3269
3427
|
}
|
|
3270
|
-
async function executeEngineRestore(engine, indexName, primaryNodeId,
|
|
3428
|
+
async function executeEngineRestore(engine, indexName, primaryNodeId, source, remainingMs) {
|
|
3271
3429
|
try {
|
|
3272
|
-
await withDeadline(engine.restore(indexName,
|
|
3430
|
+
await withDeadline(engine.restore(indexName, source), remainingMs, indexName, "restore");
|
|
3273
3431
|
return null;
|
|
3274
3432
|
} catch (err) {
|
|
3275
3433
|
if (err instanceof NarsilError && err.code === ErrorCodes.SNAPSHOT_SYNC_TIMEOUT) {
|
|
@@ -3555,9 +3713,9 @@ function validateSyncEntryForApply(entry, indexName, partitionId, expectedSeqNo,
|
|
|
3555
3713
|
}
|
|
3556
3714
|
return null;
|
|
3557
3715
|
}
|
|
3558
|
-
function createLiveSyncFrameState(indexName, partitionId) {
|
|
3716
|
+
function createLiveSyncFrameState(indexName, partitionId, spoolDirectory) {
|
|
3559
3717
|
return {
|
|
3560
|
-
snapshotState: createSnapshotStreamState({ indexName, partitionId }),
|
|
3718
|
+
snapshotState: createSnapshotStreamState({ indexName, partitionId, spoolDirectory }),
|
|
3561
3719
|
syncEntries: [],
|
|
3562
3720
|
sawSnapshotFrame: false,
|
|
3563
3721
|
error: null
|
|
@@ -3659,35 +3817,25 @@ async function applyLiveIncrementalSync(indexName, partitionId, target, entriesP
|
|
|
3659
3817
|
async function applyLiveSnapshotSync(state, entry, indexName, partitionId, target, frameState, coordinatorConfig, partitionCount, deadline, deps) {
|
|
3660
3818
|
const finalized = finalizeSnapshotStream(frameState.snapshotState);
|
|
3661
3819
|
if (!finalized.ok) {
|
|
3662
|
-
return { ok: false, error: streamFailureToError(finalized) };
|
|
3820
|
+
return { ok: false, error: streamFailureToError({ ...finalized, details: { ...finalized.details, target } }) };
|
|
3663
3821
|
}
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3822
|
+
let restoreError;
|
|
3823
|
+
try {
|
|
3824
|
+
restoreError = await restoreLiveSnapshotPartition(
|
|
3825
|
+
state,
|
|
3826
|
+
entry,
|
|
3827
|
+
indexName,
|
|
3828
|
+
partitionId,
|
|
3829
|
+
target,
|
|
3830
|
+
finalized.snapshot.read(),
|
|
3831
|
+
coordinatorConfig,
|
|
3832
|
+
partitionCount,
|
|
3833
|
+
deadline,
|
|
3834
|
+
deps
|
|
3835
|
+
);
|
|
3836
|
+
} finally {
|
|
3837
|
+
await finalized.snapshot.discard();
|
|
3678
3838
|
}
|
|
3679
|
-
const restoreError = await restoreLiveSnapshotPartition(
|
|
3680
|
-
state,
|
|
3681
|
-
entry,
|
|
3682
|
-
indexName,
|
|
3683
|
-
partitionId,
|
|
3684
|
-
target,
|
|
3685
|
-
finalized.bytes,
|
|
3686
|
-
coordinatorConfig,
|
|
3687
|
-
partitionCount,
|
|
3688
|
-
deadline,
|
|
3689
|
-
deps
|
|
3690
|
-
);
|
|
3691
3839
|
if (restoreError !== null) {
|
|
3692
3840
|
return { ok: false, error: restoreError };
|
|
3693
3841
|
}
|
|
@@ -3711,7 +3859,7 @@ async function applyLiveSnapshotSync(state, entry, indexName, partitionId, targe
|
|
|
3711
3859
|
snapshotHeader: finalized.header
|
|
3712
3860
|
};
|
|
3713
3861
|
}
|
|
3714
|
-
async function restoreLiveSnapshotPartition(state, entry, indexName, partitionId, primaryNodeId,
|
|
3862
|
+
async function restoreLiveSnapshotPartition(state, entry, indexName, partitionId, primaryNodeId, source, coordinatorConfig, partitionCount, deadline, deps) {
|
|
3715
3863
|
const remainingMs = deadline - Date.now();
|
|
3716
3864
|
if (remainingMs <= 0) {
|
|
3717
3865
|
return new NarsilError(
|
|
@@ -3735,7 +3883,7 @@ async function restoreLiveSnapshotPartition(state, entry, indexName, partitionId
|
|
|
3735
3883
|
}
|
|
3736
3884
|
try {
|
|
3737
3885
|
await withDeadline(
|
|
3738
|
-
deps.restoreReplicationPartition(indexName, partitionId,
|
|
3886
|
+
deps.restoreReplicationPartition(indexName, partitionId, source, coordinatorConfig, partitionCount),
|
|
3739
3887
|
remainingMs,
|
|
3740
3888
|
indexName,
|
|
3741
3889
|
"partition-restore"
|
|
@@ -3927,6 +4075,27 @@ async function syncFromAnyTarget(state, entry, indexName, partitionId, primaryNo
|
|
|
3927
4075
|
return { ok: false, error: lastError };
|
|
3928
4076
|
}
|
|
3929
4077
|
async function syncFromTarget(state, entry, indexName, partitionId, target, coordinatorConfig, partitionCount, deadline, deps, abortCheck) {
|
|
4078
|
+
const spoolDirectory = deps.snapshotSpoolDirectory ?? defaultSpoolDirectory();
|
|
4079
|
+
const frameState = createLiveSyncFrameState(indexName, partitionId, spoolDirectory);
|
|
4080
|
+
try {
|
|
4081
|
+
return await syncFrameStateFromTarget(
|
|
4082
|
+
state,
|
|
4083
|
+
entry,
|
|
4084
|
+
indexName,
|
|
4085
|
+
partitionId,
|
|
4086
|
+
target,
|
|
4087
|
+
coordinatorConfig,
|
|
4088
|
+
partitionCount,
|
|
4089
|
+
deadline,
|
|
4090
|
+
deps,
|
|
4091
|
+
abortCheck,
|
|
4092
|
+
frameState
|
|
4093
|
+
);
|
|
4094
|
+
} finally {
|
|
4095
|
+
abandonSnapshotStream(frameState.snapshotState);
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
async function syncFrameStateFromTarget(state, entry, indexName, partitionId, target, coordinatorConfig, partitionCount, deadline, deps, abortCheck, frameState) {
|
|
3930
4099
|
const logState = readLocalLogState(indexName, partitionId, deps);
|
|
3931
4100
|
const requestPayload = {
|
|
3932
4101
|
indexName,
|
|
@@ -3940,7 +4109,6 @@ async function syncFromTarget(state, entry, indexName, partitionId, target, coor
|
|
|
3940
4109
|
requestId: generateId(),
|
|
3941
4110
|
payload: encode(requestPayload)
|
|
3942
4111
|
};
|
|
3943
|
-
const frameState = createLiveSyncFrameState(indexName, partitionId);
|
|
3944
4112
|
const remainingMs = deadline - Date.now();
|
|
3945
4113
|
if (remainingMs <= 0) {
|
|
3946
4114
|
return {
|
|
@@ -4172,7 +4340,7 @@ async function fetchSchemaAndPrepare(entry, indexName, primaryNodeId, deps) {
|
|
|
4172
4340
|
}
|
|
4173
4341
|
return configResult.config.schema;
|
|
4174
4342
|
}
|
|
4175
|
-
async function applyRestore(state, entry, indexName, primaryNodeId,
|
|
4343
|
+
async function applyRestore(state, entry, indexName, primaryNodeId, source, coordinatorSchema, deadline, deps) {
|
|
4176
4344
|
const remainingMs = deadline - Date.now();
|
|
4177
4345
|
if (remainingMs <= 0) {
|
|
4178
4346
|
surfaceError(
|
|
@@ -4191,7 +4359,7 @@ async function applyRestore(state, entry, indexName, primaryNodeId, bytes, coord
|
|
|
4191
4359
|
surfaceAborted(deps, indexName, primaryNodeId);
|
|
4192
4360
|
return false;
|
|
4193
4361
|
}
|
|
4194
|
-
const restoreError = await executeEngineRestore(deps.engine, indexName, primaryNodeId,
|
|
4362
|
+
const restoreError = await executeEngineRestore(deps.engine, indexName, primaryNodeId, source, remainingMs);
|
|
4195
4363
|
if (restoreError !== null) {
|
|
4196
4364
|
surfaceError(deps, indexName, primaryNodeId, restoreError);
|
|
4197
4365
|
return false;
|
|
@@ -4312,7 +4480,11 @@ async function executeBootstrapSync(state, entry, indexName, partitionId, primar
|
|
|
4312
4480
|
surfaceError(deps, indexName, primaryNodeId, targetsResult);
|
|
4313
4481
|
return false;
|
|
4314
4482
|
}
|
|
4315
|
-
const fetchDeps = {
|
|
4483
|
+
const fetchDeps = {
|
|
4484
|
+
transport: deps.transport,
|
|
4485
|
+
sourceNodeId: deps.sourceNodeId,
|
|
4486
|
+
spoolDirectory: deps.snapshotSpoolDirectory
|
|
4487
|
+
};
|
|
4316
4488
|
const fetchResult = await fetchSnapshotFromAnyTarget(
|
|
4317
4489
|
indexName,
|
|
4318
4490
|
primaryNodeId,
|
|
@@ -4339,20 +4511,25 @@ async function executeBootstrapSync(state, entry, indexName, partitionId, primar
|
|
|
4339
4511
|
);
|
|
4340
4512
|
return false;
|
|
4341
4513
|
}
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4514
|
+
let restoreSucceeded;
|
|
4515
|
+
try {
|
|
4516
|
+
if (abortCheck()) {
|
|
4517
|
+
surfaceAborted(deps, indexName, primaryNodeId);
|
|
4518
|
+
return false;
|
|
4519
|
+
}
|
|
4520
|
+
restoreSucceeded = await applyRestore(
|
|
4521
|
+
state,
|
|
4522
|
+
entry,
|
|
4523
|
+
indexName,
|
|
4524
|
+
primaryNodeId,
|
|
4525
|
+
fetchResult.snapshot.read(),
|
|
4526
|
+
schemaResult,
|
|
4527
|
+
deadline,
|
|
4528
|
+
deps
|
|
4529
|
+
);
|
|
4530
|
+
} finally {
|
|
4531
|
+
await fetchResult.snapshot.discard();
|
|
4345
4532
|
}
|
|
4346
|
-
const restoreSucceeded = await applyRestore(
|
|
4347
|
-
state,
|
|
4348
|
-
entry,
|
|
4349
|
-
indexName,
|
|
4350
|
-
primaryNodeId,
|
|
4351
|
-
fetchResult.bytes,
|
|
4352
|
-
schemaResult,
|
|
4353
|
-
deadline,
|
|
4354
|
-
deps
|
|
4355
|
-
);
|
|
4356
4533
|
if (!restoreSucceeded) {
|
|
4357
4534
|
return false;
|
|
4358
4535
|
}
|
|
@@ -5216,173 +5393,40 @@ async function applyLocalReplicationEntry(core, entry) {
|
|
|
5216
5393
|
}
|
|
5217
5394
|
}
|
|
5218
5395
|
|
|
5219
|
-
// src/distribution/cluster-node/
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
const runnableConfig = await resolveRunnableConfig(config);
|
|
5223
|
-
const core = createEngineCore(runnableConfig, hooks, "partition");
|
|
5224
|
-
await startEngineCore(core, runnableConfig);
|
|
5225
|
-
core.orchestrator.shareMainThread();
|
|
5226
|
-
const engine = createNarsilFromCore(core, runnableConfig);
|
|
5227
|
-
const heldPartitions = createHeldPartitionRecord(core);
|
|
5228
|
-
return Object.assign(engine, {
|
|
5229
|
-
createIndex: (name, indexConfig) => createEngineIndex(core, runnableConfig, name, indexConfig, { adapterBinding: "deferred" }),
|
|
5230
|
-
createIndexWithUuid: (name, indexConfig, indexUuid) => createEngineIndex(core, runnableConfig, name, indexConfig, { indexUuid }),
|
|
5231
|
-
acquireIndexForReplication: (indexName) => core.indexState.acquire(indexName, false),
|
|
5232
|
-
indexUuidOf: (indexName) => core.indexRegistry.get(indexName)?.indexUuid,
|
|
5233
|
-
stampIndexUuid: (indexName, indexUuid) => stampIndexUuid(core, indexName, indexUuid),
|
|
5234
|
-
highestPersistedSeqNoOf: (indexName, partitionId) => core.durability?.manager.highestPersistedSeqNo(indexName, partitionId) ?? 0,
|
|
5235
|
-
heldPartitionsOf: (indexName) => heldPartitions.held(indexName),
|
|
5236
|
-
recordHeldPartition: (indexName, partitionId) => heldPartitions.record(indexName, partitionId),
|
|
5237
|
-
forgetHeldPartition: (indexName, partitionId) => heldPartitions.forget(indexName, partitionId),
|
|
5238
|
-
applyReplicationEntry: (entry) => applyLocalReplicationEntry(core, entry),
|
|
5239
|
-
serializeReplicationPartition: (indexName, partitionId) => serializeReplicationPartition(core, indexName, partitionId),
|
|
5240
|
-
restoreReplicationPartition: (indexName, partitionId, bytes, indexConfig, partitionCount) => restoreReplicationPartition(core, engine, indexName, partitionId, bytes, indexConfig, partitionCount),
|
|
5241
|
-
queryPartitions: (indexName, params, partitionIds, globalStats) => runEngineQuery(core, indexName, params, { partitionIds, globalStats }),
|
|
5242
|
-
preflightPartitions: (indexName, params, partitionIds) => runEnginePreflight(core, indexName, params, { partitionIds }),
|
|
5243
|
-
suggestPartitions: (indexName, params, partitionIds) => runEngineSuggest(core, indexName, params, partitionIds),
|
|
5244
|
-
listPartitions: (indexName, params, partitionIds) => runEngineListDocuments(core, indexName, params, partitionIds),
|
|
5245
|
-
collectQueryStats: (indexName, terms, partitionIds) => runEngineQueryStats(core, indexName, terms, partitionIds),
|
|
5246
|
-
partitionStatsForRead: async (indexName) => {
|
|
5247
|
-
const release = await core.indexState.acquire(indexName);
|
|
5248
|
-
try {
|
|
5249
|
-
return core.requireManager(indexName).getPartitionStats();
|
|
5250
|
-
} finally {
|
|
5251
|
-
release();
|
|
5252
|
-
}
|
|
5253
|
-
}
|
|
5254
|
-
});
|
|
5255
|
-
}
|
|
5256
|
-
async function stampIndexUuid(core, indexName, indexUuid) {
|
|
5257
|
-
const entry = core.indexRegistry.get(indexName);
|
|
5258
|
-
if (entry === void 0 || entry.indexUuid === indexUuid) {
|
|
5259
|
-
return;
|
|
5260
|
-
}
|
|
5261
|
-
core.indexRegistry.set(indexName, { ...entry, indexUuid });
|
|
5262
|
-
if (core.durability !== null) {
|
|
5263
|
-
await core.durability.manager.persistMetadata(indexName);
|
|
5264
|
-
}
|
|
5265
|
-
}
|
|
5266
|
-
async function serializeReplicationPartition(core, indexName, partitionId) {
|
|
5267
|
-
core.guardShutdown();
|
|
5268
|
-
const release = await core.indexState.acquire(indexName, false);
|
|
5269
|
-
try {
|
|
5270
|
-
return core.requireManager(indexName).serializePartitionToBytes(partitionId);
|
|
5271
|
-
} finally {
|
|
5272
|
-
release();
|
|
5273
|
-
}
|
|
5396
|
+
// src/distribution/cluster-node/replication-index.ts
|
|
5397
|
+
function isNestedSchema(value) {
|
|
5398
|
+
return typeof value === "object" && value !== null;
|
|
5274
5399
|
}
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
partition = deserializePayloadV2(bytes);
|
|
5282
|
-
} catch (err) {
|
|
5283
|
-
const cause = err instanceof Error ? err.message : String(err);
|
|
5284
|
-
throw new NarsilError(ErrorCodes.SNAPSHOT_SYNC_RESTORE_FAILED, `partition snapshot decode failed: ${cause}`, {
|
|
5285
|
-
indexName,
|
|
5286
|
-
partitionId,
|
|
5287
|
-
cause
|
|
5288
|
-
});
|
|
5289
|
-
}
|
|
5290
|
-
validatePartitionSnapshotPayload(indexName, partitionId, partitionCount, schema, partition);
|
|
5291
|
-
const trackPositions = core.indexRegistry.get(indexName)?.config.trackPositions ?? true;
|
|
5292
|
-
createPartitionIndex(partitionId, trackPositions).deserialize(partition, schema);
|
|
5293
|
-
let release = core.indexRegistry.has(indexName) ? await core.indexState.acquire(indexName, false) : null;
|
|
5294
|
-
try {
|
|
5295
|
-
const restoreIndex = await ensureReplicationIndex(
|
|
5296
|
-
core,
|
|
5297
|
-
engine,
|
|
5298
|
-
indexName,
|
|
5299
|
-
indexConfig,
|
|
5300
|
-
partition.language,
|
|
5301
|
-
partitionCount
|
|
5302
|
-
);
|
|
5303
|
-
if (release === null) release = await core.indexState.acquire(indexName, false);
|
|
5304
|
-
if (restoreIndex.created) {
|
|
5305
|
-
await core.orchestrator.replicateToWorkers({
|
|
5306
|
-
type: "createIndex",
|
|
5307
|
-
indexName,
|
|
5308
|
-
config: restoreIndex.config,
|
|
5309
|
-
requestId: `replicate-partition-index-${indexName}`
|
|
5310
|
-
});
|
|
5400
|
+
function flattenSchemaInto(schema, prefix, flat) {
|
|
5401
|
+
for (const [field, value] of Object.entries(schema)) {
|
|
5402
|
+
const path = prefix.length === 0 ? field : `${prefix}.${field}`;
|
|
5403
|
+
if (isNestedSchema(value)) {
|
|
5404
|
+
flattenSchemaInto(value, path, flat);
|
|
5405
|
+
continue;
|
|
5311
5406
|
}
|
|
5312
|
-
|
|
5313
|
-
manager.deserializePartition(partitionId, partition);
|
|
5314
|
-
await core.orchestrator.replicateToWorkers({
|
|
5315
|
-
type: "deserialize",
|
|
5316
|
-
indexName,
|
|
5317
|
-
partitionId,
|
|
5318
|
-
data: partition,
|
|
5319
|
-
requestId: `replicate-partition-restore-${indexName}-${partitionId}`
|
|
5320
|
-
});
|
|
5321
|
-
} finally {
|
|
5322
|
-
release?.();
|
|
5407
|
+
flat[path] = value;
|
|
5323
5408
|
}
|
|
5324
5409
|
}
|
|
5325
|
-
function
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
partitionId
|
|
5330
|
-
});
|
|
5331
|
-
}
|
|
5332
|
-
if (!Number.isInteger(partitionCount) || partitionCount <= 0 || partitionCount > MAX_PARTITION_COUNT) {
|
|
5333
|
-
throw new NarsilError(
|
|
5334
|
-
ErrorCodes.SNAPSHOT_SYNC_RESTORE_FAILED,
|
|
5335
|
-
`partitionCount must be an integer between 1 and ${MAX_PARTITION_COUNT}`,
|
|
5336
|
-
{ indexName, partitionCount }
|
|
5337
|
-
);
|
|
5338
|
-
}
|
|
5339
|
-
if (partitionId >= partitionCount) {
|
|
5340
|
-
throw new NarsilError(ErrorCodes.SNAPSHOT_SYNC_RESTORE_FAILED, "partitionId is outside partitionCount", {
|
|
5341
|
-
indexName,
|
|
5342
|
-
partitionId,
|
|
5343
|
-
partitionCount
|
|
5344
|
-
});
|
|
5345
|
-
}
|
|
5410
|
+
function flattenSchema(schema) {
|
|
5411
|
+
const flat = {};
|
|
5412
|
+
flattenSchemaInto(schema, "", flat);
|
|
5413
|
+
return flat;
|
|
5346
5414
|
}
|
|
5347
|
-
function
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
receivedIndexName: partition.indexName
|
|
5352
|
-
});
|
|
5353
|
-
}
|
|
5354
|
-
if (partition.partitionId !== partitionId) {
|
|
5355
|
-
throw new NarsilError(ErrorCodes.SNAPSHOT_SYNC_RESTORE_FAILED, "partition snapshot partitionId mismatch", {
|
|
5356
|
-
indexName,
|
|
5357
|
-
expectedPartitionId: partitionId,
|
|
5358
|
-
receivedPartitionId: partition.partitionId
|
|
5359
|
-
});
|
|
5360
|
-
}
|
|
5361
|
-
if (partition.totalPartitions !== partitionCount) {
|
|
5362
|
-
throw new NarsilError(ErrorCodes.SNAPSHOT_SYNC_RESTORE_FAILED, "partition snapshot partition count mismatch", {
|
|
5363
|
-
indexName,
|
|
5364
|
-
partitionId,
|
|
5365
|
-
expectedPartitionCount: partitionCount,
|
|
5366
|
-
receivedPartitionCount: partition.totalPartitions
|
|
5367
|
-
});
|
|
5368
|
-
}
|
|
5369
|
-
const expectedSchema = flattenSchema(schema);
|
|
5370
|
-
for (const [field, expectedType] of Object.entries(expectedSchema)) {
|
|
5371
|
-
if (partition.schema[field] !== expectedType) {
|
|
5372
|
-
throw new NarsilError(ErrorCodes.SNAPSHOT_SYNC_RESTORE_FAILED, "partition snapshot schema mismatch", {
|
|
5415
|
+
function requireSameFlatSchema(indexName, expected, actual, what) {
|
|
5416
|
+
for (const [field, expectedType] of Object.entries(expected)) {
|
|
5417
|
+
if (actual[field] !== expectedType) {
|
|
5418
|
+
throw new NarsilError(ErrorCodes.SNAPSHOT_SYNC_RESTORE_FAILED, `${what} schema mismatch`, {
|
|
5373
5419
|
indexName,
|
|
5374
|
-
partitionId,
|
|
5375
5420
|
field,
|
|
5376
5421
|
expected: expectedType,
|
|
5377
|
-
received:
|
|
5422
|
+
received: actual[field] ?? "(absent)"
|
|
5378
5423
|
});
|
|
5379
5424
|
}
|
|
5380
5425
|
}
|
|
5381
|
-
for (const field of Object.keys(
|
|
5382
|
-
if (!(field in
|
|
5383
|
-
throw new NarsilError(ErrorCodes.SNAPSHOT_SYNC_RESTORE_FAILED,
|
|
5426
|
+
for (const field of Object.keys(actual)) {
|
|
5427
|
+
if (!(field in expected)) {
|
|
5428
|
+
throw new NarsilError(ErrorCodes.SNAPSHOT_SYNC_RESTORE_FAILED, `${what} schema contains extra field`, {
|
|
5384
5429
|
indexName,
|
|
5385
|
-
partitionId,
|
|
5386
5430
|
field
|
|
5387
5431
|
});
|
|
5388
5432
|
}
|
|
@@ -5414,7 +5458,12 @@ async function ensureReplicationIndex(core, engine, indexName, coordinatorConfig
|
|
|
5414
5458
|
receivedLanguage: language.name
|
|
5415
5459
|
});
|
|
5416
5460
|
}
|
|
5417
|
-
|
|
5461
|
+
requireSameFlatSchema(
|
|
5462
|
+
indexName,
|
|
5463
|
+
flattenSchema(coordinatorConfig.schema),
|
|
5464
|
+
flattenSchema(entry.config.schema),
|
|
5465
|
+
"local index"
|
|
5466
|
+
);
|
|
5418
5467
|
const manager = core.requireManager(indexName);
|
|
5419
5468
|
if (manager.partitionCount > partitionCount) {
|
|
5420
5469
|
throw new NarsilError(ErrorCodes.SNAPSHOT_SYNC_RESTORE_FAILED, "local index has more partitions than allocation", {
|
|
@@ -5433,45 +5482,222 @@ async function ensureReplicationIndex(core, engine, indexName, coordinatorConfig
|
|
|
5433
5482
|
}
|
|
5434
5483
|
return { config: entry.config, created };
|
|
5435
5484
|
}
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5485
|
+
|
|
5486
|
+
// src/distribution/cluster-node/partition-archive.ts
|
|
5487
|
+
var VECTOR_FILE_ENVELOPE_VERSION = 2;
|
|
5488
|
+
async function archiveReplicationPartition(core, indexName, partitionId, lastSeqNo) {
|
|
5489
|
+
core.guardShutdown();
|
|
5490
|
+
const release = await core.indexState.acquire(indexName, false);
|
|
5491
|
+
try {
|
|
5492
|
+
const entry = core.requireIndex(indexName);
|
|
5493
|
+
const manager = core.requireManager(indexName);
|
|
5494
|
+
let captured = 0;
|
|
5495
|
+
const checkpoint = await buildArchiveFromMemory({
|
|
5496
|
+
indexName,
|
|
5497
|
+
metadata: indexMetadataOf(indexName, entry, manager.partitionCount),
|
|
5498
|
+
manager,
|
|
5499
|
+
vectorIndexes: manager.getVectorIndexes(),
|
|
5500
|
+
appliedSeqNo: () => {
|
|
5501
|
+
captured = lastSeqNo();
|
|
5502
|
+
return captured;
|
|
5503
|
+
},
|
|
5504
|
+
partitionId
|
|
5505
|
+
});
|
|
5506
|
+
const config = await encodeIndexConfiguration(entry);
|
|
5507
|
+
return { stream: archiveStream({ ...checkpoint, config, sourceIndexName: indexName }), lastSeqNo: captured };
|
|
5508
|
+
} finally {
|
|
5509
|
+
release();
|
|
5510
|
+
}
|
|
5511
|
+
}
|
|
5512
|
+
function restoreFailed(message, details) {
|
|
5513
|
+
throw new NarsilError(ErrorCodes.SNAPSHOT_SYNC_RESTORE_FAILED, message, details);
|
|
5514
|
+
}
|
|
5515
|
+
function requireRestoreTarget(indexName, partitionId, partitionCount) {
|
|
5516
|
+
if (!Number.isInteger(partitionId) || partitionId < 0) {
|
|
5517
|
+
restoreFailed("partitionId must be a non-negative integer", { indexName, partitionId });
|
|
5518
|
+
}
|
|
5519
|
+
if (!Number.isInteger(partitionCount) || partitionCount <= 0 || partitionCount > MAX_PARTITION_COUNT) {
|
|
5520
|
+
restoreFailed(`partitionCount must be an integer between 1 and ${MAX_PARTITION_COUNT}`, {
|
|
5521
|
+
indexName,
|
|
5522
|
+
partitionCount
|
|
5523
|
+
});
|
|
5524
|
+
}
|
|
5525
|
+
if (partitionId >= partitionCount) {
|
|
5526
|
+
restoreFailed("partitionId is outside partitionCount", { indexName, partitionId, partitionCount });
|
|
5527
|
+
}
|
|
5528
|
+
}
|
|
5529
|
+
async function receiveArchive(indexName, partitionId, source, schema) {
|
|
5530
|
+
const store = memoryCheckpointStore();
|
|
5531
|
+
let language = "";
|
|
5532
|
+
let received = null;
|
|
5533
|
+
await readSnapshotArchive(createByteReader(source), {
|
|
5534
|
+
config: async (bytes) => {
|
|
5535
|
+
const { payload } = await openChecksummedEnvelope(bytes, {
|
|
5536
|
+
envelopeFormatVersion: 5,
|
|
5537
|
+
what: "index configuration",
|
|
5538
|
+
allowsCompression: true
|
|
5446
5539
|
});
|
|
5540
|
+
const raw = decode(payload);
|
|
5541
|
+
if (typeof raw.language !== "string") restoreFailed("partition archive names no language", { indexName });
|
|
5542
|
+
language = raw.language;
|
|
5543
|
+
},
|
|
5544
|
+
manifest: async (manifest, sourceName) => {
|
|
5545
|
+
const listed = manifest.partitions.map((partition) => partition.partitionId);
|
|
5546
|
+
if (listed.length !== 1 || listed[0] !== partitionId) {
|
|
5547
|
+
restoreFailed("partition archive lists other partitions", { indexName, partitionId, listed });
|
|
5548
|
+
}
|
|
5549
|
+
if (manifest.vectors.some((field) => field.graphParts.length > 0)) {
|
|
5550
|
+
restoreFailed("partition archive holds a graph", { indexName, partitionId });
|
|
5551
|
+
}
|
|
5552
|
+
requireSameFlatSchema(indexName, schema, manifest.schema, "partition snapshot");
|
|
5553
|
+
const rename = (key) => `${indexName}/${key.slice(sourceName.length + 1)}`;
|
|
5554
|
+
received = {
|
|
5555
|
+
...manifest,
|
|
5556
|
+
partitions: manifest.partitions.map((partition) => ({
|
|
5557
|
+
...partition,
|
|
5558
|
+
segments: partition.segments.map((segment) => ({ ...segment, key: rename(segment.key) }))
|
|
5559
|
+
})),
|
|
5560
|
+
vectors: manifest.vectors.map((field) => ({
|
|
5561
|
+
...field,
|
|
5562
|
+
files: field.files.map((file) => ({ ...file, key: rename(file.key) }))
|
|
5563
|
+
}))
|
|
5564
|
+
};
|
|
5565
|
+
},
|
|
5566
|
+
file: async (name, _ref, bytes) => store.write(`${indexName}/${name}`, [bytes])
|
|
5567
|
+
});
|
|
5568
|
+
if (received === null) restoreFailed("partition archive holds no manifest", { indexName, partitionId });
|
|
5569
|
+
return { language, manifest: received, store };
|
|
5570
|
+
}
|
|
5571
|
+
function emptyPartition(manager, partitionId) {
|
|
5572
|
+
return {
|
|
5573
|
+
indexName: manager.indexName,
|
|
5574
|
+
partitionId,
|
|
5575
|
+
totalPartitions: manager.partitionCount,
|
|
5576
|
+
language: manager.language.name,
|
|
5577
|
+
schema: {},
|
|
5578
|
+
docCount: 0,
|
|
5579
|
+
avgDocLength: 0,
|
|
5580
|
+
documents: {},
|
|
5581
|
+
invertedIndex: {},
|
|
5582
|
+
fieldIndexes: { numeric: {}, boolean: {}, enum: {}, geopoint: {} },
|
|
5583
|
+
statistics: { totalDocuments: 0, totalFieldLengths: {}, averageFieldLengths: {}, docFrequencies: {} }
|
|
5584
|
+
};
|
|
5585
|
+
}
|
|
5586
|
+
async function replacePartition(manager, partitionId, archive) {
|
|
5587
|
+
const vectorIndexes = manager.getVectorIndexes();
|
|
5588
|
+
const held = [...manager.getPartition(partitionId).docIds()];
|
|
5589
|
+
for (const vectorIndex of vectorIndexes.values()) {
|
|
5590
|
+
for (const docId of held) if (vectorIndex.has(docId)) vectorIndex.remove(docId);
|
|
5591
|
+
}
|
|
5592
|
+
manager.deserializePartition(partitionId, emptyPartition(manager, partitionId));
|
|
5593
|
+
const entry = archive.manifest.partitions[0];
|
|
5594
|
+
if (entry !== void 0) {
|
|
5595
|
+
await loadPartitionSegments(archive.store, entry, manager, {
|
|
5596
|
+
schema: archive.manifest.schema,
|
|
5597
|
+
decodesEveryDocument: true
|
|
5598
|
+
});
|
|
5599
|
+
}
|
|
5600
|
+
for (const field of archive.manifest.vectors) {
|
|
5601
|
+
const vectorIndex = vectorIndexes.get(field.fieldPath);
|
|
5602
|
+
if (vectorIndex === void 0) continue;
|
|
5603
|
+
for (const file of field.files) {
|
|
5604
|
+
const bytes = await readListedFile(archive.store, file.key, { bytes: file.bytes, what: "vector file" });
|
|
5605
|
+
const { payload } = await openChecksummedEnvelope(bytes, {
|
|
5606
|
+
envelopeFormatVersion: VECTOR_FILE_ENVELOPE_VERSION,
|
|
5607
|
+
what: `vector file "${file.key}"`,
|
|
5608
|
+
allowsCompression: false
|
|
5609
|
+
});
|
|
5610
|
+
const decoded = decodeVectorFilePayload(decode(payload));
|
|
5611
|
+
const vectors = bytesToVectors(decoded.vectors);
|
|
5612
|
+
for (let i = 0; i < decoded.docIds.length; i++) {
|
|
5613
|
+
if (isDead(file.dead, i)) continue;
|
|
5614
|
+
const vector = vectors.slice(i * decoded.dimension, (i + 1) * decoded.dimension);
|
|
5615
|
+
vectorIndex.insert(decoded.docIds[i], vector, partitionId);
|
|
5616
|
+
}
|
|
5447
5617
|
}
|
|
5618
|
+
vectorIndex.scheduleBuild();
|
|
5448
5619
|
}
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5620
|
+
}
|
|
5621
|
+
async function restoreReplicationPartition(core, engine, indexName, partitionId, source, indexConfig, partitionCount) {
|
|
5622
|
+
core.guardShutdown();
|
|
5623
|
+
requireRestoreTarget(indexName, partitionId, partitionCount);
|
|
5624
|
+
const archive = await receiveArchive(indexName, partitionId, source, flattenSchema(indexConfig.schema));
|
|
5625
|
+
let release = core.indexRegistry.has(indexName) ? await core.indexState.acquire(indexName, false) : null;
|
|
5626
|
+
try {
|
|
5627
|
+
const restoreIndex = await ensureReplicationIndex(
|
|
5628
|
+
core,
|
|
5629
|
+
engine,
|
|
5630
|
+
indexName,
|
|
5631
|
+
indexConfig,
|
|
5632
|
+
archive.language,
|
|
5633
|
+
partitionCount
|
|
5634
|
+
);
|
|
5635
|
+
if (release === null) release = await core.indexState.acquire(indexName, false);
|
|
5636
|
+
if (restoreIndex.created) {
|
|
5637
|
+
await core.orchestrator.replicateToWorkers({
|
|
5638
|
+
type: "createIndex",
|
|
5452
5639
|
indexName,
|
|
5453
|
-
|
|
5640
|
+
config: restoreIndex.config,
|
|
5641
|
+
requestId: `replicate-partition-index-${indexName}`
|
|
5454
5642
|
});
|
|
5455
5643
|
}
|
|
5644
|
+
const wasScaledOut = core.orchestrator.desyncIndex(indexName);
|
|
5645
|
+
try {
|
|
5646
|
+
await replacePartition(core.requireManager(indexName), partitionId, archive);
|
|
5647
|
+
} finally {
|
|
5648
|
+
await core.orchestrator.resyncIndex(indexName, wasScaledOut);
|
|
5649
|
+
}
|
|
5650
|
+
} finally {
|
|
5651
|
+
release?.();
|
|
5456
5652
|
}
|
|
5457
5653
|
}
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5654
|
+
|
|
5655
|
+
// src/distribution/cluster-node/local-engine.ts
|
|
5656
|
+
async function createClusterLocalEngine(config, hooks) {
|
|
5657
|
+
requireKnownConfig(config);
|
|
5658
|
+
const runnableConfig = await resolveRunnableConfig(config);
|
|
5659
|
+
const core = createEngineCore(runnableConfig, hooks, "partition");
|
|
5660
|
+
await startEngineCore(core, runnableConfig);
|
|
5661
|
+
core.orchestrator.shareMainThread();
|
|
5662
|
+
const engine = createNarsilFromCore(core, runnableConfig);
|
|
5663
|
+
const heldPartitions = createHeldPartitionRecord(core);
|
|
5664
|
+
return Object.assign(engine, {
|
|
5665
|
+
createIndex: (name, indexConfig) => createEngineIndex(core, runnableConfig, name, indexConfig, { adapterBinding: "deferred" }),
|
|
5666
|
+
createIndexWithUuid: (name, indexConfig, indexUuid) => createEngineIndex(core, runnableConfig, name, indexConfig, { indexUuid }),
|
|
5667
|
+
acquireIndexForReplication: (indexName) => core.indexState.acquire(indexName, false),
|
|
5668
|
+
indexUuidOf: (indexName) => core.indexRegistry.get(indexName)?.indexUuid,
|
|
5669
|
+
stampIndexUuid: (indexName, indexUuid) => stampIndexUuid(core, indexName, indexUuid),
|
|
5670
|
+
highestPersistedSeqNoOf: (indexName, partitionId) => core.durability?.manager.highestPersistedSeqNo(indexName, partitionId) ?? 0,
|
|
5671
|
+
heldPartitionsOf: (indexName) => heldPartitions.held(indexName),
|
|
5672
|
+
recordHeldPartition: (indexName, partitionId) => heldPartitions.record(indexName, partitionId),
|
|
5673
|
+
forgetHeldPartition: (indexName, partitionId) => heldPartitions.forget(indexName, partitionId),
|
|
5674
|
+
applyReplicationEntry: (entry) => applyLocalReplicationEntry(core, entry),
|
|
5675
|
+
archiveReplicationPartition: (indexName, partitionId, lastSeqNo) => archiveReplicationPartition(core, indexName, partitionId, lastSeqNo),
|
|
5676
|
+
restoreReplicationPartition: (indexName, partitionId, source, indexConfig, partitionCount) => restoreReplicationPartition(core, engine, indexName, partitionId, source, indexConfig, partitionCount),
|
|
5677
|
+
queryPartitions: (indexName, params, partitionIds, globalStats) => runEngineQuery(core, indexName, params, { partitionIds, globalStats }),
|
|
5678
|
+
preflightPartitions: (indexName, params, partitionIds) => runEnginePreflight(core, indexName, params, { partitionIds }),
|
|
5679
|
+
suggestPartitions: (indexName, params, partitionIds) => runEngineSuggest(core, indexName, params, partitionIds),
|
|
5680
|
+
listPartitions: (indexName, params, partitionIds) => runEngineListDocuments(core, indexName, params, partitionIds),
|
|
5681
|
+
collectQueryStats: (indexName, terms, partitionIds) => runEngineQueryStats(core, indexName, terms, partitionIds),
|
|
5682
|
+
partitionStatsForRead: async (indexName) => {
|
|
5683
|
+
const release = await core.indexState.acquire(indexName);
|
|
5684
|
+
try {
|
|
5685
|
+
return core.requireManager(indexName).getPartitionStats();
|
|
5686
|
+
} finally {
|
|
5687
|
+
release();
|
|
5688
|
+
}
|
|
5469
5689
|
}
|
|
5470
|
-
|
|
5471
|
-
}
|
|
5690
|
+
});
|
|
5472
5691
|
}
|
|
5473
|
-
function
|
|
5474
|
-
|
|
5692
|
+
async function stampIndexUuid(core, indexName, indexUuid) {
|
|
5693
|
+
const entry = core.indexRegistry.get(indexName);
|
|
5694
|
+
if (entry === void 0 || entry.indexUuid === indexUuid) {
|
|
5695
|
+
return;
|
|
5696
|
+
}
|
|
5697
|
+
core.indexRegistry.set(indexName, { ...entry, indexUuid });
|
|
5698
|
+
if (core.durability !== null) {
|
|
5699
|
+
await core.durability.manager.persistMetadata(indexName);
|
|
5700
|
+
}
|
|
5475
5701
|
}
|
|
5476
5702
|
|
|
5477
5703
|
// src/distribution/cluster-node/local-index-reconcile.ts
|
|
@@ -5584,6 +5810,25 @@ async function authorizeSnapshotRequest(coordinator, indexName, sourceId) {
|
|
|
5584
5810
|
}
|
|
5585
5811
|
|
|
5586
5812
|
// src/distribution/cluster-node/snapshot-cache.ts
|
|
5813
|
+
function soleSnapshotLease(build) {
|
|
5814
|
+
return { build, release: () => build.discard() };
|
|
5815
|
+
}
|
|
5816
|
+
function sharedLease(entry, build) {
|
|
5817
|
+
let released = false;
|
|
5818
|
+
return {
|
|
5819
|
+
build,
|
|
5820
|
+
async release() {
|
|
5821
|
+
if (released) return;
|
|
5822
|
+
released = true;
|
|
5823
|
+
entry.holders -= 1;
|
|
5824
|
+
if (entry.holders === 0) await build.discard();
|
|
5825
|
+
}
|
|
5826
|
+
};
|
|
5827
|
+
}
|
|
5828
|
+
async function joinSnapshotBuild(entry) {
|
|
5829
|
+
entry.holders += 1;
|
|
5830
|
+
return sharedLease(entry, await entry.promise);
|
|
5831
|
+
}
|
|
5587
5832
|
function createSnapshotCacheState(maxConcurrent = DEFAULT_MAX_CONCURRENT_SNAPSHOTS, maxPerSource = DEFAULT_MAX_PER_SOURCE_SNAPSHOTS, maxStreamsPerIndex = DEFAULT_MAX_STREAMS_PER_INDEX) {
|
|
5588
5833
|
if (!Number.isInteger(maxConcurrent) || maxConcurrent <= 0) {
|
|
5589
5834
|
throw new NarsilError(ErrorCodes.CONFIG_INVALID, "maxConcurrent must be a positive integer", {
|
|
@@ -5671,7 +5916,7 @@ function releaseStreamSlot(state, handle) {
|
|
|
5671
5916
|
async function acquireSnapshotBuild(state, indexName, builder) {
|
|
5672
5917
|
const existing = state.cache.get(indexName);
|
|
5673
5918
|
if (existing !== void 0) {
|
|
5674
|
-
return existing
|
|
5919
|
+
return joinSnapshotBuild(existing);
|
|
5675
5920
|
}
|
|
5676
5921
|
if (state.active >= state.maxConcurrent) {
|
|
5677
5922
|
throw new NarsilError(
|
|
@@ -5694,7 +5939,7 @@ async function acquireSnapshotBuild(state, indexName, builder) {
|
|
|
5694
5939
|
state.active = Math.max(0, state.active - 1);
|
|
5695
5940
|
};
|
|
5696
5941
|
const rawBuild = (async () => builder())();
|
|
5697
|
-
const entry = { promise: rawBuild };
|
|
5942
|
+
const entry = { promise: rawBuild, holders: 0 };
|
|
5698
5943
|
entry.promise = rawBuild.then(
|
|
5699
5944
|
(result) => {
|
|
5700
5945
|
clearEntry(entry);
|
|
@@ -5706,7 +5951,7 @@ async function acquireSnapshotBuild(state, indexName, builder) {
|
|
|
5706
5951
|
}
|
|
5707
5952
|
);
|
|
5708
5953
|
state.cache.set(indexName, entry);
|
|
5709
|
-
return entry
|
|
5954
|
+
return joinSnapshotBuild(entry);
|
|
5710
5955
|
}
|
|
5711
5956
|
var SNAPSHOT_SYNC_ERROR_TYPE = `${ReplicationMessageTypes.SNAPSHOT_SYNC_REQUEST}.error`;
|
|
5712
5957
|
function createSingleResponseSink(respond) {
|
|
@@ -5720,8 +5965,7 @@ function createSingleResponseSink(respond) {
|
|
|
5720
5965
|
return sink;
|
|
5721
5966
|
}
|
|
5722
5967
|
async function streamSnapshotToReplica(sink, nodeId, requestId, indexName, build, metadata, options = {}) {
|
|
5723
|
-
const
|
|
5724
|
-
const totalBytes = snapshotBytes.byteLength;
|
|
5968
|
+
const totalBytes = build.totalBytes;
|
|
5725
5969
|
const checksum = build.checksum;
|
|
5726
5970
|
const header = {
|
|
5727
5971
|
lastSeqNo: metadata.lastSeqNo,
|
|
@@ -5737,22 +5981,23 @@ async function streamSnapshotToReplica(sink, nodeId, requestId, indexName, build
|
|
|
5737
5981
|
await yieldToEventLoop();
|
|
5738
5982
|
let offset = 0;
|
|
5739
5983
|
let lastYieldAt = now2();
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5984
|
+
for await (const source of build.read()) {
|
|
5985
|
+
for (let from = 0; from < source.byteLength; from += SNAPSHOT_CHUNK_SIZE) {
|
|
5986
|
+
const chunk = source.subarray(from, Math.min(from + SNAPSHOT_CHUNK_SIZE, source.byteLength));
|
|
5987
|
+
const chunkPayload = {
|
|
5988
|
+
partitionId: metadata.partitionId,
|
|
5989
|
+
indexName,
|
|
5990
|
+
offset,
|
|
5991
|
+
data: chunk
|
|
5992
|
+
};
|
|
5993
|
+
const chunkBytes = encode(chunkPayload);
|
|
5994
|
+
assertMessageWithinLimit(chunkBytes, "SNAPSHOT_CHUNK");
|
|
5995
|
+
await respondMessage(sink, ReplicationMessageTypes.SNAPSHOT_CHUNK, nodeId, requestId, chunkBytes);
|
|
5996
|
+
offset += chunk.byteLength;
|
|
5997
|
+
if (offset < totalBytes && now2() - lastYieldAt >= SNAPSHOT_CHUNK_YIELD_INTERVAL_MS) {
|
|
5998
|
+
await yieldToEventLoop();
|
|
5999
|
+
lastYieldAt = now2();
|
|
6000
|
+
}
|
|
5756
6001
|
}
|
|
5757
6002
|
}
|
|
5758
6003
|
await yieldToEventLoop();
|
|
@@ -5833,6 +6078,26 @@ async function defaultSnapshotHeaderMetadataProvider(coordinator, indexName) {
|
|
|
5833
6078
|
lastSeqNo: SNAPSHOT_HEADER_SENTINEL_SEQNO
|
|
5834
6079
|
};
|
|
5835
6080
|
}
|
|
6081
|
+
async function nodeSnapshotHeaderMetadata(deps, indexName, partitionId) {
|
|
6082
|
+
const fallback = await defaultSnapshotHeaderMetadataProvider(deps.coordinator, indexName);
|
|
6083
|
+
if (partitionId === null) {
|
|
6084
|
+
return fallback;
|
|
6085
|
+
}
|
|
6086
|
+
const localIndex = deps.engine.listIndexes().find((index) => index.name === indexName);
|
|
6087
|
+
const release = localIndex !== void 0 ? await deps.engine.acquireIndexForReplication(indexName) : null;
|
|
6088
|
+
try {
|
|
6089
|
+
const allocation = await deps.coordinator.getAllocation(indexName);
|
|
6090
|
+
const assignment = allocation?.assignments.get(partitionId);
|
|
6091
|
+
const log = deps.getReplicationLog(indexName, partitionId);
|
|
6092
|
+
return {
|
|
6093
|
+
partitionId,
|
|
6094
|
+
primaryTerm: assignment?.primaryTerm ?? fallback.primaryTerm,
|
|
6095
|
+
lastSeqNo: log.newestSeqNo ?? 0
|
|
6096
|
+
};
|
|
6097
|
+
} finally {
|
|
6098
|
+
release?.();
|
|
6099
|
+
}
|
|
6100
|
+
}
|
|
5836
6101
|
async function resolveHeaderMetadata(deps, indexName, partitionId) {
|
|
5837
6102
|
const provider = deps.resolveHeaderMetadata;
|
|
5838
6103
|
if (provider === void 0) {
|
|
@@ -5862,55 +6127,56 @@ async function acquireAndStream(message, sink, request, deps, options) {
|
|
|
5862
6127
|
return;
|
|
5863
6128
|
}
|
|
5864
6129
|
try {
|
|
5865
|
-
let
|
|
6130
|
+
let lease;
|
|
5866
6131
|
try {
|
|
5867
6132
|
const buildForRequest = async () => buildSnapshotForRequest(deps, indexName, options);
|
|
5868
|
-
|
|
6133
|
+
lease = options.disableBuildCache === true ? soleSnapshotLease(await buildForRequest()) : await acquireSnapshotBuild(deps.state, indexName, buildForRequest);
|
|
5869
6134
|
} catch (err) {
|
|
5870
6135
|
const code = err instanceof NarsilError ? err.code : ErrorCodes.SNAPSHOT_SYNC_SNAPSHOT_FAILED;
|
|
5871
6136
|
const errMessage = err instanceof Error ? err.message : String(err);
|
|
5872
6137
|
await respondError(sink, deps.nodeId, message.requestId, code, errMessage);
|
|
5873
6138
|
return;
|
|
5874
6139
|
}
|
|
5875
|
-
if (build.bytes.byteLength > MAX_SNAPSHOT_SIZE_BYTES) {
|
|
5876
|
-
await respondError(
|
|
5877
|
-
sink,
|
|
5878
|
-
deps.nodeId,
|
|
5879
|
-
message.requestId,
|
|
5880
|
-
ErrorCodes.SNAPSHOT_SYNC_TOO_LARGE,
|
|
5881
|
-
`Snapshot size ${build.bytes.byteLength} exceeds the ${MAX_SNAPSHOT_SIZE_BYTES} byte limit`
|
|
5882
|
-
);
|
|
5883
|
-
return;
|
|
5884
|
-
}
|
|
5885
|
-
let streamHandle;
|
|
5886
|
-
try {
|
|
5887
|
-
streamHandle = acquireStreamSlot(deps.state, indexName);
|
|
5888
|
-
} catch (err) {
|
|
5889
|
-
const code = err instanceof NarsilError ? err.code : ErrorCodes.SNAPSHOT_SYNC_CAPACITY_EXHAUSTED;
|
|
5890
|
-
const errMessage = err instanceof Error ? err.message : String(err);
|
|
5891
|
-
await respondError(sink, deps.nodeId, message.requestId, code, errMessage);
|
|
5892
|
-
return;
|
|
5893
|
-
}
|
|
5894
6140
|
try {
|
|
5895
|
-
|
|
5896
|
-
await streamSnapshotToReplica(sink, deps.nodeId, message.requestId, indexName, build, metadata, {
|
|
5897
|
-
closeOnEnd: options.closeOnEnd
|
|
5898
|
-
});
|
|
5899
|
-
await options.afterSnapshot?.(sink);
|
|
5900
|
-
if (options.closeOnEnd === false) {
|
|
5901
|
-
sink.closed = true;
|
|
5902
|
-
}
|
|
6141
|
+
await streamLeasedSnapshot(message, sink, request, deps, options, lease.build);
|
|
5903
6142
|
} finally {
|
|
5904
|
-
|
|
6143
|
+
await lease.release();
|
|
5905
6144
|
}
|
|
5906
6145
|
} finally {
|
|
5907
6146
|
releaseSourceSlot(deps.state, sourceHandle);
|
|
5908
6147
|
}
|
|
5909
6148
|
}
|
|
6149
|
+
async function streamLeasedSnapshot(message, sink, request, deps, options, build) {
|
|
6150
|
+
const { indexName } = request;
|
|
6151
|
+
let streamHandle;
|
|
6152
|
+
try {
|
|
6153
|
+
streamHandle = acquireStreamSlot(deps.state, indexName);
|
|
6154
|
+
} catch (err) {
|
|
6155
|
+
const code = err instanceof NarsilError ? err.code : ErrorCodes.SNAPSHOT_SYNC_CAPACITY_EXHAUSTED;
|
|
6156
|
+
const errMessage = err instanceof Error ? err.message : String(err);
|
|
6157
|
+
await respondError(sink, deps.nodeId, message.requestId, code, errMessage);
|
|
6158
|
+
return;
|
|
6159
|
+
}
|
|
6160
|
+
try {
|
|
6161
|
+
const resolved = options.metadata ?? await resolveHeaderMetadata(deps, indexName, request.partitionId ?? null);
|
|
6162
|
+
const metadata = build.lastSeqNo === void 0 ? resolved : { ...resolved, lastSeqNo: build.lastSeqNo };
|
|
6163
|
+
await streamSnapshotToReplica(sink, deps.nodeId, message.requestId, indexName, build, metadata, {
|
|
6164
|
+
closeOnEnd: options.closeOnEnd
|
|
6165
|
+
});
|
|
6166
|
+
await options.afterSnapshot?.(sink, build);
|
|
6167
|
+
if (options.closeOnEnd === false) {
|
|
6168
|
+
sink.closed = true;
|
|
6169
|
+
}
|
|
6170
|
+
} finally {
|
|
6171
|
+
releaseStreamSlot(deps.state, streamHandle);
|
|
6172
|
+
}
|
|
6173
|
+
}
|
|
5910
6174
|
async function buildSnapshot(deps, indexName) {
|
|
5911
|
-
let bytes;
|
|
5912
6175
|
try {
|
|
5913
|
-
|
|
6176
|
+
return await spoolSnapshotStream(
|
|
6177
|
+
deps.spoolDirectory ?? defaultSpoolDirectory(),
|
|
6178
|
+
await deps.engine.snapshot(indexName)
|
|
6179
|
+
);
|
|
5914
6180
|
} catch (err) {
|
|
5915
6181
|
if (err instanceof NarsilError) {
|
|
5916
6182
|
throw err;
|
|
@@ -5921,8 +6187,6 @@ async function buildSnapshot(deps, indexName) {
|
|
|
5921
6187
|
cause: errMessage
|
|
5922
6188
|
});
|
|
5923
6189
|
}
|
|
5924
|
-
const checksum = bytes.byteLength <= MAX_SNAPSHOT_SIZE_BYTES ? crc32(bytes) : 0;
|
|
5925
|
-
return { bytes, checksum };
|
|
5926
6190
|
}
|
|
5927
6191
|
function buildSnapshotForRequest(deps, indexName, options) {
|
|
5928
6192
|
if (options.buildSnapshot !== void 0) {
|
|
@@ -6131,7 +6395,6 @@ var MAX_FACET_VALUE_LENGTH = 1024;
|
|
|
6131
6395
|
var MAX_GROUPS_PER_RESPONSE = 65536;
|
|
6132
6396
|
var MAX_FACET_SIZE = 1e3;
|
|
6133
6397
|
var DEFAULT_MAX_FACET_BUCKETS = 100;
|
|
6134
|
-
var MAX_VECTOR_DIMENSION = 8192;
|
|
6135
6398
|
var MAX_VECTOR_TEXT_LENGTH = 16384;
|
|
6136
6399
|
|
|
6137
6400
|
// src/distribution/query/validators/common.ts
|
|
@@ -6867,11 +7130,11 @@ function validateVectorParams(value) {
|
|
|
6867
7130
|
);
|
|
6868
7131
|
}
|
|
6869
7132
|
const dims = value.value.length;
|
|
6870
|
-
if (dims === 0 || dims >
|
|
7133
|
+
if (dims === 0 || dims > MAX_STORED_VECTOR_DIMENSION) {
|
|
6871
7134
|
throwInvalid(
|
|
6872
7135
|
CONFIG_INVALID,
|
|
6873
|
-
`Invalid SearchPayload: "params.vector.value" length must be between 1 and ${
|
|
6874
|
-
{ length: dims, limit:
|
|
7136
|
+
`Invalid SearchPayload: "params.vector.value" length must be between 1 and ${MAX_STORED_VECTOR_DIMENSION}`,
|
|
7137
|
+
{ length: dims, limit: MAX_STORED_VECTOR_DIMENSION }
|
|
6875
7138
|
);
|
|
6876
7139
|
}
|
|
6877
7140
|
for (let i = 0; i < dims; i++) {
|
|
@@ -7062,6 +7325,9 @@ function validateParams(params) {
|
|
|
7062
7325
|
if (params.prefix !== null) {
|
|
7063
7326
|
validateBooleanParam(params.prefix, "prefix");
|
|
7064
7327
|
}
|
|
7328
|
+
if (params.partialWords !== null && params.partialWords !== void 0) {
|
|
7329
|
+
validateBooleanParam(params.partialWords, "partialWords");
|
|
7330
|
+
}
|
|
7065
7331
|
if (params.exact !== null) {
|
|
7066
7332
|
validateBooleanParam(params.exact, "exact");
|
|
7067
7333
|
}
|
|
@@ -7671,6 +7937,7 @@ function wireParamsToLocal(wire, facetShardSize) {
|
|
|
7671
7937
|
termMatch: wire.termMatch ?? void 0,
|
|
7672
7938
|
prefixLength: wire.prefixLength ?? void 0,
|
|
7673
7939
|
prefix: wire.prefix ?? void 0,
|
|
7940
|
+
partialWords: wire.partialWords ?? void 0,
|
|
7674
7941
|
exact: wire.exact ?? void 0,
|
|
7675
7942
|
pinned: wire.pinned !== null ? wire.pinned.map((entry) => ({ docId: entry.docId, position: entry.position })) : void 0,
|
|
7676
7943
|
mode: wire.mode ?? void 0,
|
|
@@ -7766,6 +8033,7 @@ function localParamsToWire(params) {
|
|
|
7766
8033
|
termMatch: params.termMatch ?? null,
|
|
7767
8034
|
prefixLength: params.prefixLength ?? null,
|
|
7768
8035
|
prefix: params.prefix ?? null,
|
|
8036
|
+
partialWords: params.partialWords ?? null,
|
|
7769
8037
|
exact: params.exact ?? null,
|
|
7770
8038
|
pinned: params.pinned !== void 0 ? params.pinned.map((entry) => ({ docId: entry.docId, position: entry.position })) : null,
|
|
7771
8039
|
mode: params.mode ?? null,
|
|
@@ -8073,6 +8341,8 @@ function toWireSortValue2(value) {
|
|
|
8073
8341
|
if (typeof value === "boolean") return value;
|
|
8074
8342
|
return null;
|
|
8075
8343
|
}
|
|
8344
|
+
|
|
8345
|
+
// src/distribution/replication/sync-primary.ts
|
|
8076
8346
|
function decideSyncTier(log, replicaLastSeqNo) {
|
|
8077
8347
|
const oldest = log.oldestSeqNo;
|
|
8078
8348
|
if (oldest === void 0) {
|
|
@@ -8194,7 +8464,8 @@ async function handleSyncRequestMessage(message, respond, deps) {
|
|
|
8194
8464
|
engine: deps.engine,
|
|
8195
8465
|
coordinator: deps.coordinator,
|
|
8196
8466
|
state: deps.snapshotSyncState,
|
|
8197
|
-
resolveHeaderMetadata: deps.resolveHeaderMetadata
|
|
8467
|
+
resolveHeaderMetadata: deps.resolveHeaderMetadata,
|
|
8468
|
+
spoolDirectory: deps.snapshotSpoolDirectory
|
|
8198
8469
|
},
|
|
8199
8470
|
{
|
|
8200
8471
|
metadata: {
|
|
@@ -8205,11 +8476,16 @@ async function handleSyncRequestMessage(message, respond, deps) {
|
|
|
8205
8476
|
closeOnEnd: false,
|
|
8206
8477
|
disableBuildCache: true,
|
|
8207
8478
|
buildSnapshot: async () => {
|
|
8208
|
-
const
|
|
8209
|
-
|
|
8479
|
+
const archive = await deps.engine.archiveReplicationPartition(
|
|
8480
|
+
request.indexName,
|
|
8481
|
+
request.partitionId,
|
|
8482
|
+
() => log.localLogEnd
|
|
8483
|
+
);
|
|
8484
|
+
const directory = deps.snapshotSpoolDirectory ?? defaultSpoolDirectory();
|
|
8485
|
+
return { ...await spoolSnapshotStream(directory, archive.stream), lastSeqNo: archive.lastSeqNo };
|
|
8210
8486
|
},
|
|
8211
|
-
afterSnapshot: async (trailingSink) => {
|
|
8212
|
-
const entries = log.getEntriesFrom(snapshotSeqNo + 1);
|
|
8487
|
+
afterSnapshot: async (trailingSink, build) => {
|
|
8488
|
+
const entries = log.getEntriesFrom((build.lastSeqNo ?? snapshotSeqNo) + 1);
|
|
8213
8489
|
await sendSyncEntriesResponse(message, trailingSink, deps, entries);
|
|
8214
8490
|
}
|
|
8215
8491
|
}
|
|
@@ -8325,7 +8601,7 @@ async function forwardInsertToRemote(indexName, document, docId, primaryNodeId,
|
|
|
8325
8601
|
indexName,
|
|
8326
8602
|
documentId: docId,
|
|
8327
8603
|
operation: "insert",
|
|
8328
|
-
document:
|
|
8604
|
+
document: encodeDocument(document),
|
|
8329
8605
|
updateFields: null
|
|
8330
8606
|
};
|
|
8331
8607
|
const message = createForwardMessage(payload, deps.nodeId);
|
|
@@ -8345,7 +8621,7 @@ async function forwardUpdateToRemote(indexName, document, docId, primaryNodeId,
|
|
|
8345
8621
|
indexName,
|
|
8346
8622
|
documentId: docId,
|
|
8347
8623
|
operation: "update",
|
|
8348
|
-
document:
|
|
8624
|
+
document: encodeDocument(document),
|
|
8349
8625
|
updateFields: null
|
|
8350
8626
|
};
|
|
8351
8627
|
const message = createForwardMessage(payload, deps.nodeId);
|
|
@@ -8371,6 +8647,8 @@ async function sendToNode(nodeId, message, deps) {
|
|
|
8371
8647
|
details: { targetNodeId: nodeId }
|
|
8372
8648
|
});
|
|
8373
8649
|
}
|
|
8650
|
+
|
|
8651
|
+
// src/distribution/cluster-node/write-routing/replication.ts
|
|
8374
8652
|
async function assertPrimaryWriteAuthority(entry, deps) {
|
|
8375
8653
|
const table = await deps.coordinator.getAllocation(entry.indexName);
|
|
8376
8654
|
const currentAssignment = table?.assignments.get(entry.partitionId);
|
|
@@ -8400,7 +8678,7 @@ function appendIndexReplicationEntry(indexName, partitionId, assignment, documen
|
|
|
8400
8678
|
partitionId,
|
|
8401
8679
|
indexName,
|
|
8402
8680
|
documentId,
|
|
8403
|
-
document:
|
|
8681
|
+
document: encodeDocument(document)
|
|
8404
8682
|
});
|
|
8405
8683
|
}
|
|
8406
8684
|
function appendDeleteReplicationEntry(indexName, partitionId, assignment, documentId, deps) {
|
|
@@ -8884,7 +9162,7 @@ function toWireOperation(item) {
|
|
|
8884
9162
|
return {
|
|
8885
9163
|
documentId: item.documentId,
|
|
8886
9164
|
operation: item.operation,
|
|
8887
|
-
document: item.document === null ? null :
|
|
9165
|
+
document: item.document === null ? null : encodeDocument(item.document),
|
|
8888
9166
|
updateFields: null
|
|
8889
9167
|
};
|
|
8890
9168
|
}
|
|
@@ -9291,9 +9569,15 @@ function validateCreateIndexOptions(partitionCount, replicationFactor) {
|
|
|
9291
9569
|
async function routeCreateIndex(name, requestedConfig, options, coordinator, engine) {
|
|
9292
9570
|
validateIndexName(name);
|
|
9293
9571
|
const config = { ...requestedConfig, schema: validateSchema(requestedConfig.schema) };
|
|
9572
|
+
requireCreatableSchema(config.schema);
|
|
9573
|
+
requireSurfaceFormsForPartialFields(config.schema, config.surfaceForms);
|
|
9294
9574
|
const partitionCount = options?.partitionCount ?? DEFAULT_PARTITION_COUNT;
|
|
9295
9575
|
const replicationFactor = options?.replicationFactor ?? DEFAULT_REPLICATION_FACTOR;
|
|
9296
9576
|
validateCreateIndexOptions(partitionCount, replicationFactor);
|
|
9577
|
+
requireConfigurationWithinSizeLimit(
|
|
9578
|
+
withPartitionCount(config, partitionCount),
|
|
9579
|
+
getLanguage(config.language ?? "english")
|
|
9580
|
+
);
|
|
9297
9581
|
const constraints = {
|
|
9298
9582
|
zoneAwareness: false,
|
|
9299
9583
|
zoneAttribute: "zone",
|
|
@@ -9574,7 +9858,8 @@ function createDataNodeHandler(deps) {
|
|
|
9574
9858
|
engine: deps.engine,
|
|
9575
9859
|
coordinator: deps.coordinator,
|
|
9576
9860
|
state: deps.snapshotSyncState,
|
|
9577
|
-
resolveHeaderMetadata: deps.resolveHeaderMetadata
|
|
9861
|
+
resolveHeaderMetadata: deps.resolveHeaderMetadata,
|
|
9862
|
+
spoolDirectory: deps.snapshotSpoolDirectory
|
|
9578
9863
|
});
|
|
9579
9864
|
return;
|
|
9580
9865
|
}
|
|
@@ -11529,6 +11814,10 @@ async function createClusterNode(config) {
|
|
|
11529
11814
|
onIndexClose: (indexName) => deleteIndexReplicationLogs(replicationLogs, indexName)
|
|
11530
11815
|
});
|
|
11531
11816
|
localEngine = engine;
|
|
11817
|
+
const snapshotSpool = await openSnapshotSpoolDirectory(config.engine?.durability?.directory).catch(async (err) => {
|
|
11818
|
+
await engine.shutdown();
|
|
11819
|
+
throw err;
|
|
11820
|
+
});
|
|
11532
11821
|
function replicationLogKey2(indexName, partitionId) {
|
|
11533
11822
|
return replicationLogKey(indexName, partitionId);
|
|
11534
11823
|
}
|
|
@@ -11588,7 +11877,8 @@ async function createClusterNode(config) {
|
|
|
11588
11877
|
resetReplicationLog: seedReplicationLog2,
|
|
11589
11878
|
applyReplicationEntry: engine.applyReplicationEntry,
|
|
11590
11879
|
restoreReplicationPartition: engine.restoreReplicationPartition,
|
|
11591
|
-
onError: forwardOnError
|
|
11880
|
+
onError: forwardOnError,
|
|
11881
|
+
snapshotSpoolDirectory: snapshotSpool.path
|
|
11592
11882
|
};
|
|
11593
11883
|
const primaryPartitionDeps = {
|
|
11594
11884
|
engine,
|
|
@@ -11720,6 +12010,7 @@ async function createClusterNode(config) {
|
|
|
11720
12010
|
coordinator: config.coordinator,
|
|
11721
12011
|
writeDeps,
|
|
11722
12012
|
snapshotSyncState: snapshotSyncHandlerState,
|
|
12013
|
+
snapshotSpoolDirectory: snapshotSpool.path,
|
|
11723
12014
|
resolveHeaderMetadata: resolveSnapshotHeaderMetadata,
|
|
11724
12015
|
isBootstrapSynced: (indexName, partitionId) => hasCompletedBootstrapSync(bootstrapSyncState, indexName, partitionId)
|
|
11725
12016
|
});
|
|
@@ -11751,28 +12042,16 @@ async function createClusterNode(config) {
|
|
|
11751
12042
|
}
|
|
11752
12043
|
await Promise.all([...heldPartitionWrites]);
|
|
11753
12044
|
await engine.shutdown();
|
|
12045
|
+
await snapshotSpool.dispose();
|
|
11754
12046
|
}
|
|
11755
12047
|
};
|
|
11756
12048
|
return node;
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
const release = localIndex !== void 0 ? await engine.acquireIndexForReplication(indexName) : null;
|
|
11764
|
-
try {
|
|
11765
|
-
const allocation = await config.coordinator.getAllocation(indexName);
|
|
11766
|
-
const assignment = allocation?.assignments.get(partitionId);
|
|
11767
|
-
const log = getReplicationLog2(indexName, partitionId);
|
|
11768
|
-
return {
|
|
11769
|
-
partitionId,
|
|
11770
|
-
primaryTerm: assignment?.primaryTerm ?? fallback.primaryTerm,
|
|
11771
|
-
lastSeqNo: log.newestSeqNo ?? 0
|
|
11772
|
-
};
|
|
11773
|
-
} finally {
|
|
11774
|
-
release?.();
|
|
11775
|
-
}
|
|
12049
|
+
function resolveSnapshotHeaderMetadata(indexName, partitionId) {
|
|
12050
|
+
return nodeSnapshotHeaderMetadata(
|
|
12051
|
+
{ coordinator: config.coordinator, engine, getReplicationLog: getReplicationLog2 },
|
|
12052
|
+
indexName,
|
|
12053
|
+
partitionId
|
|
12054
|
+
);
|
|
11776
12055
|
}
|
|
11777
12056
|
}
|
|
11778
12057
|
|
|
@@ -11875,6 +12154,8 @@ function clusterNodeEngine(node, options) {
|
|
|
11875
12154
|
rebuildAnalysis: () => unsupported("rebuildAnalysis"),
|
|
11876
12155
|
snapshot: () => unsupported("snapshot"),
|
|
11877
12156
|
restore: () => unsupported("restore"),
|
|
12157
|
+
exportDocuments: () => unsupported("exportDocuments"),
|
|
12158
|
+
importDocumentExport: () => unsupported("importDocumentExport"),
|
|
11878
12159
|
rebalance: () => unsupported("rebalance"),
|
|
11879
12160
|
updatePartitionConfig: () => unsupported("updatePartitionConfig"),
|
|
11880
12161
|
compactVectors: () => unsupported("compactVectors"),
|