@delali/narsil 0.2.4-next.43 → 0.2.4-next.44
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/adapters/filesystem.mjs +1 -1
- package/dist/adapters/indexeddb.mjs +1 -1
- package/dist/{chunk-TLRUUMVE.mjs → chunk-P4ELXVJQ.mjs} +1 -0
- package/dist/{chunk-KEDL6FFZ.mjs → chunk-WASNWIEK.mjs} +1142 -327
- package/dist/{chunk-YGQMKTKG.mjs → chunk-XNCJFU2E.mjs} +121 -27
- package/dist/{chunk-O4Q5JYNK.mjs → chunk-XWICBGYT.mjs} +1 -1
- package/dist/{client-B5uO5jL1.d.ts → client-C5BkUqB8.d.ts} +25 -2
- package/dist/client.d.ts +7 -7
- package/dist/client.mjs +11 -2
- package/dist/{config-NgUCkc6a.d.ts → config-B-ZI-ft5.d.ts} +2 -2
- package/dist/{config-B09-cud9.d.ts → config-BZRExglL.d.ts} +20 -3
- package/dist/{direct-executor-7O23GEHE.mjs → direct-executor-IMXNRS7O.mjs} +2 -2
- package/dist/distribution/coordinator/etcd.d.ts +2 -2
- package/dist/distribution/coordinator/etcd.mjs +1 -1
- package/dist/distribution/coordinator/in-memory.d.ts +2 -2
- package/dist/distribution/transport/grpc.mjs +1 -1
- package/dist/distribution.d.ts +24 -10
- package/dist/distribution.mjs +1023 -864
- package/dist/embeddings/openai.mjs +1 -1
- package/dist/{engine-CjLhdDIm.d.ts → engine-Cjr1uBLa.d.ts} +28 -6
- package/dist/index.browser.mjs +1237 -330
- package/dist/index.d.ts +9 -9
- package/dist/index.mjs +4 -4
- package/dist/{options-BakECW-r.d.ts → options-BC68VPoE.d.ts} +9 -2
- package/dist/persistence/durability/checkpoint-worker.d.ts +3 -1
- package/dist/persistence/durability/checkpoint-worker.mjs +4 -4
- package/dist/react.d.ts +4 -4
- package/dist/react.mjs +1 -0
- package/dist/{schema-DXOd96kx.d.ts → schema-B7juWg8Q.d.ts} +1 -1
- package/dist/{search-BGouJxH4.d.ts → search-DDHjfXhJ.d.ts} +14 -3
- package/dist/server.d.ts +8 -8
- package/dist/server.mjs +27 -6
- package/dist/{types-BTE33VZS.d.ts → types-DsSSjY4d.d.ts} +1 -1
- package/dist/workers/entry.mjs +2 -2
- package/package.json +1 -1
package/dist/index.browser.mjs
CHANGED
|
@@ -12,6 +12,7 @@ var ErrorCodes = {
|
|
|
12
12
|
INDEX_NOT_FOUND: "INDEX_NOT_FOUND",
|
|
13
13
|
INDEX_ALREADY_EXISTS: "INDEX_ALREADY_EXISTS",
|
|
14
14
|
INDEX_ORPHANED: "INDEX_ORPHANED",
|
|
15
|
+
INDEX_REOPEN_CAPACITY_EXHAUSTED: "INDEX_REOPEN_CAPACITY_EXHAUSTED",
|
|
15
16
|
PARTITION_CORRUPTED: "PARTITION_CORRUPTED",
|
|
16
17
|
PARTITION_REBALANCING_BACKPRESSURE: "PARTITION_REBALANCING_BACKPRESSURE",
|
|
17
18
|
WORKER_CRASHED: "WORKER_CRASHED",
|
|
@@ -2488,6 +2489,9 @@ function metadataToWire(meta) {
|
|
|
2488
2489
|
if (meta.vectorFields) {
|
|
2489
2490
|
wire.vector_fields = meta.vectorFields;
|
|
2490
2491
|
}
|
|
2492
|
+
if (isNonNegativeInteger(meta.documentCount) && Number.isSafeInteger(meta.documentCount)) {
|
|
2493
|
+
wire.document_count = meta.documentCount;
|
|
2494
|
+
}
|
|
2491
2495
|
if (meta.embedding) {
|
|
2492
2496
|
wire.embedding = meta.embedding.adapter !== void 0 ? { adapter: meta.embedding.adapter, fields: meta.embedding.fields } : { fields: meta.embedding.fields };
|
|
2493
2497
|
}
|
|
@@ -2561,6 +2565,9 @@ function wireToMetadata(raw) {
|
|
|
2561
2565
|
if (raw.vector_fields) {
|
|
2562
2566
|
meta.vectorFields = raw.vector_fields;
|
|
2563
2567
|
}
|
|
2568
|
+
if (isNonNegativeInteger(raw.document_count) && Number.isSafeInteger(raw.document_count)) {
|
|
2569
|
+
meta.documentCount = raw.document_count;
|
|
2570
|
+
}
|
|
2564
2571
|
if (raw.embedding && typeof raw.embedding === "object" && typeof raw.embedding.fields === "object") {
|
|
2565
2572
|
meta.embedding = typeof raw.embedding.adapter === "string" ? { adapter: raw.embedding.adapter, fields: raw.embedding.fields } : { fields: raw.embedding.fields };
|
|
2566
2573
|
}
|
|
@@ -3139,6 +3146,46 @@ function documentWithOwnBuffers(document) {
|
|
|
3139
3146
|
if (!sharesABiggerBuffer(document)) return document;
|
|
3140
3147
|
return withOwnBuffers(document);
|
|
3141
3148
|
}
|
|
3149
|
+
var STRING_HEADER_BYTES = 16;
|
|
3150
|
+
var TWO_BYTE_SAMPLE_LIMIT = 16;
|
|
3151
|
+
var NUMBER_BYTES = 16;
|
|
3152
|
+
var PRIMITIVE_SLOT_BYTES = 4;
|
|
3153
|
+
var ARRAY_HEADER_BYTES = 32;
|
|
3154
|
+
var ARRAY_SLOT_BYTES = 8;
|
|
3155
|
+
var OBJECT_HEADER_BYTES = 40;
|
|
3156
|
+
var PROPERTY_SLOT_BYTES = 8;
|
|
3157
|
+
var TYPED_ARRAY_HEADER_BYTES = 96;
|
|
3158
|
+
function stringBytes(value) {
|
|
3159
|
+
const sampled = Math.min(value.length, TWO_BYTE_SAMPLE_LIMIT);
|
|
3160
|
+
for (let i = 0; i < sampled; i += 1) {
|
|
3161
|
+
if (value.charCodeAt(i) > 255) {
|
|
3162
|
+
return STRING_HEADER_BYTES + value.length * 2;
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
return STRING_HEADER_BYTES + value.length;
|
|
3166
|
+
}
|
|
3167
|
+
function documentValueBytes(value) {
|
|
3168
|
+
if (typeof value === "string") return stringBytes(value);
|
|
3169
|
+
if (typeof value === "number") return NUMBER_BYTES;
|
|
3170
|
+
if (value === null || value === void 0 || typeof value === "boolean") return PRIMITIVE_SLOT_BYTES;
|
|
3171
|
+
if (ArrayBuffer.isView(value)) return TYPED_ARRAY_HEADER_BYTES + value.byteLength;
|
|
3172
|
+
if (Array.isArray(value)) {
|
|
3173
|
+
let bytes = ARRAY_HEADER_BYTES + value.length * ARRAY_SLOT_BYTES;
|
|
3174
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
3175
|
+
bytes += documentValueBytes(value[i]);
|
|
3176
|
+
}
|
|
3177
|
+
return bytes;
|
|
3178
|
+
}
|
|
3179
|
+
if (typeof value === "object") {
|
|
3180
|
+
const source = value;
|
|
3181
|
+
let bytes = OBJECT_HEADER_BYTES;
|
|
3182
|
+
for (const key of Object.keys(source)) {
|
|
3183
|
+
bytes += PROPERTY_SLOT_BYTES + documentValueBytes(source[key]);
|
|
3184
|
+
}
|
|
3185
|
+
return bytes;
|
|
3186
|
+
}
|
|
3187
|
+
return PRIMITIVE_SLOT_BYTES;
|
|
3188
|
+
}
|
|
3142
3189
|
function createDocumentStore() {
|
|
3143
3190
|
const docs = /* @__PURE__ */ new Map();
|
|
3144
3191
|
const forwardMap = /* @__PURE__ */ new Map();
|
|
@@ -3146,6 +3193,19 @@ function createDocumentStore() {
|
|
|
3146
3193
|
const lengthColumns = /* @__PURE__ */ new Map();
|
|
3147
3194
|
let nextInternalId = 0;
|
|
3148
3195
|
let sortedIds = null;
|
|
3196
|
+
let storedContentBytes = 0;
|
|
3197
|
+
function replaceStored(docId, document, fieldLengths) {
|
|
3198
|
+
const existing = docs.get(docId);
|
|
3199
|
+
if (existing !== void 0) {
|
|
3200
|
+
storedContentBytes -= documentValueBytes(existing.fields);
|
|
3201
|
+
} else {
|
|
3202
|
+
sortedIds = null;
|
|
3203
|
+
}
|
|
3204
|
+
writeFieldLengths(assignInternalId(docId), fieldLengths);
|
|
3205
|
+
const fields = documentWithOwnBuffers(document);
|
|
3206
|
+
storedContentBytes += documentValueBytes(fields);
|
|
3207
|
+
docs.set(docId, { fields, fieldLengths });
|
|
3208
|
+
}
|
|
3149
3209
|
function writeFieldLengths(internalId, fieldLengths) {
|
|
3150
3210
|
for (const fieldName of Object.keys(fieldLengths)) {
|
|
3151
3211
|
let column = lengthColumns.get(fieldName);
|
|
@@ -3182,21 +3242,21 @@ function createDocumentStore() {
|
|
|
3182
3242
|
};
|
|
3183
3243
|
return {
|
|
3184
3244
|
store(docId, document, fieldLengths) {
|
|
3185
|
-
|
|
3186
|
-
writeFieldLengths(assignInternalId(docId), fieldLengths);
|
|
3187
|
-
docs.set(docId, { fields: documentWithOwnBuffers(document), fieldLengths });
|
|
3245
|
+
replaceStored(docId, document, fieldLengths);
|
|
3188
3246
|
},
|
|
3189
3247
|
storeRef(docId, document, fieldLengths) {
|
|
3190
|
-
|
|
3191
|
-
writeFieldLengths(assignInternalId(docId), fieldLengths);
|
|
3192
|
-
docs.set(docId, { fields: documentWithOwnBuffers(document), fieldLengths });
|
|
3248
|
+
replaceStored(docId, document, fieldLengths);
|
|
3193
3249
|
},
|
|
3194
3250
|
get(docId) {
|
|
3195
3251
|
return docs.get(docId);
|
|
3196
3252
|
},
|
|
3197
3253
|
remove(docId) {
|
|
3254
|
+
const existing = docs.get(docId);
|
|
3198
3255
|
const removed = docs.delete(docId);
|
|
3199
3256
|
if (removed) {
|
|
3257
|
+
if (existing !== void 0) {
|
|
3258
|
+
storedContentBytes -= documentValueBytes(existing.fields);
|
|
3259
|
+
}
|
|
3200
3260
|
sortedIds = null;
|
|
3201
3261
|
const internalId = forwardMap.get(docId);
|
|
3202
3262
|
if (internalId !== void 0) {
|
|
@@ -3233,6 +3293,7 @@ function createDocumentStore() {
|
|
|
3233
3293
|
docs.clear();
|
|
3234
3294
|
lengthColumns.clear();
|
|
3235
3295
|
clearMappings();
|
|
3296
|
+
storedContentBytes = 0;
|
|
3236
3297
|
},
|
|
3237
3298
|
serialize() {
|
|
3238
3299
|
const result = {};
|
|
@@ -3245,10 +3306,12 @@ function createDocumentStore() {
|
|
|
3245
3306
|
docs.clear();
|
|
3246
3307
|
lengthColumns.clear();
|
|
3247
3308
|
clearMappings();
|
|
3309
|
+
storedContentBytes = 0;
|
|
3248
3310
|
for (const docId of Object.keys(data)) {
|
|
3249
3311
|
const stored = data[docId];
|
|
3250
3312
|
writeFieldLengths(assignInternalId(docId), stored.fieldLengths);
|
|
3251
3313
|
const fields = documentWithOwnBuffers(stored.fields);
|
|
3314
|
+
storedContentBytes += documentValueBytes(fields);
|
|
3252
3315
|
docs.set(docId, fields === stored.fields ? stored : { fields, fieldLengths: stored.fieldLengths });
|
|
3253
3316
|
}
|
|
3254
3317
|
},
|
|
@@ -3272,6 +3335,9 @@ function createDocumentStore() {
|
|
|
3272
3335
|
internalIdCapacity() {
|
|
3273
3336
|
return nextInternalId;
|
|
3274
3337
|
},
|
|
3338
|
+
contentBytes() {
|
|
3339
|
+
return storedContentBytes;
|
|
3340
|
+
},
|
|
3275
3341
|
resolver() {
|
|
3276
3342
|
return idResolver;
|
|
3277
3343
|
}
|
|
@@ -5803,7 +5869,7 @@ var BYTES_PER_NUMBER_SLOT = 8;
|
|
|
5803
5869
|
var BYTES_PER_BOOLEAN_SLOT = 1;
|
|
5804
5870
|
var BYTES_PER_MIXED_SLOT = 8;
|
|
5805
5871
|
var BYTES_PER_STRING_CODE_UNIT = 2;
|
|
5806
|
-
var
|
|
5872
|
+
var STRING_HEADER_BYTES2 = 24;
|
|
5807
5873
|
function grow(length, wanted) {
|
|
5808
5874
|
let next = length === 0 ? 64 : length;
|
|
5809
5875
|
while (next <= wanted) next *= 2;
|
|
@@ -5925,7 +5991,7 @@ function createValueStore(initialKind) {
|
|
|
5925
5991
|
for (const value of mixed) {
|
|
5926
5992
|
if (typeof value !== "string") continue;
|
|
5927
5993
|
if (value.length < SORT_VALUE_MAX_CODE_POINTS) continue;
|
|
5928
|
-
bytes +=
|
|
5994
|
+
bytes += STRING_HEADER_BYTES2 + value.length * BYTES_PER_STRING_CODE_UNIT;
|
|
5929
5995
|
}
|
|
5930
5996
|
return bytes;
|
|
5931
5997
|
}
|
|
@@ -6264,15 +6330,17 @@ function sortColumnBytes(state) {
|
|
|
6264
6330
|
}
|
|
6265
6331
|
|
|
6266
6332
|
// src/core/partition/memory.ts
|
|
6267
|
-
var
|
|
6268
|
-
var
|
|
6269
|
-
var
|
|
6270
|
-
var
|
|
6271
|
-
var
|
|
6333
|
+
var DOC_ENTRY_OVERHEAD_BYTES = 260;
|
|
6334
|
+
var LENGTH_COLUMN_SLOT_BYTES = 6;
|
|
6335
|
+
var POSTING_ENTRY_BYTES = 40;
|
|
6336
|
+
var POSITIONS_ARRAY_OVERHEAD_BYTES = 88;
|
|
6337
|
+
var POSITION_BYTES = 8;
|
|
6338
|
+
var PER_TERM_OVERHEAD_BYTES = 180;
|
|
6339
|
+
var SURFACE_ENTRY_OVERHEAD_BYTES = 140;
|
|
6340
|
+
var FIELD_ENTRY_OVERHEAD_BYTES = 42;
|
|
6272
6341
|
function estimatePartitionBytes(state) {
|
|
6273
6342
|
const docCount = state.docStore.count();
|
|
6274
6343
|
if (docCount === 0) return 0;
|
|
6275
|
-
let bytes = docCount * AVG_DOC_OVERHEAD;
|
|
6276
6344
|
const docFreqs = state.stats.docFrequencies;
|
|
6277
6345
|
let totalPostings = 0;
|
|
6278
6346
|
let termCount = 0;
|
|
@@ -6280,13 +6348,27 @@ function estimatePartitionBytes(state) {
|
|
|
6280
6348
|
totalPostings += docFreqs[term];
|
|
6281
6349
|
termCount++;
|
|
6282
6350
|
}
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6351
|
+
const fieldLengthTotals = state.stats.totalFieldLengths;
|
|
6352
|
+
let totalTokenOccurrences = 0;
|
|
6353
|
+
let textFieldCount = 0;
|
|
6354
|
+
for (const field in fieldLengthTotals) {
|
|
6355
|
+
totalTokenOccurrences += fieldLengthTotals[field];
|
|
6356
|
+
textFieldCount++;
|
|
6357
|
+
}
|
|
6358
|
+
let bytes = state.docStore.contentBytes();
|
|
6359
|
+
bytes += docCount * DOC_ENTRY_OVERHEAD_BYTES;
|
|
6360
|
+
bytes += textFieldCount * state.docStore.internalIdCapacity() * LENGTH_COLUMN_SLOT_BYTES;
|
|
6361
|
+
bytes += totalPostings * POSTING_ENTRY_BYTES;
|
|
6362
|
+
if (state.trackPositions) {
|
|
6363
|
+
bytes += totalPostings * POSITIONS_ARRAY_OVERHEAD_BYTES;
|
|
6364
|
+
bytes += totalTokenOccurrences * POSITION_BYTES;
|
|
6365
|
+
}
|
|
6366
|
+
bytes += termCount * PER_TERM_OVERHEAD_BYTES;
|
|
6367
|
+
bytes += state.surfaceRegistry.size() * SURFACE_ENTRY_OVERHEAD_BYTES;
|
|
6368
|
+
bytes += docCount * state.numericIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
|
|
6369
|
+
bytes += docCount * state.booleanIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
|
|
6370
|
+
bytes += docCount * state.enumIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
|
|
6371
|
+
bytes += docCount * state.geoIndexes.size * FIELD_ENTRY_OVERHEAD_BYTES;
|
|
6290
6372
|
return bytes + sortColumnBytes(state);
|
|
6291
6373
|
}
|
|
6292
6374
|
|
|
@@ -8441,6 +8523,9 @@ function createFrozenDocTable(idTable, lengthColumns, documents, tombstones) {
|
|
|
8441
8523
|
internalIdCapacity() {
|
|
8442
8524
|
return capacity;
|
|
8443
8525
|
},
|
|
8526
|
+
contentBytes() {
|
|
8527
|
+
return documents.byteLength;
|
|
8528
|
+
},
|
|
8444
8529
|
resolver() {
|
|
8445
8530
|
return resolver;
|
|
8446
8531
|
}
|
|
@@ -8468,21 +8553,32 @@ function wrapEncodedDocumentTable(data) {
|
|
|
8468
8553
|
const { blob, offsets } = data;
|
|
8469
8554
|
const count = offsets.length - 1;
|
|
8470
8555
|
const decoded = new Array(count);
|
|
8556
|
+
const encodedBytes = blob.byteLength + offsets.byteLength;
|
|
8557
|
+
let decodedBytes = 0;
|
|
8471
8558
|
return {
|
|
8472
8559
|
count,
|
|
8560
|
+
get byteLength() {
|
|
8561
|
+
return encodedBytes + decodedBytes;
|
|
8562
|
+
},
|
|
8473
8563
|
docAt(ordinal) {
|
|
8474
8564
|
let document = decoded[ordinal];
|
|
8475
8565
|
if (document === void 0) {
|
|
8476
8566
|
document = decode(blob.slice(offsets[ordinal], offsets[ordinal + 1]));
|
|
8477
8567
|
decoded[ordinal] = document;
|
|
8568
|
+
decodedBytes += documentValueBytes(document);
|
|
8478
8569
|
}
|
|
8479
8570
|
return document;
|
|
8480
8571
|
}
|
|
8481
8572
|
};
|
|
8482
8573
|
}
|
|
8483
8574
|
function wrapDocumentArray(documents) {
|
|
8575
|
+
let residentBytes = 0;
|
|
8576
|
+
for (const document of documents) {
|
|
8577
|
+
residentBytes += documentValueBytes(document);
|
|
8578
|
+
}
|
|
8484
8579
|
return {
|
|
8485
8580
|
count: documents.length,
|
|
8581
|
+
byteLength: residentBytes,
|
|
8486
8582
|
docAt(ordinal) {
|
|
8487
8583
|
return documents[ordinal];
|
|
8488
8584
|
}
|
|
@@ -13115,11 +13211,7 @@ function createWorkerPool(config) {
|
|
|
13115
13211
|
indexAssignment.set(indexName, slotIndex);
|
|
13116
13212
|
}
|
|
13117
13213
|
function removeIndex(indexName) {
|
|
13118
|
-
const
|
|
13119
|
-
if (slotIndex === void 0) {
|
|
13120
|
-
return;
|
|
13121
|
-
}
|
|
13122
|
-
workers.get(slotIndex)?.indexes.delete(indexName);
|
|
13214
|
+
for (const slot of workers.values()) slot.indexes.delete(indexName);
|
|
13123
13215
|
indexAssignment.delete(indexName);
|
|
13124
13216
|
}
|
|
13125
13217
|
function getExecutor(indexName) {
|
|
@@ -14317,6 +14409,9 @@ function createDirectExecutor() {
|
|
|
14317
14409
|
}
|
|
14318
14410
|
function dropIndex(indexName) {
|
|
14319
14411
|
const entry = requireIndex(indexName);
|
|
14412
|
+
for (const vectorIndex of entry.vectorIndexes.values()) {
|
|
14413
|
+
vectorIndex.dispose();
|
|
14414
|
+
}
|
|
14320
14415
|
for (const partition of entry.manager.getAllPartitions()) {
|
|
14321
14416
|
partition.clear();
|
|
14322
14417
|
}
|
|
@@ -14611,7 +14706,7 @@ function createAnalysisRebuildCoordinator(config, deps) {
|
|
|
14611
14706
|
async function rebuildNow(indexName, entry) {
|
|
14612
14707
|
const manager = deps.getManager(indexName);
|
|
14613
14708
|
if (manager === void 0) {
|
|
14614
|
-
|
|
14709
|
+
entry.run = null;
|
|
14615
14710
|
return;
|
|
14616
14711
|
}
|
|
14617
14712
|
const partitionCount = manager.partitionCount;
|
|
@@ -14663,6 +14758,23 @@ function createAnalysisRebuildCoordinator(config, deps) {
|
|
|
14663
14758
|
const documentCount = deps.getManager(indexName)?.countDocuments() ?? 0;
|
|
14664
14759
|
await config?.onStaleAnalysis?.({ ...entry.index, documentCount }, () => enqueue2(indexName, entry));
|
|
14665
14760
|
}
|
|
14761
|
+
async function review(indexName, entry) {
|
|
14762
|
+
if (entry.run !== null || deps.getManager(indexName) === void 0) return;
|
|
14763
|
+
try {
|
|
14764
|
+
await announce(indexName, entry);
|
|
14765
|
+
} catch (err) {
|
|
14766
|
+
deps.emit({
|
|
14767
|
+
indexName,
|
|
14768
|
+
status: "failed",
|
|
14769
|
+
partitionsRebuilt: 0,
|
|
14770
|
+
partitionCount: deps.getManager(indexName)?.partitionCount ?? 0,
|
|
14771
|
+
error: toError(err)
|
|
14772
|
+
});
|
|
14773
|
+
}
|
|
14774
|
+
if (rebuildMode === "auto") {
|
|
14775
|
+
void enqueue2(indexName, entry).catch(() => void 0);
|
|
14776
|
+
}
|
|
14777
|
+
}
|
|
14666
14778
|
return {
|
|
14667
14779
|
markStale(index) {
|
|
14668
14780
|
const existing = stale.get(index.indexName);
|
|
@@ -14677,23 +14789,16 @@ function createAnalysisRebuildCoordinator(config, deps) {
|
|
|
14677
14789
|
isStale(indexName) {
|
|
14678
14790
|
return stale.has(indexName);
|
|
14679
14791
|
},
|
|
14792
|
+
isRunning(indexName) {
|
|
14793
|
+
return stale.get(indexName)?.run !== null && stale.get(indexName)?.run !== void 0;
|
|
14794
|
+
},
|
|
14795
|
+
review(indexName) {
|
|
14796
|
+
const entry = stale.get(indexName);
|
|
14797
|
+
return entry === void 0 ? Promise.resolve() : review(indexName, entry);
|
|
14798
|
+
},
|
|
14680
14799
|
async reviewStaleIndexes() {
|
|
14681
14800
|
for (const [indexName, entry] of [...stale]) {
|
|
14682
|
-
|
|
14683
|
-
try {
|
|
14684
|
-
await announce(indexName, entry);
|
|
14685
|
-
} catch (err) {
|
|
14686
|
-
deps.emit({
|
|
14687
|
-
indexName,
|
|
14688
|
-
status: "failed",
|
|
14689
|
-
partitionsRebuilt: 0,
|
|
14690
|
-
partitionCount: deps.getManager(indexName)?.partitionCount ?? 0,
|
|
14691
|
-
error: toError(err)
|
|
14692
|
-
});
|
|
14693
|
-
}
|
|
14694
|
-
if (rebuildMode === "auto") {
|
|
14695
|
-
void enqueue2(indexName, entry).catch(() => void 0);
|
|
14696
|
-
}
|
|
14801
|
+
await review(indexName, entry);
|
|
14697
14802
|
}
|
|
14698
14803
|
},
|
|
14699
14804
|
rebuild(indexName) {
|
|
@@ -16142,6 +16247,9 @@ function mergeTimeOrderedSegments(ordered, fallback) {
|
|
|
16142
16247
|
validateMergedPartition(merged);
|
|
16143
16248
|
return merged;
|
|
16144
16249
|
}
|
|
16250
|
+
function countLiveDocuments(ordered) {
|
|
16251
|
+
return resolveWinners(ordered).size;
|
|
16252
|
+
}
|
|
16145
16253
|
function resolveWinners(ordered) {
|
|
16146
16254
|
const winner = /* @__PURE__ */ new Map();
|
|
16147
16255
|
for (let segIndex = 0; segIndex < ordered.length; segIndex += 1) {
|
|
@@ -17395,6 +17503,7 @@ async function writeSegmentedCheckpoint(input) {
|
|
|
17395
17503
|
});
|
|
17396
17504
|
}
|
|
17397
17505
|
const partitions = [];
|
|
17506
|
+
let wholeDocumentCount = input.wholePartitionPayload === void 0 ? null : 0;
|
|
17398
17507
|
for (const target of input.targets) {
|
|
17399
17508
|
const priorPartition = priorManifest?.partitions.find((p) => p.partitionId === target.partitionId);
|
|
17400
17509
|
const priorSeqNo = snapshotCheckpointFor(priorManifest?.checkpoint ?? [], target.partitionId);
|
|
@@ -17405,15 +17514,13 @@ async function writeSegmentedCheckpoint(input) {
|
|
|
17405
17514
|
priorSeqNo,
|
|
17406
17515
|
target.lastSeqNo
|
|
17407
17516
|
);
|
|
17408
|
-
|
|
17409
|
-
|
|
17410
|
-
|
|
17411
|
-
|
|
17412
|
-
|
|
17413
|
-
|
|
17414
|
-
|
|
17415
|
-
)
|
|
17416
|
-
);
|
|
17517
|
+
if (input.wholePartitionPayload === void 0) {
|
|
17518
|
+
partitions.push(await writePartition(context, target.partitionId, priorPartition, entries));
|
|
17519
|
+
} else {
|
|
17520
|
+
const whole = input.wholePartitionPayload(target.partitionId);
|
|
17521
|
+
wholeDocumentCount = (wholeDocumentCount ?? 0) + whole.docCount;
|
|
17522
|
+
partitions.push(await writeWholePartition(context, target.partitionId, priorPartition, whole, entries));
|
|
17523
|
+
}
|
|
17417
17524
|
}
|
|
17418
17525
|
carryForwardUncheckpointedPartitions(priorManifest, input.targets, partitions, checkpointByPartition);
|
|
17419
17526
|
const manifest = {
|
|
@@ -17425,6 +17532,7 @@ async function writeSegmentedCheckpoint(input) {
|
|
|
17425
17532
|
const parts = await encodeSegmentManifest(manifest);
|
|
17426
17533
|
await directory.atomicWrite(manifestKey(indexName), [parts.header, parts.payload]);
|
|
17427
17534
|
await collectGarbage(directory, indexName, priorManifest, manifest);
|
|
17535
|
+
return wholeDocumentCount;
|
|
17428
17536
|
}
|
|
17429
17537
|
async function writePartition(context, partitionId, priorPartition, entries) {
|
|
17430
17538
|
let segments = priorPartition ? [...priorPartition.segments] : [];
|
|
@@ -17544,9 +17652,9 @@ async function writeIndexCheckpoint(input) {
|
|
|
17544
17652
|
const { directory, metadata, targets, compactionThreshold, manager } = input;
|
|
17545
17653
|
const offloaded = !input.fromMemory && input.canOffload && metadata.tokenizer === void 0 && metadata.stopWords === void 0 && await runCheckpointOnWorker({ root: directory.root, metadata, targets, compactionThreshold });
|
|
17546
17654
|
if (offloaded) {
|
|
17547
|
-
return;
|
|
17655
|
+
return null;
|
|
17548
17656
|
}
|
|
17549
|
-
|
|
17657
|
+
return writeSegmentedCheckpoint({
|
|
17550
17658
|
directory,
|
|
17551
17659
|
metadata,
|
|
17552
17660
|
targets,
|
|
@@ -17578,9 +17686,171 @@ function createSeqOwner(startSeqNo, primaryTerm = SINGLE_NODE_PRIMARY_TERM) {
|
|
|
17578
17686
|
};
|
|
17579
17687
|
}
|
|
17580
17688
|
|
|
17689
|
+
// src/persistence/durability/checkpoint-run.ts
|
|
17690
|
+
function toError2(error) {
|
|
17691
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
17692
|
+
}
|
|
17693
|
+
async function runDurableCheckpoint(input) {
|
|
17694
|
+
const manager = input.hooks.getManager(input.indexName);
|
|
17695
|
+
if (manager === void 0) {
|
|
17696
|
+
return;
|
|
17697
|
+
}
|
|
17698
|
+
const metadata = input.hooks.buildMetadata(input.indexName);
|
|
17699
|
+
if (metadata === void 0) {
|
|
17700
|
+
return;
|
|
17701
|
+
}
|
|
17702
|
+
const targets = [];
|
|
17703
|
+
const documentCount = manager.countDocuments();
|
|
17704
|
+
for (let i = 0; i < manager.partitionCount; i += 1) {
|
|
17705
|
+
const partition = input.indexState.partitions.get(i);
|
|
17706
|
+
targets.push({
|
|
17707
|
+
partitionId: i,
|
|
17708
|
+
lastSeqNo: partition?.appliedSeqNo ?? 0,
|
|
17709
|
+
primaryTerm: partition?.seqOwner.primaryTerm ?? SINGLE_NODE_PRIMARY_TERM
|
|
17710
|
+
});
|
|
17711
|
+
}
|
|
17712
|
+
for (let i = 0; i < manager.partitionCount; i += 1) {
|
|
17713
|
+
const partition = input.indexState.partitions.get(i);
|
|
17714
|
+
if (partition === void 0) {
|
|
17715
|
+
continue;
|
|
17716
|
+
}
|
|
17717
|
+
try {
|
|
17718
|
+
await partition.walWriter.commit();
|
|
17719
|
+
} catch (error) {
|
|
17720
|
+
partition.failed = toError2(error);
|
|
17721
|
+
input.markFatal(partition.failed);
|
|
17722
|
+
throw partition.failed;
|
|
17723
|
+
}
|
|
17724
|
+
}
|
|
17725
|
+
const writtenDocumentCount = await writeIndexCheckpoint({
|
|
17726
|
+
directory: input.directory,
|
|
17727
|
+
metadata,
|
|
17728
|
+
targets,
|
|
17729
|
+
compactionThreshold: input.compactionThreshold,
|
|
17730
|
+
manager,
|
|
17731
|
+
canOffload: input.canOffload,
|
|
17732
|
+
fromMemory: input.fromMemory
|
|
17733
|
+
});
|
|
17734
|
+
const checkpointDocumentCount = writtenDocumentCount ?? documentCount;
|
|
17735
|
+
await input.queueMetadataWrite(input.indexName, async () => {
|
|
17736
|
+
const checkpointMetadata = input.hooks.buildMetadata(input.indexName, checkpointDocumentCount);
|
|
17737
|
+
if (checkpointMetadata === void 0) {
|
|
17738
|
+
return;
|
|
17739
|
+
}
|
|
17740
|
+
const bytes = await writeMetadataEnvelope(checkpointMetadata, { checksum: true });
|
|
17741
|
+
await input.directory.atomicWrite(`${input.indexName}/meta`, bytes);
|
|
17742
|
+
input.hooks.recordCheckpoint?.(input.indexName, checkpointDocumentCount, manager.partitionCount);
|
|
17743
|
+
});
|
|
17744
|
+
await truncateCoveredSegments(input.directory, input.indexName, targets);
|
|
17745
|
+
await reclaimWalBeyondCount(
|
|
17746
|
+
input.directory,
|
|
17747
|
+
input.indexName,
|
|
17748
|
+
manager.partitionCount,
|
|
17749
|
+
input.indexState.partitions,
|
|
17750
|
+
input.markFatal
|
|
17751
|
+
);
|
|
17752
|
+
input.indexState.mutationsSinceCheckpoint = 0;
|
|
17753
|
+
}
|
|
17754
|
+
|
|
17755
|
+
// src/persistence/durability/manager-state.ts
|
|
17756
|
+
async function drainIndexStateForUnload(indexState, pendingMetadataWrite, closeWalWriter) {
|
|
17757
|
+
indexState.unloading = true;
|
|
17758
|
+
try {
|
|
17759
|
+
if (indexState.checkpointInFlight !== null) await indexState.checkpointInFlight;
|
|
17760
|
+
await pendingMetadataWrite;
|
|
17761
|
+
for (const partition of indexState.partitions.values()) {
|
|
17762
|
+
await closeWalWriter(partition.walWriter.close());
|
|
17763
|
+
}
|
|
17764
|
+
} catch (error) {
|
|
17765
|
+
indexState.unloading = false;
|
|
17766
|
+
throw error;
|
|
17767
|
+
}
|
|
17768
|
+
}
|
|
17769
|
+
|
|
17770
|
+
// src/persistence/durability/checkpoint-count.ts
|
|
17771
|
+
async function countCheckpointDocuments(directory, indexName) {
|
|
17772
|
+
const manifest = await readSegmentManifest(directory, indexName);
|
|
17773
|
+
if (manifest !== null) {
|
|
17774
|
+
let total = 0;
|
|
17775
|
+
for (const partition of manifest.partitions) {
|
|
17776
|
+
const ordered = [];
|
|
17777
|
+
for (const segment of partition.segments) {
|
|
17778
|
+
ordered.push(await readSegmentContents(directory, segment.key));
|
|
17779
|
+
}
|
|
17780
|
+
total += countLiveDocuments(ordered);
|
|
17781
|
+
}
|
|
17782
|
+
return total;
|
|
17783
|
+
}
|
|
17784
|
+
const bytes = await directory.read(legacySnapshotKey(indexName));
|
|
17785
|
+
if (bytes === null) {
|
|
17786
|
+
return 0;
|
|
17787
|
+
}
|
|
17788
|
+
return countSnapshotBundleDocuments(bytes);
|
|
17789
|
+
}
|
|
17790
|
+
async function countSnapshotBundleDocuments(bytes) {
|
|
17791
|
+
const bundle = await decodeSnapshotBundle(bytes);
|
|
17792
|
+
let total = 0;
|
|
17793
|
+
for (const partitionBytes of bundle.partitions) {
|
|
17794
|
+
total += Object.keys(deserializePayloadV2(partitionBytes).documents).length;
|
|
17795
|
+
}
|
|
17796
|
+
return total;
|
|
17797
|
+
}
|
|
17798
|
+
|
|
17799
|
+
// src/persistence/durability/recover-index.ts
|
|
17800
|
+
async function recoverPersistedIndex(deps, indexName, metadataOnly) {
|
|
17801
|
+
const { directory, hooks } = deps;
|
|
17802
|
+
const metadata = await loadMetadata(directory, indexName);
|
|
17803
|
+
if (metadata === null) {
|
|
17804
|
+
return;
|
|
17805
|
+
}
|
|
17806
|
+
let derivedCount;
|
|
17807
|
+
if (metadataOnly && metadata.documentCount === void 0) {
|
|
17808
|
+
try {
|
|
17809
|
+
derivedCount = await countCheckpointDocuments(directory, indexName);
|
|
17810
|
+
metadata.documentCount = derivedCount;
|
|
17811
|
+
} catch {
|
|
17812
|
+
derivedCount = void 0;
|
|
17813
|
+
}
|
|
17814
|
+
}
|
|
17815
|
+
await hooks.createIndexFromMetadata(metadata, !metadataOnly);
|
|
17816
|
+
if (metadataOnly) {
|
|
17817
|
+
if (derivedCount !== void 0) {
|
|
17818
|
+
await deps.queueMetadataWrite(indexName, async () => {
|
|
17819
|
+
const upgraded = hooks.buildMetadata(indexName, derivedCount);
|
|
17820
|
+
if (upgraded === void 0) {
|
|
17821
|
+
return;
|
|
17822
|
+
}
|
|
17823
|
+
const bytes = await writeMetadataEnvelope(upgraded, { checksum: true });
|
|
17824
|
+
await directory.atomicWrite(`${indexName}/meta`, bytes);
|
|
17825
|
+
}).catch(() => void 0);
|
|
17826
|
+
}
|
|
17827
|
+
return;
|
|
17828
|
+
}
|
|
17829
|
+
const manager = hooks.getManager(indexName);
|
|
17830
|
+
if (manager === void 0) {
|
|
17831
|
+
return;
|
|
17832
|
+
}
|
|
17833
|
+
const replayDeps = {
|
|
17834
|
+
manager,
|
|
17835
|
+
vectorFieldPaths: hooks.getVectorFieldPaths(indexName),
|
|
17836
|
+
vectorIndexes: hooks.getVectorIndexes(indexName)
|
|
17837
|
+
};
|
|
17838
|
+
const checkpoint = await loadSnapshot(directory, indexName, replayDeps);
|
|
17839
|
+
const manifest = await readSegmentManifest(directory, indexName);
|
|
17840
|
+
if (manifest !== null) {
|
|
17841
|
+
await reclaimOrphanedSegments(directory, indexName, manifest);
|
|
17842
|
+
}
|
|
17843
|
+
for (let partitionId = 0; partitionId < manager.partitionCount; partitionId += 1) {
|
|
17844
|
+
const fromSeqNo = snapshotCheckpointFor(checkpoint, partitionId);
|
|
17845
|
+
const { highestSeqNo } = await replayWal(directory, indexName, partitionId, fromSeqNo, replayDeps);
|
|
17846
|
+
deps.registerPartition(indexName, partitionId, highestSeqNo);
|
|
17847
|
+
}
|
|
17848
|
+
}
|
|
17849
|
+
|
|
17581
17850
|
// src/persistence/durability/types.ts
|
|
17582
17851
|
var DEFAULT_CHECKPOINT_INTERVAL_MS = 3e5;
|
|
17583
17852
|
var DEFAULT_CHECKPOINT_MUTATION_THRESHOLD = 1e5;
|
|
17853
|
+
var DEFAULT_ASYNC_FLUSH_INTERVAL_MS = 1e3;
|
|
17584
17854
|
|
|
17585
17855
|
// src/persistence/durability/group-commit.ts
|
|
17586
17856
|
function createGroupCommitCoordinator(sync) {
|
|
@@ -17826,7 +18096,6 @@ function createWalWriter(directory, config) {
|
|
|
17826
18096
|
}
|
|
17827
18097
|
|
|
17828
18098
|
// src/persistence/durability/manager.ts
|
|
17829
|
-
var DEFAULT_ASYNC_FLUSH_INTERVAL_MS = 1e3;
|
|
17830
18099
|
function createDurabilityManager(config, hooks, directoryOverride) {
|
|
17831
18100
|
const directory = createDurableDirectory(config.directory ?? "");
|
|
17832
18101
|
const canOffloadCheckpoint = directoryOverride === void 0;
|
|
@@ -17857,20 +18126,20 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
17857
18126
|
}
|
|
17858
18127
|
hooks.onFatalError(error);
|
|
17859
18128
|
}
|
|
17860
|
-
function
|
|
18129
|
+
function toError4(err) {
|
|
17861
18130
|
return err instanceof Error ? err : new Error(String(err));
|
|
17862
18131
|
}
|
|
17863
18132
|
async function closeWriterReportingFailure(closePromise) {
|
|
17864
18133
|
try {
|
|
17865
18134
|
await closePromise;
|
|
17866
18135
|
} catch (err) {
|
|
17867
|
-
markFatal(
|
|
18136
|
+
markFatal(toError4(err));
|
|
17868
18137
|
}
|
|
17869
18138
|
}
|
|
17870
18139
|
function getOrCreateIndexState(indexName) {
|
|
17871
18140
|
let state = indexes.get(indexName);
|
|
17872
18141
|
if (state === void 0) {
|
|
17873
|
-
state = { partitions: /* @__PURE__ */ new Map(), mutationsSinceCheckpoint: 0, checkpointInFlight: null };
|
|
18142
|
+
state = { partitions: /* @__PURE__ */ new Map(), mutationsSinceCheckpoint: 0, checkpointInFlight: null, unloading: false };
|
|
17874
18143
|
indexes.set(indexName, state);
|
|
17875
18144
|
}
|
|
17876
18145
|
return state;
|
|
@@ -17933,7 +18202,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
17933
18202
|
try {
|
|
17934
18203
|
await partition.walWriter.commit();
|
|
17935
18204
|
} catch (err) {
|
|
17936
|
-
partition.failed =
|
|
18205
|
+
partition.failed = toError4(err);
|
|
17937
18206
|
markFatal(partition.failed);
|
|
17938
18207
|
}
|
|
17939
18208
|
}
|
|
@@ -17947,7 +18216,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
17947
18216
|
try {
|
|
17948
18217
|
await checkpointIndex(indexName);
|
|
17949
18218
|
} catch (err) {
|
|
17950
|
-
markFatal(
|
|
18219
|
+
markFatal(toError4(err));
|
|
17951
18220
|
}
|
|
17952
18221
|
}
|
|
17953
18222
|
}
|
|
@@ -17964,7 +18233,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
17964
18233
|
}
|
|
17965
18234
|
async function checkpointIndex(indexName, fromMemory = false) {
|
|
17966
18235
|
const indexState = indexes.get(indexName);
|
|
17967
|
-
if (indexState === void 0) {
|
|
18236
|
+
if (indexState === void 0 || indexState.unloading) {
|
|
17968
18237
|
return;
|
|
17969
18238
|
}
|
|
17970
18239
|
if (indexState.checkpointInFlight !== null) {
|
|
@@ -17980,82 +18249,46 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
17980
18249
|
return run;
|
|
17981
18250
|
}
|
|
17982
18251
|
async function performCheckpoint(indexName, indexState, fromMemory = false) {
|
|
17983
|
-
|
|
17984
|
-
if (manager === void 0) {
|
|
17985
|
-
return;
|
|
17986
|
-
}
|
|
17987
|
-
const metadata = hooks.buildMetadata(indexName);
|
|
17988
|
-
if (metadata === void 0) {
|
|
17989
|
-
return;
|
|
17990
|
-
}
|
|
17991
|
-
const targets = [];
|
|
17992
|
-
for (let i = 0; i < manager.partitionCount; i += 1) {
|
|
17993
|
-
const partition = indexState.partitions.get(i);
|
|
17994
|
-
targets.push({
|
|
17995
|
-
partitionId: i,
|
|
17996
|
-
lastSeqNo: partition?.appliedSeqNo ?? 0,
|
|
17997
|
-
primaryTerm: partition?.seqOwner.primaryTerm ?? SINGLE_NODE_PRIMARY_TERM
|
|
17998
|
-
});
|
|
17999
|
-
if (partition !== void 0) {
|
|
18000
|
-
try {
|
|
18001
|
-
await partition.walWriter.commit();
|
|
18002
|
-
} catch (err) {
|
|
18003
|
-
partition.failed = toError3(err);
|
|
18004
|
-
markFatal(partition.failed);
|
|
18005
|
-
throw partition.failed;
|
|
18006
|
-
}
|
|
18007
|
-
}
|
|
18008
|
-
}
|
|
18009
|
-
await writeIndexCheckpoint({
|
|
18252
|
+
await runDurableCheckpoint({
|
|
18010
18253
|
directory,
|
|
18011
|
-
|
|
18012
|
-
|
|
18254
|
+
hooks,
|
|
18255
|
+
indexName,
|
|
18256
|
+
indexState,
|
|
18013
18257
|
compactionThreshold,
|
|
18014
|
-
manager,
|
|
18015
18258
|
canOffload: canOffloadCheckpoint,
|
|
18016
|
-
fromMemory
|
|
18259
|
+
fromMemory,
|
|
18260
|
+
queueMetadataWrite,
|
|
18261
|
+
markFatal
|
|
18017
18262
|
});
|
|
18018
|
-
await truncateCoveredSegments(directory, indexName, targets);
|
|
18019
|
-
await reclaimWalBeyondCount(directory, indexName, manager.partitionCount, indexState.partitions, markFatal);
|
|
18020
|
-
indexState.mutationsSinceCheckpoint = 0;
|
|
18021
18263
|
}
|
|
18022
|
-
|
|
18023
|
-
|
|
18024
|
-
|
|
18025
|
-
|
|
18026
|
-
|
|
18027
|
-
|
|
18028
|
-
|
|
18029
|
-
|
|
18030
|
-
|
|
18031
|
-
|
|
18032
|
-
|
|
18033
|
-
|
|
18034
|
-
|
|
18035
|
-
vectorIndexes: hooks.getVectorIndexes(indexName)
|
|
18036
|
-
};
|
|
18037
|
-
const checkpoint = await loadSnapshot(directory, indexName, deps);
|
|
18038
|
-
const manifest = await readSegmentManifest(directory, indexName);
|
|
18039
|
-
if (manifest !== null) {
|
|
18040
|
-
await reclaimOrphanedSegments(directory, indexName, manifest);
|
|
18041
|
-
}
|
|
18042
|
-
for (let partitionId = 0; partitionId < manager.partitionCount; partitionId += 1) {
|
|
18043
|
-
const fromSeqNo = snapshotCheckpointFor(checkpoint, partitionId);
|
|
18044
|
-
const { highestSeqNo } = await replayWal(directory, indexName, partitionId, fromSeqNo, deps);
|
|
18045
|
-
getOrCreatePartition(indexName, partitionId, highestSeqNo);
|
|
18046
|
-
}
|
|
18264
|
+
function recoverIndex(indexName, metadataOnly = false) {
|
|
18265
|
+
return recoverPersistedIndex(
|
|
18266
|
+
{
|
|
18267
|
+
directory,
|
|
18268
|
+
hooks,
|
|
18269
|
+
registerPartition: (name, partitionId, startSeqNo) => {
|
|
18270
|
+
getOrCreatePartition(name, partitionId, startSeqNo);
|
|
18271
|
+
},
|
|
18272
|
+
queueMetadataWrite
|
|
18273
|
+
},
|
|
18274
|
+
indexName,
|
|
18275
|
+
metadataOnly
|
|
18276
|
+
);
|
|
18047
18277
|
}
|
|
18048
18278
|
return {
|
|
18049
18279
|
isActive() {
|
|
18050
18280
|
return true;
|
|
18051
18281
|
},
|
|
18052
|
-
async recover() {
|
|
18282
|
+
async recover(metadataOnly = false) {
|
|
18053
18283
|
const names = await listPersistedIndexes(directory);
|
|
18054
18284
|
for (const indexName of names) {
|
|
18055
|
-
await recoverIndex(indexName);
|
|
18285
|
+
await recoverIndex(indexName, metadataOnly);
|
|
18056
18286
|
}
|
|
18057
18287
|
startCheckpointTimer();
|
|
18058
18288
|
},
|
|
18289
|
+
recoverIndex(indexName) {
|
|
18290
|
+
return recoverIndex(indexName);
|
|
18291
|
+
},
|
|
18059
18292
|
highestPersistedSeqNo(indexName, partitionId) {
|
|
18060
18293
|
return indexes.get(indexName)?.partitions.get(partitionId)?.appliedSeqNo ?? 0;
|
|
18061
18294
|
},
|
|
@@ -18079,7 +18312,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
18079
18312
|
const entry = buildMutationEntry(record, allocatedSeqNo);
|
|
18080
18313
|
await partition.walWriter.append(entry);
|
|
18081
18314
|
} catch (err) {
|
|
18082
|
-
partition.failed =
|
|
18315
|
+
partition.failed = toError4(err);
|
|
18083
18316
|
markFatal(partition.failed);
|
|
18084
18317
|
throw partition.failed;
|
|
18085
18318
|
}
|
|
@@ -18091,7 +18324,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
18091
18324
|
try {
|
|
18092
18325
|
await partition.walWriter.commit();
|
|
18093
18326
|
} catch (err) {
|
|
18094
|
-
partition.failed =
|
|
18327
|
+
partition.failed = toError4(err);
|
|
18095
18328
|
markFatal(partition.failed);
|
|
18096
18329
|
throw partition.failed;
|
|
18097
18330
|
}
|
|
@@ -18101,7 +18334,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
18101
18334
|
startAsyncFlushTimer();
|
|
18102
18335
|
if (indexState.mutationsSinceCheckpoint >= checkpointMutationThreshold) {
|
|
18103
18336
|
void checkpointIndex(record.indexName).catch((err) => {
|
|
18104
|
-
markFatal(
|
|
18337
|
+
markFatal(toError4(err));
|
|
18105
18338
|
});
|
|
18106
18339
|
}
|
|
18107
18340
|
return allocatedSeqNo;
|
|
@@ -18131,15 +18364,23 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
18131
18364
|
async removeIndex(indexName) {
|
|
18132
18365
|
const indexState = indexes.get(indexName);
|
|
18133
18366
|
if (indexState !== void 0) {
|
|
18134
|
-
|
|
18135
|
-
await closeWriterReportingFailure(partition.walWriter.close());
|
|
18136
|
-
}
|
|
18367
|
+
await drainIndexStateForUnload(indexState, metadataWrites.get(indexName), closeWriterReportingFailure);
|
|
18137
18368
|
indexes.delete(indexName);
|
|
18369
|
+
metadataWrites.delete(indexName);
|
|
18138
18370
|
}
|
|
18139
18371
|
for (const key of await directory.list(`${indexName}/`)) {
|
|
18140
18372
|
await directory.remove(key);
|
|
18141
18373
|
}
|
|
18142
18374
|
},
|
|
18375
|
+
async unloadIndex(indexName) {
|
|
18376
|
+
const indexState = indexes.get(indexName);
|
|
18377
|
+
if (indexState === void 0) {
|
|
18378
|
+
return;
|
|
18379
|
+
}
|
|
18380
|
+
await drainIndexStateForUnload(indexState, metadataWrites.get(indexName), (close) => close);
|
|
18381
|
+
indexes.delete(indexName);
|
|
18382
|
+
metadataWrites.delete(indexName);
|
|
18383
|
+
},
|
|
18143
18384
|
async shutdown() {
|
|
18144
18385
|
shuttingDown = true;
|
|
18145
18386
|
if (checkpointTimer !== null) {
|
|
@@ -18178,7 +18419,13 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18178
18419
|
function getOrCreateIndexState(indexName) {
|
|
18179
18420
|
let state = indexes.get(indexName);
|
|
18180
18421
|
if (state === void 0) {
|
|
18181
|
-
state = {
|
|
18422
|
+
state = {
|
|
18423
|
+
mutationsSinceCheckpoint: 0,
|
|
18424
|
+
checkpointInFlight: null,
|
|
18425
|
+
applyChain: Promise.resolve(),
|
|
18426
|
+
unloading: false,
|
|
18427
|
+
appliedSeqNoByPartition: /* @__PURE__ */ new Map()
|
|
18428
|
+
};
|
|
18182
18429
|
indexes.set(indexName, state);
|
|
18183
18430
|
}
|
|
18184
18431
|
return state;
|
|
@@ -18209,10 +18456,11 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18209
18456
|
if (manager === void 0 || metadata === void 0) {
|
|
18210
18457
|
return;
|
|
18211
18458
|
}
|
|
18459
|
+
const documentCount = manager.countDocuments();
|
|
18212
18460
|
const seqNoByPartition = /* @__PURE__ */ new Map();
|
|
18213
18461
|
const primaryTermByPartition = /* @__PURE__ */ new Map();
|
|
18214
18462
|
for (let i = 0; i < manager.partitionCount; i += 1) {
|
|
18215
|
-
seqNoByPartition.set(i, 0);
|
|
18463
|
+
seqNoByPartition.set(i, indexState.appliedSeqNoByPartition.get(i) ?? 0);
|
|
18216
18464
|
primaryTermByPartition.set(i, SINGLE_NODE_PRIMARY_TERM);
|
|
18217
18465
|
}
|
|
18218
18466
|
const { parts } = await buildSnapshotBundleBytes({
|
|
@@ -18228,6 +18476,13 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18228
18476
|
primaryTermByPartition
|
|
18229
18477
|
});
|
|
18230
18478
|
await adapter.save(snapshotStorageKey(indexName), concatEnvelopeParts(parts));
|
|
18479
|
+
await queueMetadataWrite(indexName, async () => {
|
|
18480
|
+
const checkpointMetadata = hooks.buildMetadata(indexName, documentCount);
|
|
18481
|
+
if (checkpointMetadata === void 0) return;
|
|
18482
|
+
const metadataBytes = await writeMetadataEnvelope(checkpointMetadata, { checksum: true });
|
|
18483
|
+
await adapter.save(metadataKey(indexName), metadataBytes);
|
|
18484
|
+
hooks.recordCheckpoint?.(indexName, documentCount, manager.partitionCount);
|
|
18485
|
+
});
|
|
18231
18486
|
indexState.mutationsSinceCheckpoint = 0;
|
|
18232
18487
|
if (publisher !== void 0) {
|
|
18233
18488
|
const partitionIds = [];
|
|
@@ -18237,15 +18492,18 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18237
18492
|
await publisher.publishPartitions(indexName, partitionIds);
|
|
18238
18493
|
}
|
|
18239
18494
|
}
|
|
18240
|
-
async function checkpointIndex(indexName) {
|
|
18495
|
+
async function checkpointIndex(indexName, forceFresh = false) {
|
|
18241
18496
|
const indexState = indexes.get(indexName);
|
|
18242
|
-
if (indexState === void 0) {
|
|
18497
|
+
if (indexState === void 0 || indexState.unloading) {
|
|
18243
18498
|
return;
|
|
18244
18499
|
}
|
|
18245
18500
|
if (indexState.checkpointInFlight !== null) {
|
|
18246
|
-
|
|
18501
|
+
await indexState.checkpointInFlight;
|
|
18502
|
+
if (!forceFresh) {
|
|
18503
|
+
return;
|
|
18504
|
+
}
|
|
18247
18505
|
}
|
|
18248
|
-
const run = performCheckpoint(indexName, indexState).finally(() => {
|
|
18506
|
+
const run = indexState.applyChain.then(() => performCheckpoint(indexName, indexState)).finally(() => {
|
|
18249
18507
|
indexState.checkpointInFlight = null;
|
|
18250
18508
|
});
|
|
18251
18509
|
indexState.checkpointInFlight = run;
|
|
@@ -18260,13 +18518,35 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18260
18518
|
);
|
|
18261
18519
|
return queued;
|
|
18262
18520
|
}
|
|
18263
|
-
async function recoverIndex(indexName) {
|
|
18521
|
+
async function recoverIndex(indexName, metadataOnly = false) {
|
|
18264
18522
|
const metaBytes = await adapter.load(metadataKey(indexName));
|
|
18265
18523
|
if (metaBytes === null) {
|
|
18266
18524
|
return;
|
|
18267
18525
|
}
|
|
18268
18526
|
const { metadata } = await readMetadataEnvelope(metaBytes);
|
|
18269
|
-
|
|
18527
|
+
let derivedCount;
|
|
18528
|
+
if (metadataOnly && metadata.documentCount === void 0) {
|
|
18529
|
+
try {
|
|
18530
|
+
derivedCount = await countStoredBundleDocuments(indexName);
|
|
18531
|
+
metadata.documentCount = derivedCount;
|
|
18532
|
+
} catch {
|
|
18533
|
+
derivedCount = void 0;
|
|
18534
|
+
}
|
|
18535
|
+
}
|
|
18536
|
+
await hooks.createIndexFromMetadata(metadata, !metadataOnly);
|
|
18537
|
+
if (metadataOnly) {
|
|
18538
|
+
if (derivedCount !== void 0) {
|
|
18539
|
+
await queueMetadataWrite(indexName, async () => {
|
|
18540
|
+
const upgraded = hooks.buildMetadata(indexName, derivedCount);
|
|
18541
|
+
if (upgraded === void 0) {
|
|
18542
|
+
return;
|
|
18543
|
+
}
|
|
18544
|
+
const bytes = await writeMetadataEnvelope(upgraded, { checksum: true });
|
|
18545
|
+
await adapter.save(metadataKey(indexName), bytes);
|
|
18546
|
+
}).catch(() => void 0);
|
|
18547
|
+
}
|
|
18548
|
+
return;
|
|
18549
|
+
}
|
|
18270
18550
|
const manager = hooks.getManager(indexName);
|
|
18271
18551
|
if (manager === void 0) {
|
|
18272
18552
|
return;
|
|
@@ -18275,33 +18555,58 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18275
18555
|
if (snapshotBytes === null) {
|
|
18276
18556
|
return;
|
|
18277
18557
|
}
|
|
18278
|
-
await loadSnapshotBundleBytes(snapshotBytes, {
|
|
18558
|
+
const checkpoint = await loadSnapshotBundleBytes(snapshotBytes, {
|
|
18279
18559
|
manager,
|
|
18280
18560
|
vectorFieldPaths: hooks.getVectorFieldPaths(indexName),
|
|
18281
18561
|
vectorIndexes: hooks.getVectorIndexes(indexName)
|
|
18282
18562
|
});
|
|
18563
|
+
const indexState = getOrCreateIndexState(indexName);
|
|
18564
|
+
for (let partitionId = 0; partitionId < manager.partitionCount; partitionId += 1) {
|
|
18565
|
+
const persistedSeqNo = snapshotCheckpointFor(checkpoint, partitionId);
|
|
18566
|
+
if (persistedSeqNo > 0) {
|
|
18567
|
+
indexState.appliedSeqNoByPartition.set(partitionId, persistedSeqNo);
|
|
18568
|
+
}
|
|
18569
|
+
}
|
|
18570
|
+
}
|
|
18571
|
+
async function countStoredBundleDocuments(indexName) {
|
|
18572
|
+
const snapshotBytes = await adapter.load(snapshotStorageKey(indexName));
|
|
18573
|
+
if (snapshotBytes === null) {
|
|
18574
|
+
return 0;
|
|
18575
|
+
}
|
|
18576
|
+
return countSnapshotBundleDocuments(snapshotBytes);
|
|
18283
18577
|
}
|
|
18284
18578
|
return {
|
|
18285
18579
|
isActive() {
|
|
18286
18580
|
return true;
|
|
18287
18581
|
},
|
|
18288
|
-
async recover() {
|
|
18582
|
+
async recover(metadataOnly = false) {
|
|
18289
18583
|
const keys = await adapter.list("");
|
|
18290
18584
|
for (const key of keys) {
|
|
18291
18585
|
if (key.endsWith("/meta")) {
|
|
18292
|
-
await recoverIndex(key.slice(0, -"/meta".length));
|
|
18586
|
+
await recoverIndex(key.slice(0, -"/meta".length), metadataOnly);
|
|
18293
18587
|
}
|
|
18294
18588
|
}
|
|
18295
18589
|
startCheckpointTimer();
|
|
18296
18590
|
},
|
|
18297
|
-
|
|
18298
|
-
return
|
|
18591
|
+
recoverIndex(indexName) {
|
|
18592
|
+
return recoverIndex(indexName);
|
|
18593
|
+
},
|
|
18594
|
+
highestPersistedSeqNo(indexName, partitionId) {
|
|
18595
|
+
return indexes.get(indexName)?.appliedSeqNoByPartition.get(partitionId) ?? 0;
|
|
18299
18596
|
},
|
|
18300
18597
|
async recordMutation(record) {
|
|
18301
18598
|
const indexState = getOrCreateIndexState(record.indexName);
|
|
18302
|
-
const buffered = indexState.applyChain.then(() =>
|
|
18303
|
-
|
|
18304
|
-
|
|
18599
|
+
const buffered = indexState.applyChain.then(async () => {
|
|
18600
|
+
await record.apply();
|
|
18601
|
+
const appliedSeqNo2 = (indexState.appliedSeqNoByPartition.get(record.partitionId) ?? 0) + 1;
|
|
18602
|
+
indexState.appliedSeqNoByPartition.set(record.partitionId, appliedSeqNo2);
|
|
18603
|
+
return appliedSeqNo2;
|
|
18604
|
+
});
|
|
18605
|
+
indexState.applyChain = buffered.then(
|
|
18606
|
+
() => void 0,
|
|
18607
|
+
() => void 0
|
|
18608
|
+
);
|
|
18609
|
+
const appliedSeqNo = await buffered;
|
|
18305
18610
|
indexState.mutationsSinceCheckpoint += 1;
|
|
18306
18611
|
startCheckpointTimer();
|
|
18307
18612
|
if (indexState.mutationsSinceCheckpoint >= checkpointMutationThreshold) {
|
|
@@ -18309,7 +18614,7 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18309
18614
|
hooks.onFatalError(err instanceof Error ? err : new Error(String(err)));
|
|
18310
18615
|
});
|
|
18311
18616
|
}
|
|
18312
|
-
return
|
|
18617
|
+
return appliedSeqNo;
|
|
18313
18618
|
},
|
|
18314
18619
|
persistMetadata(indexName) {
|
|
18315
18620
|
return queueMetadataWrite(indexName, async () => {
|
|
@@ -18325,17 +18630,49 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18325
18630
|
checkpoint(indexName) {
|
|
18326
18631
|
return checkpointIndex(indexName);
|
|
18327
18632
|
},
|
|
18633
|
+
checkpointFromMemory(indexName) {
|
|
18634
|
+
return checkpointIndex(indexName, true);
|
|
18635
|
+
},
|
|
18328
18636
|
async checkpointAll() {
|
|
18329
18637
|
for (const indexName of [...indexes.keys()]) {
|
|
18330
18638
|
await checkpointIndex(indexName);
|
|
18331
18639
|
}
|
|
18332
18640
|
},
|
|
18333
18641
|
async removeIndex(indexName) {
|
|
18642
|
+
const indexState = indexes.get(indexName);
|
|
18643
|
+
if (indexState !== void 0) {
|
|
18644
|
+
indexState.unloading = true;
|
|
18645
|
+
try {
|
|
18646
|
+
await indexState.applyChain;
|
|
18647
|
+
if (indexState.checkpointInFlight !== null) await indexState.checkpointInFlight;
|
|
18648
|
+
await metadataWrites.get(indexName);
|
|
18649
|
+
} catch (error) {
|
|
18650
|
+
indexState.unloading = false;
|
|
18651
|
+
throw error;
|
|
18652
|
+
}
|
|
18653
|
+
}
|
|
18334
18654
|
indexes.delete(indexName);
|
|
18655
|
+
metadataWrites.delete(indexName);
|
|
18335
18656
|
for (const key of await adapter.list(`${indexName}/`)) {
|
|
18336
18657
|
await adapter.delete(key);
|
|
18337
18658
|
}
|
|
18338
18659
|
},
|
|
18660
|
+
async unloadIndex(indexName) {
|
|
18661
|
+
const indexState = indexes.get(indexName);
|
|
18662
|
+
if (indexState !== void 0) {
|
|
18663
|
+
indexState.unloading = true;
|
|
18664
|
+
try {
|
|
18665
|
+
await indexState.applyChain;
|
|
18666
|
+
if (indexState.checkpointInFlight !== null) await indexState.checkpointInFlight;
|
|
18667
|
+
await metadataWrites.get(indexName);
|
|
18668
|
+
} catch (error) {
|
|
18669
|
+
indexState.unloading = false;
|
|
18670
|
+
throw error;
|
|
18671
|
+
}
|
|
18672
|
+
}
|
|
18673
|
+
indexes.delete(indexName);
|
|
18674
|
+
metadataWrites.delete(indexName);
|
|
18675
|
+
},
|
|
18339
18676
|
async reloadIndex(indexName) {
|
|
18340
18677
|
const indexState = getOrCreateIndexState(indexName);
|
|
18341
18678
|
const reload = indexState.applyChain.then(() => recoverIndex(indexName));
|
|
@@ -18359,21 +18696,22 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18359
18696
|
}
|
|
18360
18697
|
|
|
18361
18698
|
// src/engine/durability-integration.ts
|
|
18362
|
-
function buildMetadata(indexName, hooks) {
|
|
18699
|
+
function buildMetadata(indexName, hooks, documentCount) {
|
|
18363
18700
|
const config = hooks.getIndexConfig(indexName);
|
|
18364
18701
|
const manager = hooks.getManager(indexName);
|
|
18365
|
-
if (config === void 0
|
|
18702
|
+
if (config === void 0) {
|
|
18366
18703
|
return void 0;
|
|
18367
18704
|
}
|
|
18368
18705
|
const metadata = {
|
|
18369
18706
|
indexName,
|
|
18370
18707
|
schema: config.schema,
|
|
18371
18708
|
language: config.language,
|
|
18372
|
-
partitionCount: manager.partitionCount,
|
|
18709
|
+
partitionCount: manager?.partitionCount ?? config.partitionCount,
|
|
18373
18710
|
bm25Params: { k1: config.k1, b: config.b },
|
|
18374
18711
|
createdAt: Date.now(),
|
|
18375
18712
|
engineVersion: VERSION
|
|
18376
18713
|
};
|
|
18714
|
+
metadata.documentCount = documentCount ?? config.documentCount;
|
|
18377
18715
|
if (config.embedding) {
|
|
18378
18716
|
metadata.embedding = config.embedding;
|
|
18379
18717
|
}
|
|
@@ -18423,8 +18761,9 @@ function createDurabilityIntegration(tier, hooks) {
|
|
|
18423
18761
|
getManager: hooks.getManager,
|
|
18424
18762
|
getVectorFieldPaths: hooks.getVectorFieldPaths,
|
|
18425
18763
|
getVectorIndexes: hooks.getVectorIndexes,
|
|
18426
|
-
buildMetadata: (indexName) => buildMetadata(indexName, hooks),
|
|
18764
|
+
buildMetadata: (indexName, documentCount) => buildMetadata(indexName, hooks, documentCount),
|
|
18427
18765
|
createIndexFromMetadata: hooks.createIndexFromMetadata,
|
|
18766
|
+
recordCheckpoint: hooks.recordCheckpoint,
|
|
18428
18767
|
onFatalError: hooks.onFatalError
|
|
18429
18768
|
};
|
|
18430
18769
|
const manager = tier.kind === "wal" ? createDurabilityManager(tier.config, managerHooks) : createSnapshotOnlyManager(tier.adapter, tier.config, managerHooks, hooks.checkpointPublisher);
|
|
@@ -18463,9 +18802,9 @@ function createDurabilityFromTier(tier, wiring) {
|
|
|
18463
18802
|
}
|
|
18464
18803
|
return createDurabilityIntegration(tier, {
|
|
18465
18804
|
checkpointPublisher: { publishPartitions: wiring.publishCheckpointedPartitions },
|
|
18466
|
-
getManager: (indexName) => wiring.indexRegistry.has(indexName) ? wiring.
|
|
18805
|
+
getManager: (indexName) => wiring.indexRegistry.has(indexName) ? wiring.getManager(indexName) : void 0,
|
|
18467
18806
|
getVectorFieldPaths: (indexName) => wiring.indexRegistry.get(indexName)?.vectorFieldPaths ?? /* @__PURE__ */ new Set(),
|
|
18468
|
-
getVectorIndexes: (indexName) => wiring.indexRegistry.has(indexName) ? wiring.
|
|
18807
|
+
getVectorIndexes: (indexName) => (wiring.indexRegistry.has(indexName) ? wiring.getManager(indexName)?.getVectorIndexes() : void 0) ?? /* @__PURE__ */ new Map(),
|
|
18469
18808
|
getIndexConfig: (indexName) => {
|
|
18470
18809
|
const entry = wiring.indexRegistry.get(indexName);
|
|
18471
18810
|
if (entry === void 0) {
|
|
@@ -18478,6 +18817,8 @@ function createDurabilityFromTier(tier, wiring) {
|
|
|
18478
18817
|
return {
|
|
18479
18818
|
...entry.indexUuid !== null ? { indexUuid: entry.indexUuid } : {},
|
|
18480
18819
|
...entry.heldPartitions !== null ? { heldPartitions: entry.heldPartitions } : {},
|
|
18820
|
+
documentCount: entry.documentCount,
|
|
18821
|
+
partitionCount: entry.partitionCount,
|
|
18481
18822
|
schema: flattenSchema(entry.config.schema),
|
|
18482
18823
|
language: entry.language.name,
|
|
18483
18824
|
k1: entry.config.bm25?.k1 ?? 1.2,
|
|
@@ -18497,10 +18838,401 @@ function createDurabilityFromTier(tier, wiring) {
|
|
|
18497
18838
|
};
|
|
18498
18839
|
},
|
|
18499
18840
|
createIndexFromMetadata: wiring.createIndexFromMetadata,
|
|
18841
|
+
recordCheckpoint: wiring.recordCheckpoint,
|
|
18500
18842
|
onFatalError: wiring.emitFatalError
|
|
18501
18843
|
});
|
|
18502
18844
|
}
|
|
18503
18845
|
|
|
18846
|
+
// src/engine/index-state/entry.ts
|
|
18847
|
+
var DEFAULT_MAX_REOPEN_WAITERS = 64;
|
|
18848
|
+
var MAX_REOPEN_FAILURES = 5;
|
|
18849
|
+
var MIN_SWEEP_INTERVAL_MS = 100;
|
|
18850
|
+
var MAX_SWEEP_INTERVAL_MS = 6e4;
|
|
18851
|
+
function createLifecycleEntry(state) {
|
|
18852
|
+
return {
|
|
18853
|
+
state,
|
|
18854
|
+
lastAccessAt: Date.now(),
|
|
18855
|
+
activeOperations: 0,
|
|
18856
|
+
reopenCount: 0,
|
|
18857
|
+
reopenFailures: 0,
|
|
18858
|
+
nextReopenAt: 0,
|
|
18859
|
+
cachedError: null,
|
|
18860
|
+
reopenPromise: null,
|
|
18861
|
+
waiterCount: 0,
|
|
18862
|
+
closePromise: null,
|
|
18863
|
+
dropPromise: null,
|
|
18864
|
+
drainResolvers: /* @__PURE__ */ new Set()
|
|
18865
|
+
};
|
|
18866
|
+
}
|
|
18867
|
+
function validateLimit(name, value, allowZero = false) {
|
|
18868
|
+
if (value === void 0) return;
|
|
18869
|
+
const minimum = allowZero ? 0 : 1;
|
|
18870
|
+
if (!Number.isSafeInteger(value) || value < minimum) {
|
|
18871
|
+
const requirement = allowZero ? "a non-negative safe integer" : "a positive safe integer";
|
|
18872
|
+
throw new NarsilError(ErrorCodes.CONFIG_INVALID, `${name} must be ${requirement}`, {
|
|
18873
|
+
field: name,
|
|
18874
|
+
value
|
|
18875
|
+
});
|
|
18876
|
+
}
|
|
18877
|
+
}
|
|
18878
|
+
function cachedRecoveryError(error, indexName) {
|
|
18879
|
+
if (error instanceof NarsilError) return error;
|
|
18880
|
+
return new NarsilError(ErrorCodes.PERSISTENCE_LOAD_FAILED, `Index "${indexName}" could not be reopened`, {
|
|
18881
|
+
indexName,
|
|
18882
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
18883
|
+
});
|
|
18884
|
+
}
|
|
18885
|
+
|
|
18886
|
+
// src/engine/index-state/index.ts
|
|
18887
|
+
function createIndexStateCoordinator(config, callbacks) {
|
|
18888
|
+
validateLimit("lifecycle.idleTimeoutMs", config?.idleTimeoutMs);
|
|
18889
|
+
validateLimit("lifecycle.maxOpenIndexes", config?.maxOpenIndexes);
|
|
18890
|
+
validateLimit("lifecycle.maxOpenBytes", config?.maxOpenBytes);
|
|
18891
|
+
validateLimit("lifecycle.maxReopenWaiters", config?.maxReopenWaiters, true);
|
|
18892
|
+
const entries = /* @__PURE__ */ new Map();
|
|
18893
|
+
const waiterLimit = config?.maxReopenWaiters ?? DEFAULT_MAX_REOPEN_WAITERS;
|
|
18894
|
+
let sweepTimer = null;
|
|
18895
|
+
function entryOf(indexName) {
|
|
18896
|
+
const entry = entries.get(indexName);
|
|
18897
|
+
if (entry === void 0) {
|
|
18898
|
+
throw new NarsilError(ErrorCodes.INDEX_NOT_FOUND, `Index "${indexName}" does not exist`, { indexName });
|
|
18899
|
+
}
|
|
18900
|
+
return entry;
|
|
18901
|
+
}
|
|
18902
|
+
function publicState(entry) {
|
|
18903
|
+
return entry.state === "closing" || entry.state === "dropping" ? "open" : entry.state;
|
|
18904
|
+
}
|
|
18905
|
+
async function waitForDrain(entry) {
|
|
18906
|
+
if (entry.activeOperations === 0) return;
|
|
18907
|
+
await new Promise((resolve) => entry.drainResolvers.add(resolve));
|
|
18908
|
+
}
|
|
18909
|
+
function markClosed(entry) {
|
|
18910
|
+
entry.state = "closed";
|
|
18911
|
+
entry.cachedError = null;
|
|
18912
|
+
entry.reopenFailures = 0;
|
|
18913
|
+
}
|
|
18914
|
+
function releaseEntry(entry) {
|
|
18915
|
+
entry.activeOperations -= 1;
|
|
18916
|
+
if (entry.activeOperations !== 0) return;
|
|
18917
|
+
for (const resolve of entry.drainResolvers) resolve();
|
|
18918
|
+
entry.drainResolvers.clear();
|
|
18919
|
+
}
|
|
18920
|
+
async function waitForTransition(indexName, entry, transition) {
|
|
18921
|
+
if (entry.waiterCount >= waiterLimit) {
|
|
18922
|
+
throw new NarsilError(
|
|
18923
|
+
ErrorCodes.INDEX_REOPEN_CAPACITY_EXHAUSTED,
|
|
18924
|
+
`Index "${indexName}" has reached its reopen waiter limit`,
|
|
18925
|
+
{ indexName, maximum: waiterLimit }
|
|
18926
|
+
);
|
|
18927
|
+
}
|
|
18928
|
+
entry.waiterCount += 1;
|
|
18929
|
+
try {
|
|
18930
|
+
await transition;
|
|
18931
|
+
} finally {
|
|
18932
|
+
entry.waiterCount -= 1;
|
|
18933
|
+
}
|
|
18934
|
+
}
|
|
18935
|
+
async function awaitPendingTransitions(indexName) {
|
|
18936
|
+
let entry = entryOf(indexName);
|
|
18937
|
+
while (true) {
|
|
18938
|
+
if (entry.state === "dropping" && entry.dropPromise !== null) {
|
|
18939
|
+
await entry.dropPromise;
|
|
18940
|
+
entry = entryOf(indexName);
|
|
18941
|
+
continue;
|
|
18942
|
+
}
|
|
18943
|
+
if (entry.state === "closing" && entry.closePromise !== null) {
|
|
18944
|
+
await waitForTransition(
|
|
18945
|
+
indexName,
|
|
18946
|
+
entry,
|
|
18947
|
+
entry.closePromise.catch(() => void 0)
|
|
18948
|
+
);
|
|
18949
|
+
entry = entryOf(indexName);
|
|
18950
|
+
continue;
|
|
18951
|
+
}
|
|
18952
|
+
return entry;
|
|
18953
|
+
}
|
|
18954
|
+
}
|
|
18955
|
+
async function reopen(indexName, explicit) {
|
|
18956
|
+
const entry = await awaitPendingTransitions(indexName);
|
|
18957
|
+
if (entry.state === "open") return;
|
|
18958
|
+
if (explicit) {
|
|
18959
|
+
entry.reopenFailures = 0;
|
|
18960
|
+
entry.nextReopenAt = 0;
|
|
18961
|
+
entry.cachedError = null;
|
|
18962
|
+
if (entry.state === "reopen-failed") entry.state = "closed";
|
|
18963
|
+
}
|
|
18964
|
+
if (entry.state === "reopen-failed") throw entry.cachedError;
|
|
18965
|
+
if (entry.reopenPromise !== null) {
|
|
18966
|
+
await waitForTransition(indexName, entry, entry.reopenPromise);
|
|
18967
|
+
return;
|
|
18968
|
+
}
|
|
18969
|
+
if (!explicit && entry.cachedError !== null && Date.now() < entry.nextReopenAt) {
|
|
18970
|
+
throw entry.cachedError;
|
|
18971
|
+
}
|
|
18972
|
+
const run = callbacks.reopen(indexName).then(() => {
|
|
18973
|
+
entry.state = "open";
|
|
18974
|
+
entry.reopenCount += 1;
|
|
18975
|
+
entry.reopenFailures = 0;
|
|
18976
|
+
entry.nextReopenAt = 0;
|
|
18977
|
+
entry.cachedError = null;
|
|
18978
|
+
}).catch((error) => {
|
|
18979
|
+
entry.reopenFailures += 1;
|
|
18980
|
+
entry.cachedError = cachedRecoveryError(error, indexName);
|
|
18981
|
+
entry.nextReopenAt = Date.now() + 100 * 2 ** (entry.reopenFailures - 1);
|
|
18982
|
+
entry.state = entry.reopenFailures >= MAX_REOPEN_FAILURES ? "reopen-failed" : "closed";
|
|
18983
|
+
throw entry.cachedError;
|
|
18984
|
+
}).finally(() => {
|
|
18985
|
+
entry.reopenPromise = null;
|
|
18986
|
+
});
|
|
18987
|
+
entry.reopenPromise = run;
|
|
18988
|
+
await run;
|
|
18989
|
+
}
|
|
18990
|
+
async function closeEntry(indexName, automatic) {
|
|
18991
|
+
let entry = entryOf(indexName);
|
|
18992
|
+
while (true) {
|
|
18993
|
+
if (entry.state === "dropping" && entry.dropPromise !== null) {
|
|
18994
|
+
if (automatic) return false;
|
|
18995
|
+
await entry.dropPromise;
|
|
18996
|
+
entry = entryOf(indexName);
|
|
18997
|
+
continue;
|
|
18998
|
+
}
|
|
18999
|
+
if (entry.reopenPromise !== null) {
|
|
19000
|
+
if (automatic) return false;
|
|
19001
|
+
await entry.reopenPromise.catch(() => void 0);
|
|
19002
|
+
entry = entryOf(indexName);
|
|
19003
|
+
continue;
|
|
19004
|
+
}
|
|
19005
|
+
break;
|
|
19006
|
+
}
|
|
19007
|
+
if (entry.state === "closed" || entry.state === "reopen-failed") return true;
|
|
19008
|
+
if (entry.state === "closing" && entry.closePromise !== null) {
|
|
19009
|
+
if (!automatic) await entry.closePromise;
|
|
19010
|
+
return !automatic;
|
|
19011
|
+
}
|
|
19012
|
+
if (automatic && (entry.activeOperations > 0 || !callbacks.canCloseAutomatically(indexName))) return false;
|
|
19013
|
+
entry.state = "closing";
|
|
19014
|
+
let irreversible = false;
|
|
19015
|
+
const run = (async () => {
|
|
19016
|
+
await waitForDrain(entry);
|
|
19017
|
+
await callbacks.close(indexName, () => {
|
|
19018
|
+
irreversible = true;
|
|
19019
|
+
});
|
|
19020
|
+
markClosed(entry);
|
|
19021
|
+
})().catch((error) => {
|
|
19022
|
+
if (irreversible) markClosed(entry);
|
|
19023
|
+
else entry.state = "open";
|
|
19024
|
+
throw error;
|
|
19025
|
+
}).finally(() => {
|
|
19026
|
+
entry.closePromise = null;
|
|
19027
|
+
});
|
|
19028
|
+
entry.closePromise = run;
|
|
19029
|
+
if (automatic) {
|
|
19030
|
+
await run.catch(() => void 0);
|
|
19031
|
+
return publicState(entry) === "closed";
|
|
19032
|
+
}
|
|
19033
|
+
await run;
|
|
19034
|
+
return true;
|
|
19035
|
+
}
|
|
19036
|
+
async function dropEntry(indexName, action) {
|
|
19037
|
+
let entry = entryOf(indexName);
|
|
19038
|
+
while (true) {
|
|
19039
|
+
if (entry.dropPromise !== null) {
|
|
19040
|
+
await entry.dropPromise;
|
|
19041
|
+
return;
|
|
19042
|
+
}
|
|
19043
|
+
if (entry.reopenPromise !== null) {
|
|
19044
|
+
await entry.reopenPromise.catch(() => void 0);
|
|
19045
|
+
entry = entryOf(indexName);
|
|
19046
|
+
continue;
|
|
19047
|
+
}
|
|
19048
|
+
if (entry.closePromise !== null) {
|
|
19049
|
+
await entry.closePromise.catch(() => void 0);
|
|
19050
|
+
entry = entryOf(indexName);
|
|
19051
|
+
continue;
|
|
19052
|
+
}
|
|
19053
|
+
break;
|
|
19054
|
+
}
|
|
19055
|
+
const previousState = entry.state;
|
|
19056
|
+
entry.state = "dropping";
|
|
19057
|
+
const run = (async () => {
|
|
19058
|
+
await waitForDrain(entry);
|
|
19059
|
+
await action();
|
|
19060
|
+
entries.delete(indexName);
|
|
19061
|
+
})().catch((error) => {
|
|
19062
|
+
entry.state = previousState;
|
|
19063
|
+
throw error;
|
|
19064
|
+
}).finally(() => {
|
|
19065
|
+
entry.dropPromise = null;
|
|
19066
|
+
});
|
|
19067
|
+
entry.dropPromise = run;
|
|
19068
|
+
await run;
|
|
19069
|
+
}
|
|
19070
|
+
function exceedsLimits() {
|
|
19071
|
+
let open = 0;
|
|
19072
|
+
let bytes = 0;
|
|
19073
|
+
for (const [name, entry] of entries) {
|
|
19074
|
+
if (entry.state !== "open") continue;
|
|
19075
|
+
open += 1;
|
|
19076
|
+
bytes += callbacks.estimateBytes(name);
|
|
19077
|
+
}
|
|
19078
|
+
return config?.maxOpenIndexes !== void 0 && open > config.maxOpenIndexes || config?.maxOpenBytes !== void 0 && bytes > config.maxOpenBytes;
|
|
19079
|
+
}
|
|
19080
|
+
async function enforceLimits(protectedName) {
|
|
19081
|
+
if (config?.maxOpenIndexes === void 0 && config?.maxOpenBytes === void 0) return;
|
|
19082
|
+
while (exceedsLimits()) {
|
|
19083
|
+
const candidates = [...entries].filter(([name, entry]) => name !== protectedName && entry.state === "open" && entry.activeOperations === 0).sort((left, right) => left[1].lastAccessAt - right[1].lastAccessAt);
|
|
19084
|
+
const candidate = candidates[0];
|
|
19085
|
+
if (candidate === void 0 || !await closeEntry(candidate[0], true)) return;
|
|
19086
|
+
}
|
|
19087
|
+
}
|
|
19088
|
+
async function sweep() {
|
|
19089
|
+
const timeout = config?.idleTimeoutMs;
|
|
19090
|
+
if (timeout !== void 0 && timeout > 0) {
|
|
19091
|
+
const cutoff = Date.now() - timeout;
|
|
19092
|
+
for (const [name, entry] of entries) {
|
|
19093
|
+
if (entry.state === "open" && entry.lastAccessAt <= cutoff) await closeEntry(name, true);
|
|
19094
|
+
}
|
|
19095
|
+
}
|
|
19096
|
+
await enforceLimits();
|
|
19097
|
+
}
|
|
19098
|
+
if (config !== void 0) {
|
|
19099
|
+
const requested = config.idleTimeoutMs === void 0 ? MAX_SWEEP_INTERVAL_MS : config.idleTimeoutMs / 2;
|
|
19100
|
+
const interval = Math.min(MAX_SWEEP_INTERVAL_MS, Math.max(MIN_SWEEP_INTERVAL_MS, requested));
|
|
19101
|
+
sweepTimer = setInterval(() => void sweep().catch(() => void 0), interval);
|
|
19102
|
+
if (typeof sweepTimer.unref === "function") sweepTimer.unref();
|
|
19103
|
+
}
|
|
19104
|
+
return {
|
|
19105
|
+
async registerOpen(indexName) {
|
|
19106
|
+
entries.set(indexName, createLifecycleEntry("open"));
|
|
19107
|
+
await enforceLimits(indexName);
|
|
19108
|
+
},
|
|
19109
|
+
registerClosed(indexName) {
|
|
19110
|
+
entries.set(indexName, createLifecycleEntry("closed"));
|
|
19111
|
+
},
|
|
19112
|
+
forget(indexName) {
|
|
19113
|
+
entries.delete(indexName);
|
|
19114
|
+
},
|
|
19115
|
+
async acquire(indexName, markActive = true) {
|
|
19116
|
+
let entry = entryOf(indexName);
|
|
19117
|
+
const opened = entry.state !== "open";
|
|
19118
|
+
await reopen(indexName, false);
|
|
19119
|
+
entry = entryOf(indexName);
|
|
19120
|
+
while (entry.state !== "open") {
|
|
19121
|
+
await reopen(indexName, false);
|
|
19122
|
+
entry = entryOf(indexName);
|
|
19123
|
+
}
|
|
19124
|
+
if (markActive) entry.lastAccessAt = Date.now();
|
|
19125
|
+
entry.activeOperations += 1;
|
|
19126
|
+
try {
|
|
19127
|
+
if (opened) await enforceLimits(indexName);
|
|
19128
|
+
} catch (error) {
|
|
19129
|
+
releaseEntry(entry);
|
|
19130
|
+
throw error;
|
|
19131
|
+
}
|
|
19132
|
+
let released = false;
|
|
19133
|
+
return () => {
|
|
19134
|
+
if (released) return;
|
|
19135
|
+
released = true;
|
|
19136
|
+
releaseEntry(entry);
|
|
19137
|
+
};
|
|
19138
|
+
},
|
|
19139
|
+
async open(indexName) {
|
|
19140
|
+
await reopen(indexName, true);
|
|
19141
|
+
let entry = entryOf(indexName);
|
|
19142
|
+
while (entry.state !== "open") {
|
|
19143
|
+
await reopen(indexName, true);
|
|
19144
|
+
entry = entryOf(indexName);
|
|
19145
|
+
}
|
|
19146
|
+
entry.lastAccessAt = Date.now();
|
|
19147
|
+
entry.activeOperations += 1;
|
|
19148
|
+
try {
|
|
19149
|
+
await enforceLimits(indexName);
|
|
19150
|
+
} finally {
|
|
19151
|
+
releaseEntry(entry);
|
|
19152
|
+
}
|
|
19153
|
+
},
|
|
19154
|
+
async close(indexName) {
|
|
19155
|
+
await closeEntry(indexName, false);
|
|
19156
|
+
},
|
|
19157
|
+
drop(indexName, action) {
|
|
19158
|
+
return dropEntry(indexName, action);
|
|
19159
|
+
},
|
|
19160
|
+
stateOf(indexName) {
|
|
19161
|
+
return publicState(entryOf(indexName));
|
|
19162
|
+
},
|
|
19163
|
+
reopenCount(indexName) {
|
|
19164
|
+
return entryOf(indexName).reopenCount;
|
|
19165
|
+
},
|
|
19166
|
+
counts() {
|
|
19167
|
+
let open = 0;
|
|
19168
|
+
let closed = 0;
|
|
19169
|
+
let reopens = 0;
|
|
19170
|
+
for (const entry of entries.values()) {
|
|
19171
|
+
if (entry.state === "open" || entry.state === "closing") open += 1;
|
|
19172
|
+
else closed += 1;
|
|
19173
|
+
reopens += entry.reopenCount;
|
|
19174
|
+
}
|
|
19175
|
+
return { open, closed, reopens };
|
|
19176
|
+
},
|
|
19177
|
+
dispose() {
|
|
19178
|
+
if (sweepTimer !== null) clearInterval(sweepTimer);
|
|
19179
|
+
sweepTimer = null;
|
|
19180
|
+
entries.clear();
|
|
19181
|
+
}
|
|
19182
|
+
};
|
|
19183
|
+
}
|
|
19184
|
+
|
|
19185
|
+
// src/engine/index-state-wiring.ts
|
|
19186
|
+
function wireIndexState(wiring) {
|
|
19187
|
+
const { durability, executor, orchestrator, analysisRebuild, indexRegistry, rebalancingIndexes } = wiring;
|
|
19188
|
+
return createIndexStateCoordinator(wiring.config, {
|
|
19189
|
+
async reopen(indexName) {
|
|
19190
|
+
if (durability === null) {
|
|
19191
|
+
throw new NarsilError(ErrorCodes.CONFIG_INVALID, "An index needs durability before it can reopen");
|
|
19192
|
+
}
|
|
19193
|
+
try {
|
|
19194
|
+
await durability.manager.recoverIndex(indexName);
|
|
19195
|
+
await analysisRebuild.review(indexName);
|
|
19196
|
+
await orchestrator.openIndex(indexName);
|
|
19197
|
+
await wiring.onOpen?.(indexName);
|
|
19198
|
+
} catch (error) {
|
|
19199
|
+
await orchestrator.closeIndex(indexName).catch(() => void 0);
|
|
19200
|
+
await durability.manager.unloadIndex(indexName).catch(() => void 0);
|
|
19201
|
+
if (executor.getManager(indexName) !== void 0) executor.dropIndex(indexName);
|
|
19202
|
+
throw error;
|
|
19203
|
+
}
|
|
19204
|
+
},
|
|
19205
|
+
async close(indexName, markIrreversible) {
|
|
19206
|
+
if (durability === null) {
|
|
19207
|
+
throw new NarsilError(ErrorCodes.CONFIG_INVALID, "An index needs durability before it can close");
|
|
19208
|
+
}
|
|
19209
|
+
const manager = wiring.requireManager(indexName);
|
|
19210
|
+
const documentCount = manager.countDocuments();
|
|
19211
|
+
const partitionCount = manager.partitionCount;
|
|
19212
|
+
await (durability.manager.checkpointFromMemory?.(indexName) ?? durability.manager.checkpoint(indexName));
|
|
19213
|
+
await orchestrator.closeIndex(indexName);
|
|
19214
|
+
await durability.manager.unloadIndex(indexName);
|
|
19215
|
+
markIrreversible();
|
|
19216
|
+
try {
|
|
19217
|
+
await wiring.onClose?.(indexName);
|
|
19218
|
+
} finally {
|
|
19219
|
+
if (executor.getManager(indexName) !== void 0) executor.dropIndex(indexName);
|
|
19220
|
+
const entry = indexRegistry.get(indexName);
|
|
19221
|
+
if (entry !== void 0) {
|
|
19222
|
+
entry.documentCount = documentCount;
|
|
19223
|
+
entry.partitionCount = partitionCount;
|
|
19224
|
+
}
|
|
19225
|
+
}
|
|
19226
|
+
},
|
|
19227
|
+
canCloseAutomatically(indexName) {
|
|
19228
|
+
return !rebalancingIndexes.has(indexName) && !analysisRebuild.isRunning(indexName) && !orchestrator.isIndexBusy(indexName);
|
|
19229
|
+
},
|
|
19230
|
+
estimateBytes(indexName) {
|
|
19231
|
+
return executor.getManager(indexName)?.estimateMemoryBytes() ?? 0;
|
|
19232
|
+
}
|
|
19233
|
+
});
|
|
19234
|
+
}
|
|
19235
|
+
|
|
18504
19236
|
// src/engine/invalidation.ts
|
|
18505
19237
|
var STATISTICS_BROADCAST_INTERVAL_MS = 5e3;
|
|
18506
19238
|
var FOREIGN_STATS_TTL_MS = 6e4;
|
|
@@ -18526,7 +19258,7 @@ function createInvalidationIntegration(deps) {
|
|
|
18526
19258
|
const reloadChains = /* @__PURE__ */ new Map();
|
|
18527
19259
|
let broadcastTimer = null;
|
|
18528
19260
|
let stopped = false;
|
|
18529
|
-
function
|
|
19261
|
+
function toError4(err) {
|
|
18530
19262
|
return err instanceof Error ? err : new Error(String(err));
|
|
18531
19263
|
}
|
|
18532
19264
|
function pruneStaleForeignStats(entries) {
|
|
@@ -18539,7 +19271,7 @@ function createInvalidationIntegration(deps) {
|
|
|
18539
19271
|
}
|
|
18540
19272
|
function scheduleReload(indexName) {
|
|
18541
19273
|
const chain = reloadChains.get(indexName) ?? Promise.resolve();
|
|
18542
|
-
const next = chain.then(() => reloadIndex(indexName)).catch((err) => onError(
|
|
19274
|
+
const next = chain.then(() => reloadIndex(indexName)).catch((err) => onError(toError4(err)));
|
|
18543
19275
|
reloadChains.set(indexName, next);
|
|
18544
19276
|
}
|
|
18545
19277
|
function storeForeignStats(indexName, sourceInstanceId, stats) {
|
|
@@ -18565,7 +19297,7 @@ function createInvalidationIntegration(deps) {
|
|
|
18565
19297
|
}
|
|
18566
19298
|
storeForeignStats(event.indexName, event.instanceId, event.stats);
|
|
18567
19299
|
} catch (err) {
|
|
18568
|
-
onError(
|
|
19300
|
+
onError(toError4(err));
|
|
18569
19301
|
}
|
|
18570
19302
|
}
|
|
18571
19303
|
async function publishOwnStatistics() {
|
|
@@ -18587,7 +19319,7 @@ function createInvalidationIntegration(deps) {
|
|
|
18587
19319
|
}
|
|
18588
19320
|
});
|
|
18589
19321
|
} catch (err) {
|
|
18590
|
-
onError(
|
|
19322
|
+
onError(toError4(err));
|
|
18591
19323
|
}
|
|
18592
19324
|
}
|
|
18593
19325
|
}
|
|
@@ -18606,7 +19338,7 @@ function createInvalidationIntegration(deps) {
|
|
|
18606
19338
|
sourceInstanceId: instanceId
|
|
18607
19339
|
});
|
|
18608
19340
|
} catch (err) {
|
|
18609
|
-
onError(
|
|
19341
|
+
onError(toError4(err));
|
|
18610
19342
|
}
|
|
18611
19343
|
},
|
|
18612
19344
|
broadcastStats(indexName) {
|
|
@@ -18651,7 +19383,7 @@ function createInvalidationIntegration(deps) {
|
|
|
18651
19383
|
try {
|
|
18652
19384
|
await adapter.shutdown();
|
|
18653
19385
|
} catch (err) {
|
|
18654
|
-
onError(
|
|
19386
|
+
onError(toError4(err));
|
|
18655
19387
|
}
|
|
18656
19388
|
}
|
|
18657
19389
|
};
|
|
@@ -18693,7 +19425,7 @@ async function transferIndexToPool(indexName, pool, config, manager) {
|
|
|
18693
19425
|
}
|
|
18694
19426
|
|
|
18695
19427
|
// src/engine/orchestration/eligibility.ts
|
|
18696
|
-
function
|
|
19428
|
+
function toError3(value) {
|
|
18697
19429
|
return value instanceof Error ? value : new Error(String(value));
|
|
18698
19430
|
}
|
|
18699
19431
|
function isDeterministicFailure(error) {
|
|
@@ -19272,7 +20004,7 @@ async function promoteBeforeBatch(state, indexName, incomingCount) {
|
|
|
19272
20004
|
if (!result.shouldPromote) return;
|
|
19273
20005
|
state.promotionInProgress = true;
|
|
19274
20006
|
const run = runPromotion(state, result.reason).catch((err) => {
|
|
19275
|
-
const error =
|
|
20007
|
+
const error = toError3(err);
|
|
19276
20008
|
state.promotionBlocked = isDeterministicFailure(error);
|
|
19277
20009
|
state.promotionInProgress = false;
|
|
19278
20010
|
state.callbacks?.onPromotionFailure?.(result.reason, error, !state.promotionBlocked);
|
|
@@ -19293,7 +20025,7 @@ async function checkPromotion(state) {
|
|
|
19293
20025
|
state.promotionInProgress = true;
|
|
19294
20026
|
setTimeout(() => {
|
|
19295
20027
|
const run = runPromotion(state, result.reason).catch((err) => {
|
|
19296
|
-
const error =
|
|
20028
|
+
const error = toError3(err);
|
|
19297
20029
|
state.promotionBlocked = isDeterministicFailure(error);
|
|
19298
20030
|
state.promotionInProgress = false;
|
|
19299
20031
|
state.callbacks?.onPromotionFailure?.(result.reason, error, !state.promotionBlocked);
|
|
@@ -19463,6 +20195,56 @@ function createWorkerOrchestrator(config, executor, promoter, indexRegistry, cal
|
|
|
19463
20195
|
state.segmentLedger.clear();
|
|
19464
20196
|
}
|
|
19465
20197
|
}
|
|
20198
|
+
async function awaitPromotionIdle() {
|
|
20199
|
+
while (state.promotionInProgress) {
|
|
20200
|
+
const run = state.promotionRun;
|
|
20201
|
+
if (run !== null) {
|
|
20202
|
+
await run;
|
|
20203
|
+
} else {
|
|
20204
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
20205
|
+
}
|
|
20206
|
+
}
|
|
20207
|
+
}
|
|
20208
|
+
async function closeIndex(indexName) {
|
|
20209
|
+
await awaitPromotionIdle();
|
|
20210
|
+
await awaitReplicationIdle(state, indexName);
|
|
20211
|
+
await awaitCompactions(state);
|
|
20212
|
+
const pool = state.workerPool;
|
|
20213
|
+
if (pool !== null && state.promotedIndexes.has(indexName)) {
|
|
20214
|
+
const outcomes = await Promise.allSettled(
|
|
20215
|
+
pool.getAllExecutors().map((worker) => worker.execute({ type: "dropIndex", indexName, requestId: `close-${indexName}` }))
|
|
20216
|
+
);
|
|
20217
|
+
const failure = outcomes.find((outcome) => outcome.status === "rejected");
|
|
20218
|
+
if (failure?.status === "rejected") {
|
|
20219
|
+
const entry = indexRegistry.get(indexName);
|
|
20220
|
+
const manager = executor.getManager(indexName);
|
|
20221
|
+
if (entry !== void 0 && manager !== void 0) {
|
|
20222
|
+
try {
|
|
20223
|
+
await transferIndexToPool(indexName, pool, entry.config, manager);
|
|
20224
|
+
} catch (repairError) {
|
|
20225
|
+
throw new NarsilError(
|
|
20226
|
+
ErrorCodes.WORKER_CRASHED,
|
|
20227
|
+
`Worker copies for index "${indexName}" could not be restored after close failed`,
|
|
20228
|
+
{
|
|
20229
|
+
indexName,
|
|
20230
|
+
closeError: failure.reason instanceof Error ? failure.reason.message : String(failure.reason),
|
|
20231
|
+
repairError: repairError instanceof Error ? repairError.message : String(repairError)
|
|
20232
|
+
}
|
|
20233
|
+
);
|
|
20234
|
+
}
|
|
20235
|
+
}
|
|
20236
|
+
if (failure.reason instanceof NarsilError) throw failure.reason;
|
|
20237
|
+
throw new NarsilError(ErrorCodes.WORKER_CRASHED, `A worker could not close index "${indexName}"`, {
|
|
20238
|
+
indexName,
|
|
20239
|
+
cause: failure.reason instanceof Error ? failure.reason.message : String(failure.reason)
|
|
20240
|
+
});
|
|
20241
|
+
}
|
|
20242
|
+
pool.removeIndex(indexName);
|
|
20243
|
+
}
|
|
20244
|
+
state.promotedIndexes.delete(indexName);
|
|
20245
|
+
state.replicationQueues.delete(indexName);
|
|
20246
|
+
state.segmentLedger.delete(indexName);
|
|
20247
|
+
}
|
|
19466
20248
|
async function replicate(action) {
|
|
19467
20249
|
await replicateToWorkers(state, action);
|
|
19468
20250
|
if (action.type === "attachSegments") {
|
|
@@ -19475,6 +20257,9 @@ function createWorkerOrchestrator(config, executor, promoter, indexRegistry, cal
|
|
|
19475
20257
|
replicateToWorkers: replicate,
|
|
19476
20258
|
awaitReplication: (indexName) => awaitReplicationIdle(state, indexName),
|
|
19477
20259
|
awaitCompactions: () => awaitCompactions(state),
|
|
20260
|
+
openIndex: (indexName) => resyncIndex(indexName, true),
|
|
20261
|
+
closeIndex,
|
|
20262
|
+
isIndexBusy: (indexName) => state.promotionInProgress || state.replicationQueues.has(indexName) || state.compactionsInFlight.has(indexName),
|
|
19478
20263
|
buildSegments: (requests) => buildSegments(state, requests),
|
|
19479
20264
|
segmentBuildConcurrency: (indexName) => segmentBuildConcurrency(state, indexName),
|
|
19480
20265
|
searchViaWorker: (indexName, params, globalStats, partitionIds) => searchViaWorker(state, indexName, params, globalStats, partitionIds),
|
|
@@ -19486,6 +20271,11 @@ function createWorkerOrchestrator(config, executor, promoter, indexRegistry, cal
|
|
|
19486
20271
|
};
|
|
19487
20272
|
}
|
|
19488
20273
|
|
|
20274
|
+
// src/engine/vector-fields.ts
|
|
20275
|
+
function getVectorFieldPaths(schema) {
|
|
20276
|
+
return new Set(extractVectorFieldsFromSchema(schema).keys());
|
|
20277
|
+
}
|
|
20278
|
+
|
|
19489
20279
|
// src/engine/watermark.ts
|
|
19490
20280
|
function createWatermarkNotifier(deps) {
|
|
19491
20281
|
const latchedCapacity = /* @__PURE__ */ new Map();
|
|
@@ -19521,10 +20311,7 @@ function createWatermarkNotifier(deps) {
|
|
|
19521
20311
|
}
|
|
19522
20312
|
|
|
19523
20313
|
// src/engine/core.ts
|
|
19524
|
-
function
|
|
19525
|
-
return new Set(extractVectorFieldsFromSchema(schema).keys());
|
|
19526
|
-
}
|
|
19527
|
-
function createEngineCore(config) {
|
|
20314
|
+
function createEngineCore(config, hooks) {
|
|
19528
20315
|
const executor = createDirectExecutor();
|
|
19529
20316
|
const promoter = createExecutionPromoter({
|
|
19530
20317
|
perIndexThreshold: config?.workers?.promotionThreshold,
|
|
@@ -19608,8 +20395,12 @@ function createEngineCore(config) {
|
|
|
19608
20395
|
}
|
|
19609
20396
|
return manager;
|
|
19610
20397
|
}
|
|
19611
|
-
async function createIndexFromMetadata(metadata) {
|
|
19612
|
-
|
|
20398
|
+
async function createIndexFromMetadata(metadata, loadData) {
|
|
20399
|
+
const existing = indexRegistry.get(metadata.indexName);
|
|
20400
|
+
if (existing !== void 0) {
|
|
20401
|
+
if (loadData && executor.getManager(metadata.indexName) === void 0) {
|
|
20402
|
+
executor.createIndex(metadata.indexName, existing.config, existing.language);
|
|
20403
|
+
}
|
|
19613
20404
|
return;
|
|
19614
20405
|
}
|
|
19615
20406
|
const indexConfig = reconstructSchemaFromMetadata(metadata);
|
|
@@ -19638,7 +20429,7 @@ function createEngineCore(config) {
|
|
|
19638
20429
|
}
|
|
19639
20430
|
}
|
|
19640
20431
|
try {
|
|
19641
|
-
executor.createIndex(metadata.indexName, indexConfig, language);
|
|
20432
|
+
if (loadData) executor.createIndex(metadata.indexName, indexConfig, language);
|
|
19642
20433
|
} catch (err) {
|
|
19643
20434
|
if (err instanceof NarsilError && err.code === ErrorCodes.CONFIG_INVALID) {
|
|
19644
20435
|
throw new NarsilError(err.code, `Recovery of index "${metadata.indexName}" failed: ${err.message}`, {
|
|
@@ -19656,8 +20447,12 @@ function createEngineCore(config) {
|
|
|
19656
20447
|
embeddingAdapterName: adapterName,
|
|
19657
20448
|
vectorFieldPaths: getVectorFieldPaths(indexConfig.schema),
|
|
19658
20449
|
indexUuid: metadata.indexUuid ?? null,
|
|
19659
|
-
heldPartitions: metadata.heldPartitions ?? null
|
|
20450
|
+
heldPartitions: metadata.heldPartitions ?? null,
|
|
20451
|
+
documentCount: metadata.documentCount ?? 0,
|
|
20452
|
+
partitionCount: metadata.partitionCount
|
|
19660
20453
|
});
|
|
20454
|
+
if (loadData) await indexState.registerOpen(metadata.indexName);
|
|
20455
|
+
else indexState.registerClosed(metadata.indexName);
|
|
19661
20456
|
if (metadata.analysisRevision !== language.revision) {
|
|
19662
20457
|
analysisRebuild.markStale({
|
|
19663
20458
|
indexName: metadata.indexName,
|
|
@@ -19668,9 +20463,12 @@ function createEngineCore(config) {
|
|
|
19668
20463
|
}
|
|
19669
20464
|
}
|
|
19670
20465
|
const durabilityTier = config !== void 0 ? resolveDurabilityTier(config) : null;
|
|
20466
|
+
if (config?.lifecycle !== void 0 && durabilityTier === null) {
|
|
20467
|
+
throw new NarsilError(ErrorCodes.CONFIG_INVALID, "Index lifecycle settings require durability");
|
|
20468
|
+
}
|
|
19671
20469
|
let invalidation = null;
|
|
19672
20470
|
const durability = createDurabilityFromTier(durabilityTier, {
|
|
19673
|
-
|
|
20471
|
+
getManager: (indexName) => executor.getManager(indexName),
|
|
19674
20472
|
indexRegistry,
|
|
19675
20473
|
createIndexFromMetadata,
|
|
19676
20474
|
emitFatalError(error) {
|
|
@@ -19679,7 +20477,13 @@ function createEngineCore(config) {
|
|
|
19679
20477
|
for (const handler of handlers) handler({ error });
|
|
19680
20478
|
}
|
|
19681
20479
|
},
|
|
19682
|
-
publishCheckpointedPartitions: (indexName, partitions) => invalidation?.publishPartitions(indexName, partitions) ?? Promise.resolve()
|
|
20480
|
+
publishCheckpointedPartitions: (indexName, partitions) => invalidation?.publishPartitions(indexName, partitions) ?? Promise.resolve(),
|
|
20481
|
+
recordCheckpoint(indexName, documentCount, partitionCount) {
|
|
20482
|
+
const entry = indexRegistry.get(indexName);
|
|
20483
|
+
if (entry === void 0) return;
|
|
20484
|
+
entry.documentCount = documentCount;
|
|
20485
|
+
entry.partitionCount = partitionCount;
|
|
20486
|
+
}
|
|
19683
20487
|
});
|
|
19684
20488
|
invalidation = createInvalidationFromConfig(config, durabilityTier?.kind ?? null, {
|
|
19685
20489
|
getManager: (indexName) => executor.getManager(indexName) ?? void 0,
|
|
@@ -19725,6 +20529,18 @@ function createEngineCore(config) {
|
|
|
19725
20529
|
resyncIndex: (indexName, wasPromoted) => orchestrator.resyncIndex(indexName, wasPromoted),
|
|
19726
20530
|
durabilityManager: durability?.manager ?? null
|
|
19727
20531
|
});
|
|
20532
|
+
const indexState = wireIndexState({
|
|
20533
|
+
config: config?.lifecycle,
|
|
20534
|
+
durability,
|
|
20535
|
+
executor,
|
|
20536
|
+
orchestrator,
|
|
20537
|
+
analysisRebuild,
|
|
20538
|
+
indexRegistry,
|
|
20539
|
+
rebalancingIndexes,
|
|
20540
|
+
requireManager,
|
|
20541
|
+
onClose: hooks?.onIndexClose,
|
|
20542
|
+
onOpen: hooks?.onIndexOpen
|
|
20543
|
+
});
|
|
19728
20544
|
const mutationCtx = {
|
|
19729
20545
|
executor,
|
|
19730
20546
|
pluginRegistry,
|
|
@@ -19778,6 +20594,7 @@ function createEngineCore(config) {
|
|
|
19778
20594
|
bufferIfRebalancing,
|
|
19779
20595
|
watermarkNotifier,
|
|
19780
20596
|
analysisRebuild,
|
|
20597
|
+
indexState,
|
|
19781
20598
|
mutationCtx,
|
|
19782
20599
|
rebalanceCtx
|
|
19783
20600
|
};
|
|
@@ -19937,8 +20754,11 @@ async function createEngineIndex(core, config, name, indexConfig, indexUuid) {
|
|
|
19937
20754
|
embeddingAdapterName,
|
|
19938
20755
|
vectorFieldPaths: getVectorFieldPaths(indexConfig.schema),
|
|
19939
20756
|
indexUuid: null,
|
|
19940
|
-
heldPartitions: null
|
|
20757
|
+
heldPartitions: null,
|
|
20758
|
+
documentCount: 0,
|
|
20759
|
+
partitionCount: indexConfig.partitions?.maxPartitions ?? 1
|
|
19941
20760
|
});
|
|
20761
|
+
await core.indexState.registerOpen(name);
|
|
19942
20762
|
if (core.durability) {
|
|
19943
20763
|
await core.durability.manager.persistMetadata(name);
|
|
19944
20764
|
}
|
|
@@ -19947,13 +20767,21 @@ async function createEngineIndex(core, config, name, indexConfig, indexUuid) {
|
|
|
19947
20767
|
async function dropEngineIndex(core, name) {
|
|
19948
20768
|
core.guardShutdown();
|
|
19949
20769
|
const entry = core.requireIndex(name);
|
|
19950
|
-
core.
|
|
19951
|
-
|
|
19952
|
-
|
|
19953
|
-
|
|
19954
|
-
|
|
19955
|
-
|
|
19956
|
-
|
|
20770
|
+
await core.indexState.drop(name, async () => {
|
|
20771
|
+
await core.orchestrator.closeIndex(name);
|
|
20772
|
+
if (core.durability) {
|
|
20773
|
+
try {
|
|
20774
|
+
await core.durability.manager.removeIndex(name);
|
|
20775
|
+
} catch (error) {
|
|
20776
|
+
await core.orchestrator.openIndex(name).catch(() => void 0);
|
|
20777
|
+
throw error;
|
|
20778
|
+
}
|
|
20779
|
+
}
|
|
20780
|
+
if (core.executor.getManager(name) !== void 0) core.executor.dropIndex(name);
|
|
20781
|
+
core.indexRegistry.delete(name);
|
|
20782
|
+
core.watermarkNotifier.forget(name);
|
|
20783
|
+
core.analysisRebuild.clearStale(name);
|
|
20784
|
+
});
|
|
19957
20785
|
await core.pluginRegistry.runHook("onIndexDrop", { indexName: name, config: entry.config });
|
|
19958
20786
|
}
|
|
19959
20787
|
function registerEngineEmbeddingAdapter(core, name, adapter) {
|
|
@@ -19988,6 +20816,7 @@ function registerEngineEmbeddingAdapter(core, name, adapter) {
|
|
|
19988
20816
|
// src/engine/lifecycle.ts
|
|
19989
20817
|
async function shutdownEngine(core) {
|
|
19990
20818
|
const { executor, durability, indexRegistry, eventHandlers, orchestrator } = core;
|
|
20819
|
+
core.indexState.dispose();
|
|
19991
20820
|
for (const [name] of indexRegistry) {
|
|
19992
20821
|
const manager = executor.getManager(name);
|
|
19993
20822
|
if (manager) {
|
|
@@ -21879,8 +22708,12 @@ async function restoreFromSnapshot(indexName, data, deps) {
|
|
|
21879
22708
|
embeddingAdapterName: adapterName,
|
|
21880
22709
|
vectorFieldPaths: vectorFieldPaths2,
|
|
21881
22710
|
indexUuid: null,
|
|
21882
|
-
heldPartitions: null
|
|
22711
|
+
heldPartitions: null,
|
|
22712
|
+
documentCount: 0,
|
|
22713
|
+
partitionCount: envelope.partitions.length
|
|
21883
22714
|
});
|
|
22715
|
+
await deps.indexState.registerOpen(indexName);
|
|
22716
|
+
const release = await deps.indexState.acquire(indexName, false);
|
|
21884
22717
|
try {
|
|
21885
22718
|
const manager = deps.requireManager(indexName);
|
|
21886
22719
|
while (manager.partitionCount < envelope.partitions.length) {
|
|
@@ -21900,26 +22733,36 @@ async function restoreFromSnapshot(indexName, data, deps) {
|
|
|
21900
22733
|
}
|
|
21901
22734
|
}
|
|
21902
22735
|
}
|
|
22736
|
+
const entry = deps.indexRegistry.get(indexName);
|
|
22737
|
+
if (entry !== void 0) {
|
|
22738
|
+
entry.documentCount = manager.countDocuments();
|
|
22739
|
+
entry.partitionCount = manager.partitionCount;
|
|
22740
|
+
}
|
|
22741
|
+
if (deps.durability) {
|
|
22742
|
+
for (let partitionId = 0; partitionId < manager.partitionCount; partitionId++) {
|
|
22743
|
+
for (const docId of manager.getPartition(partitionId).docIds()) {
|
|
22744
|
+
const document = manager.get(docId);
|
|
22745
|
+
if (document !== void 0) {
|
|
22746
|
+
await deps.durability.recordInsertOrUpdate(indexName, docId, document, noopApply2);
|
|
22747
|
+
}
|
|
22748
|
+
}
|
|
22749
|
+
}
|
|
22750
|
+
await deps.durability.manager.persistMetadata(indexName);
|
|
22751
|
+
await deps.durability.manager.checkpoint(indexName);
|
|
22752
|
+
}
|
|
21903
22753
|
} catch (err) {
|
|
21904
22754
|
try {
|
|
21905
22755
|
deps.executor.dropIndex(indexName);
|
|
21906
|
-
deps.indexRegistry.delete(indexName);
|
|
21907
22756
|
} catch (_) {
|
|
21908
22757
|
}
|
|
21909
|
-
|
|
21910
|
-
|
|
21911
|
-
|
|
21912
|
-
|
|
21913
|
-
for (let partitionId = 0; partitionId < manager.partitionCount; partitionId++) {
|
|
21914
|
-
for (const docId of manager.getPartition(partitionId).docIds()) {
|
|
21915
|
-
const document = manager.get(docId);
|
|
21916
|
-
if (document !== void 0) {
|
|
21917
|
-
await deps.durability.recordInsertOrUpdate(indexName, docId, document, noopApply2);
|
|
21918
|
-
}
|
|
21919
|
-
}
|
|
22758
|
+
deps.indexRegistry.delete(indexName);
|
|
22759
|
+
deps.indexState.forget(indexName);
|
|
22760
|
+
if (deps.durability) {
|
|
22761
|
+
await deps.durability.manager.removeIndex(indexName).catch(() => void 0);
|
|
21920
22762
|
}
|
|
21921
|
-
|
|
21922
|
-
|
|
22763
|
+
throw err;
|
|
22764
|
+
} finally {
|
|
22765
|
+
release();
|
|
21923
22766
|
}
|
|
21924
22767
|
}
|
|
21925
22768
|
function noopApply2() {
|
|
@@ -23207,85 +24050,107 @@ function broadcastStatsFor(core, options) {
|
|
|
23207
24050
|
}
|
|
23208
24051
|
async function runEngineQuery(core, indexName, params, options) {
|
|
23209
24052
|
core.guardShutdown();
|
|
23210
|
-
const
|
|
23211
|
-
const manager = core.requireManager(indexName);
|
|
23212
|
-
const resolvedParams = await resolveVectorText(
|
|
23213
|
-
scopedParams(params),
|
|
23214
|
-
entry.embeddingAdapter,
|
|
23215
|
-
core.abortController.signal,
|
|
23216
|
-
entry.embeddingAdapterName
|
|
23217
|
-
);
|
|
23218
|
-
await core.pluginRegistry.runHook("beforeSearch", { indexName, params: resolvedParams });
|
|
23219
|
-
const workerSearch = core.orchestrator.isPromoted() ? core.orchestrator.searchViaWorker.bind(core.orchestrator) : void 0;
|
|
23220
|
-
const result = await executeQuery(resolvedParams, {
|
|
23221
|
-
manager,
|
|
23222
|
-
language: entry.language,
|
|
23223
|
-
config: entry.config,
|
|
23224
|
-
workerSearch,
|
|
23225
|
-
indexName,
|
|
23226
|
-
broadcastStats: broadcastStatsFor(core),
|
|
23227
|
-
partitionIds: options?.partitionIds,
|
|
23228
|
-
cursorBinding: queryBindingOf(params)
|
|
23229
|
-
});
|
|
24053
|
+
const release = await core.indexState.acquire(indexName);
|
|
23230
24054
|
try {
|
|
23231
|
-
|
|
24055
|
+
const entry = core.requireIndex(indexName);
|
|
24056
|
+
const manager = core.requireManager(indexName);
|
|
24057
|
+
const resolvedParams = await resolveVectorText(
|
|
24058
|
+
scopedParams(params, options),
|
|
24059
|
+
entry.embeddingAdapter,
|
|
24060
|
+
core.abortController.signal,
|
|
24061
|
+
entry.embeddingAdapterName
|
|
24062
|
+
);
|
|
24063
|
+
await core.pluginRegistry.runHook("beforeSearch", { indexName, params: resolvedParams });
|
|
24064
|
+
const workerSearch = core.orchestrator.isPromoted() ? core.orchestrator.searchViaWorker.bind(core.orchestrator) : void 0;
|
|
24065
|
+
const result = await executeQuery(resolvedParams, {
|
|
24066
|
+
manager,
|
|
24067
|
+
language: entry.language,
|
|
24068
|
+
config: entry.config,
|
|
24069
|
+
workerSearch,
|
|
23232
24070
|
indexName,
|
|
23233
|
-
|
|
23234
|
-
|
|
24071
|
+
broadcastStats: broadcastStatsFor(core, options),
|
|
24072
|
+
partitionIds: options?.partitionIds,
|
|
24073
|
+
cursorBinding: queryBindingOf(params)
|
|
23235
24074
|
});
|
|
23236
|
-
|
|
23237
|
-
|
|
23238
|
-
|
|
23239
|
-
|
|
23240
|
-
|
|
24075
|
+
try {
|
|
24076
|
+
await core.pluginRegistry.runHook("afterSearch", {
|
|
24077
|
+
indexName,
|
|
24078
|
+
params: resolvedParams,
|
|
24079
|
+
results: result
|
|
24080
|
+
});
|
|
24081
|
+
} catch (err) {
|
|
24082
|
+
console.warn("afterSearch plugin hook error:", err);
|
|
24083
|
+
}
|
|
24084
|
+
if (core.analysisRebuild.isStale(indexName)) {
|
|
24085
|
+
result.analysisStale = true;
|
|
24086
|
+
}
|
|
24087
|
+
return result;
|
|
24088
|
+
} finally {
|
|
24089
|
+
release();
|
|
23241
24090
|
}
|
|
23242
|
-
return result;
|
|
23243
24091
|
}
|
|
23244
24092
|
async function runEnginePreflight(core, indexName, params, options) {
|
|
23245
24093
|
core.guardShutdown();
|
|
23246
|
-
const
|
|
23247
|
-
|
|
23248
|
-
|
|
23249
|
-
|
|
23250
|
-
|
|
23251
|
-
|
|
23252
|
-
|
|
23253
|
-
|
|
23254
|
-
|
|
23255
|
-
|
|
23256
|
-
|
|
23257
|
-
|
|
23258
|
-
|
|
23259
|
-
|
|
23260
|
-
|
|
23261
|
-
|
|
23262
|
-
|
|
23263
|
-
|
|
23264
|
-
|
|
23265
|
-
|
|
23266
|
-
|
|
24094
|
+
const release = await core.indexState.acquire(indexName);
|
|
24095
|
+
try {
|
|
24096
|
+
const entry = core.requireIndex(indexName);
|
|
24097
|
+
const manager = core.requireManager(indexName);
|
|
24098
|
+
const resolvedParams = await resolveVectorText(
|
|
24099
|
+
scopedParams(params, options),
|
|
24100
|
+
entry.embeddingAdapter,
|
|
24101
|
+
core.abortController.signal,
|
|
24102
|
+
entry.embeddingAdapterName
|
|
24103
|
+
);
|
|
24104
|
+
const workerSearch = core.orchestrator.isPromoted() ? core.orchestrator.searchViaWorker.bind(core.orchestrator) : void 0;
|
|
24105
|
+
const result = await executePreflight(resolvedParams, {
|
|
24106
|
+
manager,
|
|
24107
|
+
language: entry.language,
|
|
24108
|
+
config: entry.config,
|
|
24109
|
+
workerSearch,
|
|
24110
|
+
indexName,
|
|
24111
|
+
broadcastStats: broadcastStatsFor(core, options),
|
|
24112
|
+
partitionIds: options?.partitionIds,
|
|
24113
|
+
cursorBinding: queryBindingOf(params)
|
|
24114
|
+
});
|
|
24115
|
+
if (core.analysisRebuild.isStale(indexName)) {
|
|
24116
|
+
result.analysisStale = true;
|
|
24117
|
+
}
|
|
24118
|
+
return result;
|
|
24119
|
+
} finally {
|
|
24120
|
+
release();
|
|
23267
24121
|
}
|
|
23268
|
-
return result;
|
|
23269
24122
|
}
|
|
23270
24123
|
async function runEngineSuggest(core, indexName, params, partitionIds) {
|
|
23271
24124
|
core.guardShutdown();
|
|
23272
|
-
const
|
|
23273
|
-
|
|
23274
|
-
|
|
23275
|
-
|
|
23276
|
-
|
|
23277
|
-
|
|
24125
|
+
const release = await core.indexState.acquire(indexName);
|
|
24126
|
+
try {
|
|
24127
|
+
const result = executeSuggest(
|
|
24128
|
+
core.requireManager(indexName),
|
|
24129
|
+
core.requireIndex(indexName).language,
|
|
24130
|
+
params,
|
|
24131
|
+
partitionIds
|
|
24132
|
+
);
|
|
24133
|
+
if (core.analysisRebuild.isStale(indexName)) {
|
|
24134
|
+
result.analysisStale = true;
|
|
24135
|
+
}
|
|
24136
|
+
return result;
|
|
24137
|
+
} finally {
|
|
24138
|
+
release();
|
|
23278
24139
|
}
|
|
23279
|
-
return result;
|
|
23280
24140
|
}
|
|
23281
24141
|
async function runEngineListDocuments(core, indexName, params, partitionIds) {
|
|
23282
24142
|
core.guardShutdown();
|
|
23283
|
-
const
|
|
23284
|
-
|
|
23285
|
-
|
|
23286
|
-
|
|
23287
|
-
|
|
23288
|
-
|
|
24143
|
+
const release = await core.indexState.acquire(indexName);
|
|
24144
|
+
try {
|
|
24145
|
+
const entry = core.requireIndex(indexName);
|
|
24146
|
+
return executeListDocuments(params, {
|
|
24147
|
+
manager: core.requireManager(indexName),
|
|
24148
|
+
schema: entry.config.schema,
|
|
24149
|
+
partitionIds
|
|
24150
|
+
});
|
|
24151
|
+
} finally {
|
|
24152
|
+
release();
|
|
24153
|
+
}
|
|
23289
24154
|
}
|
|
23290
24155
|
|
|
23291
24156
|
// src/narsil/operations.ts
|
|
@@ -23305,6 +24170,16 @@ function createNarsilFromCore(core, config) {
|
|
|
23305
24170
|
mutationCtx,
|
|
23306
24171
|
rebalanceCtx
|
|
23307
24172
|
} = core;
|
|
24173
|
+
async function withOpenIndex(indexName, action) {
|
|
24174
|
+
guardShutdown();
|
|
24175
|
+
requireIndex(indexName);
|
|
24176
|
+
const release = await core.indexState.acquire(indexName);
|
|
24177
|
+
try {
|
|
24178
|
+
return await action();
|
|
24179
|
+
} finally {
|
|
24180
|
+
release();
|
|
24181
|
+
}
|
|
24182
|
+
}
|
|
23308
24183
|
const narsil = {
|
|
23309
24184
|
createIndex(name, indexConfig) {
|
|
23310
24185
|
return createEngineIndex(core, config, name, indexConfig);
|
|
@@ -23315,15 +24190,27 @@ function createNarsilFromCore(core, config) {
|
|
|
23315
24190
|
dropIndex(name) {
|
|
23316
24191
|
return dropEngineIndex(core, name);
|
|
23317
24192
|
},
|
|
24193
|
+
open(indexName) {
|
|
24194
|
+
guardShutdown();
|
|
24195
|
+
requireIndex(indexName);
|
|
24196
|
+
return core.indexState.open(indexName);
|
|
24197
|
+
},
|
|
24198
|
+
close(indexName) {
|
|
24199
|
+
guardShutdown();
|
|
24200
|
+
requireIndex(indexName);
|
|
24201
|
+
return core.indexState.close(indexName);
|
|
24202
|
+
},
|
|
23318
24203
|
listIndexes() {
|
|
23319
24204
|
const infos = [];
|
|
23320
24205
|
for (const [name, entry] of indexRegistry) {
|
|
23321
24206
|
const manager = executor.getManager(name);
|
|
23322
24207
|
infos.push({
|
|
23323
24208
|
name,
|
|
23324
|
-
documentCount: manager?.countDocuments() ??
|
|
23325
|
-
partitionCount: manager?.partitionCount ??
|
|
24209
|
+
documentCount: manager?.countDocuments() ?? entry.documentCount,
|
|
24210
|
+
partitionCount: manager?.partitionCount ?? entry.partitionCount,
|
|
23326
24211
|
language: entry.language.name,
|
|
24212
|
+
state: core.indexState.stateOf(name),
|
|
24213
|
+
reopenCount: core.indexState.reopenCount(name),
|
|
23327
24214
|
...core.analysisRebuild.isStale(name) ? { analysisStale: true } : {}
|
|
23328
24215
|
});
|
|
23329
24216
|
}
|
|
@@ -23334,8 +24221,8 @@ function createNarsilFromCore(core, config) {
|
|
|
23334
24221
|
const entry = requireIndex(indexName);
|
|
23335
24222
|
const manager = executor.getManager(indexName);
|
|
23336
24223
|
return {
|
|
23337
|
-
documentCount: manager?.countDocuments() ??
|
|
23338
|
-
partitionCount: manager?.partitionCount ??
|
|
24224
|
+
documentCount: manager?.countDocuments() ?? entry.documentCount,
|
|
24225
|
+
partitionCount: manager?.partitionCount ?? entry.partitionCount,
|
|
23339
24226
|
estimatedMemoryBytes: manager?.estimateMemoryBytes() ?? 0,
|
|
23340
24227
|
language: entry.language.name,
|
|
23341
24228
|
schema: entry.config.schema
|
|
@@ -23347,47 +24234,48 @@ function createNarsilFromCore(core, config) {
|
|
|
23347
24234
|
return executor.getManager(indexName)?.getPartitionStats() ?? [];
|
|
23348
24235
|
},
|
|
23349
24236
|
insert(indexName, document, docId, options) {
|
|
23350
|
-
return insertDocument(mutationCtx, indexName, document, docId, options);
|
|
24237
|
+
return withOpenIndex(indexName, () => insertDocument(mutationCtx, indexName, document, docId, options));
|
|
23351
24238
|
},
|
|
23352
24239
|
insertBatch(indexName, documents, options) {
|
|
23353
|
-
return insertDocumentBatch(mutationCtx, indexName, documents, options);
|
|
24240
|
+
return withOpenIndex(indexName, () => insertDocumentBatch(mutationCtx, indexName, documents, options));
|
|
23354
24241
|
},
|
|
23355
24242
|
remove(indexName, docId) {
|
|
23356
|
-
return removeDocument(mutationCtx, indexName, docId);
|
|
24243
|
+
return withOpenIndex(indexName, () => removeDocument(mutationCtx, indexName, docId));
|
|
23357
24244
|
},
|
|
23358
24245
|
removeBatch(indexName, docIds) {
|
|
23359
|
-
return removeDocumentBatch(mutationCtx, indexName, docIds);
|
|
24246
|
+
return withOpenIndex(indexName, () => removeDocumentBatch(mutationCtx, indexName, docIds));
|
|
23360
24247
|
},
|
|
23361
24248
|
update(indexName, docId, document) {
|
|
23362
|
-
return updateDocument(mutationCtx, indexName, docId, document);
|
|
24249
|
+
return withOpenIndex(indexName, () => updateDocument(mutationCtx, indexName, docId, document));
|
|
23363
24250
|
},
|
|
23364
24251
|
updateBatch(indexName, updates) {
|
|
23365
|
-
return updateDocumentBatch(mutationCtx, indexName, updates);
|
|
24252
|
+
return withOpenIndex(indexName, () => updateDocumentBatch(mutationCtx, indexName, updates));
|
|
23366
24253
|
},
|
|
23367
24254
|
async get(indexName, docId) {
|
|
23368
|
-
|
|
23369
|
-
requireIndex(indexName);
|
|
23370
|
-
return executor.execute({ type: "get", indexName, docId, requestId: docId });
|
|
24255
|
+
return withOpenIndex(indexName, () => executor.execute({ type: "get", indexName, docId, requestId: docId }));
|
|
23371
24256
|
},
|
|
23372
24257
|
async getMultiple(indexName, docIds) {
|
|
23373
24258
|
guardShutdown();
|
|
23374
24259
|
requireIndex(indexName);
|
|
23375
|
-
|
|
23376
|
-
|
|
23377
|
-
const
|
|
23378
|
-
|
|
23379
|
-
|
|
23380
|
-
|
|
24260
|
+
return withOpenIndex(indexName, async () => {
|
|
24261
|
+
const result = /* @__PURE__ */ new Map();
|
|
24262
|
+
for (const docId of docIds) {
|
|
24263
|
+
const doc = await executor.execute({
|
|
24264
|
+
type: "get",
|
|
24265
|
+
indexName,
|
|
24266
|
+
docId,
|
|
24267
|
+
requestId: docId
|
|
24268
|
+
});
|
|
24269
|
+
if (doc !== void 0) result.set(docId, doc);
|
|
24270
|
+
}
|
|
24271
|
+
return result;
|
|
24272
|
+
});
|
|
23381
24273
|
},
|
|
23382
24274
|
async has(indexName, docId) {
|
|
23383
|
-
|
|
23384
|
-
requireIndex(indexName);
|
|
23385
|
-
return executor.execute({ type: "has", indexName, docId, requestId: docId });
|
|
24275
|
+
return withOpenIndex(indexName, () => executor.execute({ type: "has", indexName, docId, requestId: docId }));
|
|
23386
24276
|
},
|
|
23387
24277
|
async countDocuments(indexName) {
|
|
23388
|
-
|
|
23389
|
-
requireIndex(indexName);
|
|
23390
|
-
return executor.execute({ type: "count", indexName, requestId: indexName });
|
|
24278
|
+
return withOpenIndex(indexName, () => executor.execute({ type: "count", indexName, requestId: indexName }));
|
|
23391
24279
|
},
|
|
23392
24280
|
async listDocuments(indexName, params) {
|
|
23393
24281
|
return runEngineListDocuments(core, indexName, params ?? {});
|
|
@@ -23404,15 +24292,15 @@ function createNarsilFromCore(core, config) {
|
|
|
23404
24292
|
async rebuildAnalysis(indexName) {
|
|
23405
24293
|
guardShutdown();
|
|
23406
24294
|
requireIndex(indexName);
|
|
23407
|
-
await core.analysisRebuild.rebuild(indexName);
|
|
24295
|
+
await withOpenIndex(indexName, () => core.analysisRebuild.rebuild(indexName));
|
|
23408
24296
|
},
|
|
23409
24297
|
async snapshot(indexName) {
|
|
23410
24298
|
guardShutdown();
|
|
23411
|
-
return createSnapshot(requireManager(indexName), requireIndex(indexName));
|
|
24299
|
+
return withOpenIndex(indexName, () => createSnapshot(requireManager(indexName), requireIndex(indexName)));
|
|
23412
24300
|
},
|
|
23413
24301
|
async restore(indexName, data) {
|
|
23414
24302
|
guardShutdown();
|
|
23415
|
-
|
|
24303
|
+
await restoreFromSnapshot(indexName, data, {
|
|
23416
24304
|
executor,
|
|
23417
24305
|
indexRegistry,
|
|
23418
24306
|
getVectorFieldPaths,
|
|
@@ -23422,66 +24310,74 @@ function createNarsilFromCore(core, config) {
|
|
|
23422
24310
|
embeddingAdapters: core.embeddingAdapters,
|
|
23423
24311
|
defaultEmbeddingAdapter: config?.embedding ?? null,
|
|
23424
24312
|
markAnalysisStale: core.analysisRebuild.markStale,
|
|
23425
|
-
clearAnalysisStale: core.analysisRebuild.clearStale
|
|
24313
|
+
clearAnalysisStale: core.analysisRebuild.clearStale,
|
|
24314
|
+
indexState: core.indexState
|
|
23426
24315
|
});
|
|
23427
24316
|
},
|
|
23428
24317
|
async checkpoint(indexName) {
|
|
23429
24318
|
guardShutdown();
|
|
23430
24319
|
requireIndex(indexName);
|
|
23431
|
-
|
|
23432
|
-
await durability.manager.checkpoint(indexName);
|
|
23433
|
-
}
|
|
24320
|
+
await withOpenIndex(indexName, async () => {
|
|
24321
|
+
if (durability) await durability.manager.checkpoint(indexName);
|
|
24322
|
+
});
|
|
23434
24323
|
},
|
|
23435
24324
|
async clear(indexName) {
|
|
23436
24325
|
guardShutdown();
|
|
23437
24326
|
requireIndex(indexName);
|
|
23438
|
-
await
|
|
23439
|
-
|
|
23440
|
-
|
|
24327
|
+
await withOpenIndex(indexName, async () => {
|
|
24328
|
+
await executor.execute({ type: "clear", indexName, requestId: indexName });
|
|
24329
|
+
await orchestrator.replicateToWorkers({ type: "clear", indexName, requestId: `replicate-clear-${indexName}` });
|
|
24330
|
+
core.watermarkNotifier.forget(indexName);
|
|
24331
|
+
});
|
|
23441
24332
|
},
|
|
23442
24333
|
async rebalance(indexName, targetPartitionCount) {
|
|
23443
24334
|
guardShutdown();
|
|
23444
24335
|
requireIndex(indexName);
|
|
23445
|
-
return
|
|
24336
|
+
return withOpenIndex(
|
|
24337
|
+
indexName,
|
|
24338
|
+
() => executeRebalance(requireManager(indexName), indexName, targetPartitionCount, rebalanceCtx)
|
|
24339
|
+
);
|
|
23446
24340
|
},
|
|
23447
24341
|
async updatePartitionConfig(indexName, partitionConfig) {
|
|
23448
24342
|
guardShutdown();
|
|
23449
|
-
|
|
23450
|
-
|
|
23451
|
-
|
|
23452
|
-
|
|
23453
|
-
|
|
23454
|
-
|
|
23455
|
-
|
|
23456
|
-
|
|
23457
|
-
|
|
23458
|
-
|
|
23459
|
-
|
|
23460
|
-
ErrorCodes.PARTITION_CAPACITY_EXCEEDED,
|
|
23461
|
-
`maxPartitions (${partitionConfig.maxPartitions}) is less than the current partition count (${manager.partitionCount})`,
|
|
23462
|
-
{ maxPartitions: partitionConfig.maxPartitions, partitionCount: manager.partitionCount }
|
|
23463
|
-
);
|
|
23464
|
-
}
|
|
23465
|
-
const currentDocCount = manager.countDocuments();
|
|
23466
|
-
const newMaxDocs = partitionConfig.maxDocsPerPartition ?? entry.config.partitions?.maxDocsPerPartition;
|
|
23467
|
-
if (newMaxDocs !== void 0) {
|
|
23468
|
-
const newTotalCapacity = newMaxDocs * manager.partitionCount;
|
|
23469
|
-
if (newTotalCapacity < currentDocCount) {
|
|
24343
|
+
return withOpenIndex(indexName, async () => {
|
|
24344
|
+
const entry = requireIndex(indexName);
|
|
24345
|
+
const manager = requireManager(indexName);
|
|
24346
|
+
if (rebalancingIndexes.has(indexName)) {
|
|
24347
|
+
throw new NarsilError(
|
|
24348
|
+
ErrorCodes.PARTITION_REBALANCING_BACKPRESSURE,
|
|
24349
|
+
`Index "${indexName}" is currently being rebalanced`
|
|
24350
|
+
);
|
|
24351
|
+
}
|
|
24352
|
+
validatePartitionConfig(partitionConfig);
|
|
24353
|
+
if (partitionConfig.maxPartitions !== void 0 && partitionConfig.maxPartitions < manager.partitionCount) {
|
|
23470
24354
|
throw new NarsilError(
|
|
23471
24355
|
ErrorCodes.PARTITION_CAPACITY_EXCEEDED,
|
|
23472
|
-
`
|
|
23473
|
-
{
|
|
24356
|
+
`maxPartitions (${partitionConfig.maxPartitions}) is less than the current partition count (${manager.partitionCount})`,
|
|
24357
|
+
{ maxPartitions: partitionConfig.maxPartitions, partitionCount: manager.partitionCount }
|
|
23474
24358
|
);
|
|
23475
24359
|
}
|
|
23476
|
-
|
|
23477
|
-
|
|
23478
|
-
|
|
23479
|
-
|
|
23480
|
-
|
|
23481
|
-
|
|
23482
|
-
|
|
23483
|
-
|
|
23484
|
-
|
|
24360
|
+
const currentDocCount = manager.countDocuments();
|
|
24361
|
+
const newMaxDocs = partitionConfig.maxDocsPerPartition ?? entry.config.partitions?.maxDocsPerPartition;
|
|
24362
|
+
if (newMaxDocs !== void 0) {
|
|
24363
|
+
const newTotalCapacity = newMaxDocs * manager.partitionCount;
|
|
24364
|
+
if (newTotalCapacity < currentDocCount) {
|
|
24365
|
+
throw new NarsilError(
|
|
24366
|
+
ErrorCodes.PARTITION_CAPACITY_EXCEEDED,
|
|
24367
|
+
`New capacity (${newTotalCapacity}) is less than current document count (${currentDocCount})`,
|
|
24368
|
+
{ newTotalCapacity, currentDocCount }
|
|
24369
|
+
);
|
|
24370
|
+
}
|
|
24371
|
+
}
|
|
24372
|
+
if (!entry.config.partitions) entry.config.partitions = {};
|
|
24373
|
+
if (partitionConfig.maxDocsPerPartition !== void 0)
|
|
24374
|
+
entry.config.partitions.maxDocsPerPartition = partitionConfig.maxDocsPerPartition;
|
|
24375
|
+
if (partitionConfig.maxPartitions !== void 0)
|
|
24376
|
+
entry.config.partitions.maxPartitions = partitionConfig.maxPartitions;
|
|
24377
|
+
if (partitionConfig.watermark !== void 0) entry.config.partitions.watermark = partitionConfig.watermark;
|
|
24378
|
+
if (durability) await durability.manager.persistMetadata(indexName);
|
|
24379
|
+
core.watermarkNotifier.check(indexName);
|
|
24380
|
+
});
|
|
23485
24381
|
},
|
|
23486
24382
|
async getMemoryStats() {
|
|
23487
24383
|
let estimatedIndexBytes = 0;
|
|
@@ -23489,22 +24385,33 @@ function createNarsilFromCore(core, config) {
|
|
|
23489
24385
|
estimatedIndexBytes += executor.getManager(name)?.estimateMemoryBytes() ?? 0;
|
|
23490
24386
|
}
|
|
23491
24387
|
const workers = await orchestrator.getWorkerMemoryStats();
|
|
23492
|
-
|
|
24388
|
+
const lifecycleCounts = core.indexState.counts();
|
|
24389
|
+
return {
|
|
24390
|
+
process: readProcessMemory(),
|
|
24391
|
+
estimatedIndexBytes,
|
|
24392
|
+
openIndexCount: lifecycleCounts.open,
|
|
24393
|
+
closedIndexCount: lifecycleCounts.closed,
|
|
24394
|
+
reopenCount: lifecycleCounts.reopens,
|
|
24395
|
+
workers
|
|
24396
|
+
};
|
|
23493
24397
|
},
|
|
23494
24398
|
async compactVectors(indexName, fieldName) {
|
|
23495
24399
|
guardShutdown();
|
|
23496
24400
|
requireIndex(indexName);
|
|
23497
|
-
|
|
24401
|
+
await withOpenIndex(indexName, async () => {
|
|
24402
|
+
compactVectors(requireManager(indexName), indexName, fieldName);
|
|
24403
|
+
});
|
|
23498
24404
|
},
|
|
23499
24405
|
async optimizeVectors(indexName, fieldName) {
|
|
23500
24406
|
guardShutdown();
|
|
23501
24407
|
requireIndex(indexName);
|
|
23502
|
-
await optimizeVectors(requireManager(indexName), indexName, fieldName);
|
|
24408
|
+
await withOpenIndex(indexName, () => optimizeVectors(requireManager(indexName), indexName, fieldName));
|
|
23503
24409
|
},
|
|
23504
24410
|
vectorMaintenanceStatus(indexName) {
|
|
23505
24411
|
guardShutdown();
|
|
23506
24412
|
requireIndex(indexName);
|
|
23507
|
-
|
|
24413
|
+
const manager = executor.getManager(indexName);
|
|
24414
|
+
return manager === void 0 ? [] : getVectorMaintenanceStatus(manager);
|
|
23508
24415
|
},
|
|
23509
24416
|
on(event, handler) {
|
|
23510
24417
|
const key = event;
|
|
@@ -23537,12 +24444,12 @@ function createNarsilFromCore(core, config) {
|
|
|
23537
24444
|
async function createNarsil(config) {
|
|
23538
24445
|
const core = createEngineCore(config);
|
|
23539
24446
|
if (core.durability) {
|
|
23540
|
-
await core.durability.manager.recover();
|
|
24447
|
+
await core.durability.manager.recover(config?.lifecycle !== void 0);
|
|
23541
24448
|
}
|
|
23542
24449
|
if (core.invalidation) {
|
|
23543
24450
|
await core.invalidation.start();
|
|
23544
24451
|
}
|
|
23545
|
-
await core.analysisRebuild.reviewStaleIndexes();
|
|
24452
|
+
if (config?.lifecycle === void 0) await core.analysisRebuild.reviewStaleIndexes();
|
|
23546
24453
|
return createNarsilFromCore(core, config);
|
|
23547
24454
|
}
|
|
23548
24455
|
|