@delali/narsil 0.2.4-next.42 → 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-GB2IIJJG.mjs → chunk-P4ELXVJQ.mjs} +2 -4
- package/dist/{chunk-MMSWEEZT.mjs → chunk-WASNWIEK.mjs} +1452 -373
- package/dist/{chunk-GRWFY2HI.mjs → chunk-XNCJFU2E.mjs} +239 -61
- package/dist/{chunk-WU2UZEVD.mjs → chunk-XWICBGYT.mjs} +1 -1
- package/dist/{client-BobVnoIK.d.ts → client-C5BkUqB8.d.ts} +26 -3
- package/dist/client.d.ts +8 -8
- package/dist/client.mjs +12 -6
- package/dist/{config-fLC4lbmg.d.ts → config-B-ZI-ft5.d.ts} +2 -2
- package/dist/{config-Byyl1QqH.d.ts → config-BZRExglL.d.ts} +20 -3
- package/dist/{direct-executor-MSJEVPZU.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 +1721 -1129
- package/dist/embeddings/openai.mjs +1 -1
- package/dist/{engine-BE45D5ZG.d.ts → engine-Cjr1uBLa.d.ts} +47 -24
- package/dist/index.browser.mjs +1668 -416
- package/dist/index.d.ts +9 -9
- package/dist/index.mjs +4 -4
- package/dist/{options-EXYFcem-.d.ts → options-BC68VPoE.d.ts} +10 -3
- package/dist/persistence/durability/checkpoint-worker.d.ts +3 -1
- package/dist/persistence/durability/checkpoint-worker.mjs +4 -4
- package/dist/react.d.ts +6 -6
- package/dist/react.mjs +2 -4
- package/dist/{schema-ZRbk8BgH.d.ts → schema-B7juWg8Q.d.ts} +17 -6
- package/dist/{search-DHuXwJYk.d.ts → search-DDHjfXhJ.d.ts} +35 -11
- package/dist/server.d.ts +10 -10
- package/dist/server.mjs +30 -12
- package/dist/{tasks-CnMLpfk-.d.ts → tasks-Bn4R1NPN.d.ts} +1 -1
- package/dist/{types-B5Tsaalz.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
|
@@ -3,7 +3,6 @@ import { encode, decode } from '@msgpack/msgpack';
|
|
|
3
3
|
// src/errors.ts
|
|
4
4
|
var ErrorCodes = {
|
|
5
5
|
SCHEMA_INVALID_TYPE: "SCHEMA_INVALID_TYPE",
|
|
6
|
-
SCHEMA_MISSING_FIELD: "SCHEMA_MISSING_FIELD",
|
|
7
6
|
SCHEMA_DEPTH_EXCEEDED: "SCHEMA_DEPTH_EXCEEDED",
|
|
8
7
|
SCHEMA_INVALID_VECTOR_DIMENSION: "SCHEMA_INVALID_VECTOR_DIMENSION",
|
|
9
8
|
SCHEMA_INVALID_GEOPOINT: "SCHEMA_INVALID_GEOPOINT",
|
|
@@ -13,6 +12,7 @@ var ErrorCodes = {
|
|
|
13
12
|
INDEX_NOT_FOUND: "INDEX_NOT_FOUND",
|
|
14
13
|
INDEX_ALREADY_EXISTS: "INDEX_ALREADY_EXISTS",
|
|
15
14
|
INDEX_ORPHANED: "INDEX_ORPHANED",
|
|
15
|
+
INDEX_REOPEN_CAPACITY_EXHAUSTED: "INDEX_REOPEN_CAPACITY_EXHAUSTED",
|
|
16
16
|
PARTITION_CORRUPTED: "PARTITION_CORRUPTED",
|
|
17
17
|
PARTITION_REBALANCING_BACKPRESSURE: "PARTITION_REBALANCING_BACKPRESSURE",
|
|
18
18
|
WORKER_CRASHED: "WORKER_CRASHED",
|
|
@@ -25,7 +25,6 @@ var ErrorCodes = {
|
|
|
25
25
|
PERSISTENCE_WAL_CORRUPT: "PERSISTENCE_WAL_CORRUPT",
|
|
26
26
|
PERSISTENCE_FSYNC_FAILED: "PERSISTENCE_FSYNC_FAILED",
|
|
27
27
|
SEARCH_INVALID_FIELD: "SEARCH_INVALID_FIELD",
|
|
28
|
-
SEARCH_INVALID_VECTOR_SIZE: "SEARCH_INVALID_VECTOR_SIZE",
|
|
29
28
|
VECTOR_DIMENSION_MISMATCH: "VECTOR_DIMENSION_MISMATCH",
|
|
30
29
|
SEARCH_INVALID_FILTER: "SEARCH_INVALID_FILTER",
|
|
31
30
|
SEARCH_INVALID_MODE: "SEARCH_INVALID_MODE",
|
|
@@ -49,8 +48,6 @@ var ErrorCodes = {
|
|
|
49
48
|
ALLOCATION_NO_DATA_NODES: "ALLOCATION_NO_DATA_NODES",
|
|
50
49
|
ALLOCATION_INVALID_CONFIG: "ALLOCATION_INVALID_CONFIG",
|
|
51
50
|
ALLOCATION_FAILED: "ALLOCATION_FAILED",
|
|
52
|
-
CONTROLLER_LEASE_LOST: "CONTROLLER_LEASE_LOST",
|
|
53
|
-
CONTROLLER_NOT_ACTIVE: "CONTROLLER_NOT_ACTIVE",
|
|
54
51
|
CONTROLLER_METADATA_INVALID: "CONTROLLER_METADATA_INVALID",
|
|
55
52
|
NODE_BOOTSTRAP_FAILED: "NODE_BOOTSTRAP_FAILED",
|
|
56
53
|
NODE_ALREADY_JOINED: "NODE_ALREADY_JOINED",
|
|
@@ -106,6 +103,7 @@ var ServerErrorCodes = {
|
|
|
106
103
|
TASK_NOT_FOUND: "TASK_NOT_FOUND",
|
|
107
104
|
TASK_NOT_CANCELLABLE: "TASK_NOT_CANCELLABLE",
|
|
108
105
|
TASK_OWNED_BY_ANOTHER_INSTANCE: "TASK_OWNED_BY_ANOTHER_INSTANCE",
|
|
106
|
+
TASK_INTERRUPTED: "TASK_INTERRUPTED",
|
|
109
107
|
TOO_MANY_REQUESTS: "TOO_MANY_REQUESTS",
|
|
110
108
|
HOOK_ERROR: "HOOK_ERROR",
|
|
111
109
|
INTERNAL_ERROR: "INTERNAL_ERROR"
|
|
@@ -2491,6 +2489,9 @@ function metadataToWire(meta) {
|
|
|
2491
2489
|
if (meta.vectorFields) {
|
|
2492
2490
|
wire.vector_fields = meta.vectorFields;
|
|
2493
2491
|
}
|
|
2492
|
+
if (isNonNegativeInteger(meta.documentCount) && Number.isSafeInteger(meta.documentCount)) {
|
|
2493
|
+
wire.document_count = meta.documentCount;
|
|
2494
|
+
}
|
|
2494
2495
|
if (meta.embedding) {
|
|
2495
2496
|
wire.embedding = meta.embedding.adapter !== void 0 ? { adapter: meta.embedding.adapter, fields: meta.embedding.fields } : { fields: meta.embedding.fields };
|
|
2496
2497
|
}
|
|
@@ -2564,6 +2565,9 @@ function wireToMetadata(raw) {
|
|
|
2564
2565
|
if (raw.vector_fields) {
|
|
2565
2566
|
meta.vectorFields = raw.vector_fields;
|
|
2566
2567
|
}
|
|
2568
|
+
if (isNonNegativeInteger(raw.document_count) && Number.isSafeInteger(raw.document_count)) {
|
|
2569
|
+
meta.documentCount = raw.document_count;
|
|
2570
|
+
}
|
|
2567
2571
|
if (raw.embedding && typeof raw.embedding === "object" && typeof raw.embedding.fields === "object") {
|
|
2568
2572
|
meta.embedding = typeof raw.embedding.adapter === "string" ? { adapter: raw.embedding.adapter, fields: raw.embedding.fields } : { fields: raw.embedding.fields };
|
|
2569
2573
|
}
|
|
@@ -2622,7 +2626,7 @@ function wireToMetadata(raw) {
|
|
|
2622
2626
|
if (isPositiveInteger2(promotion.threshold)) {
|
|
2623
2627
|
restored.threshold = promotion.threshold;
|
|
2624
2628
|
}
|
|
2625
|
-
if (
|
|
2629
|
+
if (isSelectivityRatio(promotion.filter_threshold)) {
|
|
2626
2630
|
restored.filterThreshold = promotion.filter_threshold;
|
|
2627
2631
|
}
|
|
2628
2632
|
if (Object.keys(hnswConfig).length > 0) {
|
|
@@ -2649,6 +2653,9 @@ function isNonNegativeInteger(value) {
|
|
|
2649
2653
|
function isPositiveInteger2(value) {
|
|
2650
2654
|
return typeof value === "number" && Number.isInteger(value) && value >= 1;
|
|
2651
2655
|
}
|
|
2656
|
+
function isSelectivityRatio(value) {
|
|
2657
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
|
|
2658
|
+
}
|
|
2652
2659
|
function isWatermarkFraction(value) {
|
|
2653
2660
|
return typeof value === "number" && value > 0 && value <= 1;
|
|
2654
2661
|
}
|
|
@@ -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
|
|
|
@@ -8079,7 +8161,7 @@ function createRebalancer() {
|
|
|
8079
8161
|
skipClone: true,
|
|
8080
8162
|
stopWordOverride: manager.analysis.stopWords,
|
|
8081
8163
|
customTokenizer: manager.analysis.customTokenizer,
|
|
8082
|
-
collectSurfaces: manager.config.surfaceForms
|
|
8164
|
+
collectSurfaces: manager.config.surfaceForms !== false
|
|
8083
8165
|
};
|
|
8084
8166
|
for (const partition of currentPartitions) {
|
|
8085
8167
|
const docIds = [...partition.docIds()];
|
|
@@ -8140,8 +8222,7 @@ function createRebalancer() {
|
|
|
8140
8222
|
var FNV_OFFSET_BASIS = 2166136261;
|
|
8141
8223
|
var FNV_PRIME = 16777619;
|
|
8142
8224
|
var encoder = new TextEncoder();
|
|
8143
|
-
function
|
|
8144
|
-
const bytes = encoder.encode(input);
|
|
8225
|
+
function fnv1aBytes(bytes) {
|
|
8145
8226
|
let hash = FNV_OFFSET_BASIS;
|
|
8146
8227
|
for (let i = 0; i < bytes.length; i++) {
|
|
8147
8228
|
hash ^= bytes[i];
|
|
@@ -8149,6 +8230,9 @@ function fnv1a(input) {
|
|
|
8149
8230
|
}
|
|
8150
8231
|
return hash >>> 0;
|
|
8151
8232
|
}
|
|
8233
|
+
function fnv1a(input) {
|
|
8234
|
+
return fnv1aBytes(encoder.encode(input));
|
|
8235
|
+
}
|
|
8152
8236
|
|
|
8153
8237
|
// src/partitioning/router.ts
|
|
8154
8238
|
function createPartitionRouter() {
|
|
@@ -8439,6 +8523,9 @@ function createFrozenDocTable(idTable, lengthColumns, documents, tombstones) {
|
|
|
8439
8523
|
internalIdCapacity() {
|
|
8440
8524
|
return capacity;
|
|
8441
8525
|
},
|
|
8526
|
+
contentBytes() {
|
|
8527
|
+
return documents.byteLength;
|
|
8528
|
+
},
|
|
8442
8529
|
resolver() {
|
|
8443
8530
|
return resolver;
|
|
8444
8531
|
}
|
|
@@ -8466,21 +8553,32 @@ function wrapEncodedDocumentTable(data) {
|
|
|
8466
8553
|
const { blob, offsets } = data;
|
|
8467
8554
|
const count = offsets.length - 1;
|
|
8468
8555
|
const decoded = new Array(count);
|
|
8556
|
+
const encodedBytes = blob.byteLength + offsets.byteLength;
|
|
8557
|
+
let decodedBytes = 0;
|
|
8469
8558
|
return {
|
|
8470
8559
|
count,
|
|
8560
|
+
get byteLength() {
|
|
8561
|
+
return encodedBytes + decodedBytes;
|
|
8562
|
+
},
|
|
8471
8563
|
docAt(ordinal) {
|
|
8472
8564
|
let document = decoded[ordinal];
|
|
8473
8565
|
if (document === void 0) {
|
|
8474
8566
|
document = decode(blob.slice(offsets[ordinal], offsets[ordinal + 1]));
|
|
8475
8567
|
decoded[ordinal] = document;
|
|
8568
|
+
decodedBytes += documentValueBytes(document);
|
|
8476
8569
|
}
|
|
8477
8570
|
return document;
|
|
8478
8571
|
}
|
|
8479
8572
|
};
|
|
8480
8573
|
}
|
|
8481
8574
|
function wrapDocumentArray(documents) {
|
|
8575
|
+
let residentBytes = 0;
|
|
8576
|
+
for (const document of documents) {
|
|
8577
|
+
residentBytes += documentValueBytes(document);
|
|
8578
|
+
}
|
|
8482
8579
|
return {
|
|
8483
8580
|
count: documents.length,
|
|
8581
|
+
byteLength: residentBytes,
|
|
8484
8582
|
docAt(ordinal) {
|
|
8485
8583
|
return documents[ordinal];
|
|
8486
8584
|
}
|
|
@@ -10189,7 +10287,7 @@ function applySorting(hits, sort, sortKeyOf) {
|
|
|
10189
10287
|
}
|
|
10190
10288
|
|
|
10191
10289
|
// src/search/cursor.ts
|
|
10192
|
-
var CURSOR_VERSION =
|
|
10290
|
+
var CURSOR_VERSION = 3;
|
|
10193
10291
|
var MAX_CURSOR_LENGTH = 40960;
|
|
10194
10292
|
var MAX_SORT_FIELDS = 8;
|
|
10195
10293
|
var MAX_SORT_FIELD_NAME_LENGTH = 255;
|
|
@@ -10244,6 +10342,7 @@ function encodePageCursor(cursor) {
|
|
|
10244
10342
|
} else if (cursor.score !== null) {
|
|
10245
10343
|
payload.s = cursor.score;
|
|
10246
10344
|
}
|
|
10345
|
+
payload.q = cursor.binding;
|
|
10247
10346
|
return encodeCursorText(JSON.stringify(payload));
|
|
10248
10347
|
}
|
|
10249
10348
|
function decodeSortKey(value, cursor) {
|
|
@@ -10288,8 +10387,11 @@ function decodePageCursor(cursor) {
|
|
|
10288
10387
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
10289
10388
|
throw invalidCursor(cursor, "expected an object");
|
|
10290
10389
|
}
|
|
10291
|
-
const { v, a, s, k, o } = parsed;
|
|
10390
|
+
const { v, a, s, k, o, q } = parsed;
|
|
10292
10391
|
if (v !== CURSOR_VERSION) throw invalidCursor(cursor, `unsupported cursor version ${String(v)}`);
|
|
10392
|
+
if (typeof q !== "string" || !/^[0-9a-f]{8}$/.test(q)) {
|
|
10393
|
+
throw invalidCursor(cursor, '"q" must be 8 lowercase hex digits');
|
|
10394
|
+
}
|
|
10293
10395
|
if (typeof a !== "string" || a.length === 0) throw invalidCursor(cursor, '"a" must be a non-empty string');
|
|
10294
10396
|
if (exceedsCodePoints(a, MAX_ANCHOR_CODE_POINTS)) {
|
|
10295
10397
|
throw invalidCursor(cursor, '"a" is too long to be a document id');
|
|
@@ -10307,10 +10409,14 @@ function decodePageCursor(cursor) {
|
|
|
10307
10409
|
anchor: a,
|
|
10308
10410
|
score: typeof s === "number" ? s : null,
|
|
10309
10411
|
sortKey,
|
|
10310
|
-
sortSignature: typeof o === "string" ? o : null
|
|
10412
|
+
sortSignature: typeof o === "string" ? o : null,
|
|
10413
|
+
binding: q
|
|
10311
10414
|
};
|
|
10312
10415
|
}
|
|
10313
|
-
function requireMatchingCursor(cursor, encoded, signature, scoreAnchored) {
|
|
10416
|
+
function requireMatchingCursor(cursor, encoded, signature, scoreAnchored, binding) {
|
|
10417
|
+
if (cursor.binding !== binding) {
|
|
10418
|
+
throw invalidCursor(encoded, "the cursor belongs to a different query");
|
|
10419
|
+
}
|
|
10314
10420
|
if (signature !== null) {
|
|
10315
10421
|
if (cursor.sortSignature !== signature) {
|
|
10316
10422
|
throw invalidCursor(encoded, "the cursor belongs to a different sort order");
|
|
@@ -10344,6 +10450,12 @@ function requireWithinResultWindow(limit, offset) {
|
|
|
10344
10450
|
{ limit, offset, window: RESULT_WINDOW }
|
|
10345
10451
|
);
|
|
10346
10452
|
}
|
|
10453
|
+
function lastOrganicResult(sliced, pinnedIds) {
|
|
10454
|
+
for (let i = sliced.length - 1; i >= 0; i--) {
|
|
10455
|
+
if (pinnedIds === void 0 || !pinnedIds.has(sliced[i].id)) return sliced[i];
|
|
10456
|
+
}
|
|
10457
|
+
return void 0;
|
|
10458
|
+
}
|
|
10347
10459
|
function ordersAfterAnchor(result, anchor, sort) {
|
|
10348
10460
|
if (sort !== void 0 && anchor.sortKey !== null) {
|
|
10349
10461
|
const comparison = compareSortValues(sort.sortKeyOf(result.id), anchor.sortKey, sort.directions) || compareCodePoints(result.id, anchor.anchor);
|
|
@@ -10354,10 +10466,10 @@ function ordersAfterAnchor(result, anchor, sort) {
|
|
|
10354
10466
|
if (result.score < anchor.score) return true;
|
|
10355
10467
|
return result.score === anchor.score && compareCodePoints(result.id, anchor.anchor) > 0;
|
|
10356
10468
|
}
|
|
10357
|
-
function applyPagination(results, limit, offset, cursor, sort) {
|
|
10469
|
+
function applyPagination(results, limit, offset, binding, cursor, sort, pinnedIds) {
|
|
10358
10470
|
const decoded = cursor ? decodePageCursor(cursor) : null;
|
|
10359
10471
|
if (decoded !== null && cursor !== void 0) {
|
|
10360
|
-
requireMatchingCursor(decoded, cursor, sort?.signature ?? null, true);
|
|
10472
|
+
requireMatchingCursor(decoded, cursor, sort?.signature ?? null, true, binding);
|
|
10361
10473
|
}
|
|
10362
10474
|
if (limit <= 0) {
|
|
10363
10475
|
return { paginated: [] };
|
|
@@ -10376,18 +10488,20 @@ function applyPagination(results, limit, offset, cursor, sort) {
|
|
|
10376
10488
|
const sliced = results.slice(afterOffset, afterOffset + limit);
|
|
10377
10489
|
let nextCursor;
|
|
10378
10490
|
const hasMore = afterOffset + limit < results.length;
|
|
10379
|
-
|
|
10380
|
-
|
|
10491
|
+
const lastResult = lastOrganicResult(sliced, pinnedIds);
|
|
10492
|
+
if (hasMore && lastResult !== void 0) {
|
|
10381
10493
|
nextCursor = sort !== void 0 ? encodePageCursor({
|
|
10382
10494
|
anchor: lastResult.id,
|
|
10383
10495
|
score: null,
|
|
10384
10496
|
sortKey: sort.sortKeyOf(lastResult.id).map(toComparableSortValue),
|
|
10385
|
-
sortSignature: sort.signature
|
|
10497
|
+
sortSignature: sort.signature,
|
|
10498
|
+
binding
|
|
10386
10499
|
}) : encodePageCursor({
|
|
10387
10500
|
anchor: lastResult.id,
|
|
10388
10501
|
score: lastResult.score ?? null,
|
|
10389
10502
|
sortKey: null,
|
|
10390
|
-
sortSignature: null
|
|
10503
|
+
sortSignature: null,
|
|
10504
|
+
binding
|
|
10391
10505
|
});
|
|
10392
10506
|
}
|
|
10393
10507
|
return { paginated: sliced, nextCursor };
|
|
@@ -10587,9 +10701,24 @@ function queryTermSatisfied(queryToken, indexTokens, tolerance, exact) {
|
|
|
10587
10701
|
return false;
|
|
10588
10702
|
}
|
|
10589
10703
|
|
|
10704
|
+
// src/partitioning/partition-selection.ts
|
|
10705
|
+
function partitionsIn(manager, partitionIds) {
|
|
10706
|
+
if (partitionIds === void 0) {
|
|
10707
|
+
return manager.getAllPartitions();
|
|
10708
|
+
}
|
|
10709
|
+
const partitions = [];
|
|
10710
|
+
for (const partitionId of partitionIds) {
|
|
10711
|
+
const partition = manager.partitionAt(partitionId);
|
|
10712
|
+
if (partition !== void 0) {
|
|
10713
|
+
partitions.push(partition);
|
|
10714
|
+
}
|
|
10715
|
+
}
|
|
10716
|
+
return partitions;
|
|
10717
|
+
}
|
|
10718
|
+
|
|
10590
10719
|
// src/partitioning/fan-out.ts
|
|
10591
10720
|
async function fanOutQuery(manager, params, language, schema, config, searchOptions) {
|
|
10592
|
-
const partitions =
|
|
10721
|
+
const partitions = partitionsIn(manager, config.partitionIds);
|
|
10593
10722
|
if (partitions.length === 0) {
|
|
10594
10723
|
return { scored: [], totalMatched: 0 };
|
|
10595
10724
|
}
|
|
@@ -11018,6 +11147,19 @@ function createPartitionManager(indexName, config, language, router, initialPart
|
|
|
11018
11147
|
return manager;
|
|
11019
11148
|
}
|
|
11020
11149
|
|
|
11150
|
+
// src/partitioning/match-count.ts
|
|
11151
|
+
function countsWithoutScores(params) {
|
|
11152
|
+
return params.minScore === void 0 && (params.termMatch === void 0 || params.termMatch === "any");
|
|
11153
|
+
}
|
|
11154
|
+
function fanOutMatchCount(manager, params, language, schema, options) {
|
|
11155
|
+
let count = 0;
|
|
11156
|
+
for (const partition of partitionsIn(manager, options?.partitionIds)) {
|
|
11157
|
+
const matches = fulltextMatches(partition, params, language, schema, options?.searchOptions);
|
|
11158
|
+
if (matches !== null) count += matches.count;
|
|
11159
|
+
}
|
|
11160
|
+
return count;
|
|
11161
|
+
}
|
|
11162
|
+
|
|
11021
11163
|
// src/vector/scalar-quantization-calibration.ts
|
|
11022
11164
|
var PADDING_FACTOR = 0.01;
|
|
11023
11165
|
function computeCalibrationBounds(vectors, dimensions) {
|
|
@@ -11247,7 +11389,7 @@ function createScalarQuantizer(dimensions, ordinalSource) {
|
|
|
11247
11389
|
let liveCount = 0;
|
|
11248
11390
|
let constants = deriveSq8Constants(0, 0, dimensions);
|
|
11249
11391
|
let calibrated = false;
|
|
11250
|
-
function
|
|
11392
|
+
function ensureCapacity3(needed) {
|
|
11251
11393
|
if (needed <= capacity) return;
|
|
11252
11394
|
let newCap = capacity === 0 ? INITIAL_CAPACITY2 : capacity;
|
|
11253
11395
|
while (newCap < needed) newCap *= 2;
|
|
@@ -11377,7 +11519,7 @@ function createScalarQuantizer(dimensions, ordinalSource) {
|
|
|
11377
11519
|
liveCount = 0;
|
|
11378
11520
|
for (const [docId, vec] of collected) {
|
|
11379
11521
|
const ord = resolveOrdinal(docId);
|
|
11380
|
-
|
|
11522
|
+
ensureCapacity3(ord + 1);
|
|
11381
11523
|
quantizeInto(ord, vec);
|
|
11382
11524
|
storeEntry(docId, ord);
|
|
11383
11525
|
}
|
|
@@ -11387,7 +11529,7 @@ function createScalarQuantizer(dimensions, ordinalSource) {
|
|
|
11387
11529
|
calibrateFromVectors([vector]);
|
|
11388
11530
|
}
|
|
11389
11531
|
const ord = resolveOrdinal(docId);
|
|
11390
|
-
|
|
11532
|
+
ensureCapacity3(ord + 1);
|
|
11391
11533
|
quantizeInto(ord, vector);
|
|
11392
11534
|
storeEntry(docId, ord);
|
|
11393
11535
|
},
|
|
@@ -11454,7 +11596,7 @@ function createScalarQuantizer(dimensions, ordinalSource) {
|
|
|
11454
11596
|
},
|
|
11455
11597
|
restoreEntry(docId, quantized, sum, sumSq) {
|
|
11456
11598
|
const ord = resolveOrdinal(docId);
|
|
11457
|
-
|
|
11599
|
+
ensureCapacity3(ord + 1);
|
|
11458
11600
|
const base = ord * dimensions;
|
|
11459
11601
|
quantizedArena.set(quantized.subarray(0, dimensions), base);
|
|
11460
11602
|
sums[ord] = sum;
|
|
@@ -11605,7 +11747,7 @@ function createVectorStore() {
|
|
|
11605
11747
|
arena = new Float32Array(simd.memory.buffer);
|
|
11606
11748
|
}
|
|
11607
11749
|
}
|
|
11608
|
-
function
|
|
11750
|
+
function ensureCapacity3(needed) {
|
|
11609
11751
|
if (needed <= capacity) return;
|
|
11610
11752
|
let newCap = capacity === 0 ? INITIAL_CAPACITY3 : capacity;
|
|
11611
11753
|
while (newCap < needed) newCap *= 2;
|
|
@@ -11687,7 +11829,7 @@ function createVectorStore() {
|
|
|
11687
11829
|
recycledOrds.delete(docId);
|
|
11688
11830
|
docToOrd.set(docId, recycled);
|
|
11689
11831
|
ordToDoc[recycled] = docId;
|
|
11690
|
-
|
|
11832
|
+
ensureCapacity3(recycled + 1);
|
|
11691
11833
|
writeVector(recycled, vector);
|
|
11692
11834
|
recordPartition(recycled, partitionId, false);
|
|
11693
11835
|
liveCount++;
|
|
@@ -11696,7 +11838,7 @@ function createVectorStore() {
|
|
|
11696
11838
|
const ord = ordToDoc.length;
|
|
11697
11839
|
docToOrd.set(docId, ord);
|
|
11698
11840
|
ordToDoc.push(docId);
|
|
11699
|
-
|
|
11841
|
+
ensureCapacity3(ord + 1);
|
|
11700
11842
|
writeVector(ord, vector);
|
|
11701
11843
|
recordPartition(ord, partitionId, false);
|
|
11702
11844
|
liveCount++;
|
|
@@ -11862,7 +12004,7 @@ function createVectorStore() {
|
|
|
11862
12004
|
}
|
|
11863
12005
|
if (snapshot.dimension === 0 || snapshot.slots === 0) return;
|
|
11864
12006
|
initStorage(snapshot.dimension);
|
|
11865
|
-
|
|
12007
|
+
ensureCapacity3(snapshot.slots);
|
|
11866
12008
|
arena.set(snapshot.vectors.subarray(0, snapshot.slots * snapshot.dimension), scratchFloatLength);
|
|
11867
12009
|
mags.set(snapshot.magnitudes.subarray(0, Math.min(snapshot.slots, mags.length)));
|
|
11868
12010
|
ensurePartitionSlots(snapshot.slots);
|
|
@@ -13015,20 +13157,43 @@ function resolveWorkerCount(requested) {
|
|
|
13015
13157
|
}
|
|
13016
13158
|
function createWorkerPool(config) {
|
|
13017
13159
|
const workerCount = resolveWorkerCount(config.count);
|
|
13018
|
-
const workers =
|
|
13160
|
+
const workers = /* @__PURE__ */ new Map();
|
|
13161
|
+
const deadSlots = /* @__PURE__ */ new Set();
|
|
13019
13162
|
const indexAssignment = /* @__PURE__ */ new Map();
|
|
13020
13163
|
let isShutdown = false;
|
|
13164
|
+
function handleWorkerDeath(slotIndex, error) {
|
|
13165
|
+
if (isShutdown || deadSlots.has(slotIndex)) {
|
|
13166
|
+
return;
|
|
13167
|
+
}
|
|
13168
|
+
deadSlots.add(slotIndex);
|
|
13169
|
+
const slot = workers.get(slotIndex);
|
|
13170
|
+
const indexNames = slot ? [...slot.indexes].sort() : [];
|
|
13171
|
+
workers.delete(slotIndex);
|
|
13172
|
+
config.onWorkerCrash?.(slotIndex, indexNames, error);
|
|
13173
|
+
}
|
|
13021
13174
|
function ensureWorker(slotIndex) {
|
|
13022
|
-
if (
|
|
13023
|
-
|
|
13024
|
-
|
|
13175
|
+
if (deadSlots.has(slotIndex)) {
|
|
13176
|
+
return void 0;
|
|
13177
|
+
}
|
|
13178
|
+
let slot = workers.get(slotIndex);
|
|
13179
|
+
if (!slot) {
|
|
13180
|
+
slot = {
|
|
13181
|
+
executor: config.workerFactory(slotIndex, (error) => handleWorkerDeath(slotIndex, error)),
|
|
13025
13182
|
indexes: /* @__PURE__ */ new Set()
|
|
13026
13183
|
};
|
|
13184
|
+
workers.set(slotIndex, slot);
|
|
13027
13185
|
}
|
|
13028
|
-
return
|
|
13186
|
+
return slot;
|
|
13029
13187
|
}
|
|
13030
13188
|
function assignSlot(indexName) {
|
|
13031
|
-
|
|
13189
|
+
const preferred = fnv1a(indexName) % workerCount;
|
|
13190
|
+
for (let probe = 0; probe < workerCount; probe++) {
|
|
13191
|
+
const candidate = (preferred + probe) % workerCount;
|
|
13192
|
+
if (!deadSlots.has(candidate)) {
|
|
13193
|
+
return candidate;
|
|
13194
|
+
}
|
|
13195
|
+
}
|
|
13196
|
+
throw new NarsilError(ErrorCodes.WORKER_CRASHED, `Every worker in the pool has crashed`);
|
|
13032
13197
|
}
|
|
13033
13198
|
function addIndex(indexName) {
|
|
13034
13199
|
if (isShutdown) {
|
|
@@ -13039,18 +13204,14 @@ function createWorkerPool(config) {
|
|
|
13039
13204
|
}
|
|
13040
13205
|
const slotIndex = assignSlot(indexName);
|
|
13041
13206
|
const slot = ensureWorker(slotIndex);
|
|
13207
|
+
if (!slot) {
|
|
13208
|
+
throw new NarsilError(ErrorCodes.WORKER_CRASHED, `Worker ${slotIndex} for index "${indexName}" has crashed`);
|
|
13209
|
+
}
|
|
13042
13210
|
slot.indexes.add(indexName);
|
|
13043
13211
|
indexAssignment.set(indexName, slotIndex);
|
|
13044
13212
|
}
|
|
13045
13213
|
function removeIndex(indexName) {
|
|
13046
|
-
const
|
|
13047
|
-
if (slotIndex === void 0) {
|
|
13048
|
-
return;
|
|
13049
|
-
}
|
|
13050
|
-
const slot = workers[slotIndex];
|
|
13051
|
-
if (slot) {
|
|
13052
|
-
slot.indexes.delete(indexName);
|
|
13053
|
-
}
|
|
13214
|
+
for (const slot of workers.values()) slot.indexes.delete(indexName);
|
|
13054
13215
|
indexAssignment.delete(indexName);
|
|
13055
13216
|
}
|
|
13056
13217
|
function getExecutor(indexName) {
|
|
@@ -13061,15 +13222,22 @@ function createWorkerPool(config) {
|
|
|
13061
13222
|
if (slotIndex === void 0) {
|
|
13062
13223
|
throw new NarsilError(ErrorCodes.INDEX_NOT_FOUND, `Index "${indexName}" is not registered in the worker pool`);
|
|
13063
13224
|
}
|
|
13064
|
-
|
|
13225
|
+
const slot = workers.get(slotIndex);
|
|
13226
|
+
if (slot) {
|
|
13227
|
+
return slot.executor;
|
|
13228
|
+
}
|
|
13229
|
+
for (const survivor of workers.values()) {
|
|
13230
|
+
if (survivor.indexes.has(indexName)) {
|
|
13231
|
+
return survivor.executor;
|
|
13232
|
+
}
|
|
13233
|
+
}
|
|
13234
|
+
throw new NarsilError(ErrorCodes.WORKER_CRASHED, `Every worker holding index "${indexName}" has crashed`);
|
|
13065
13235
|
}
|
|
13066
13236
|
async function getMemoryStats() {
|
|
13067
13237
|
const indices = [];
|
|
13068
13238
|
const pending = [];
|
|
13069
|
-
for (
|
|
13070
|
-
|
|
13071
|
-
if (!slot) continue;
|
|
13072
|
-
indices.push(i);
|
|
13239
|
+
for (const [slotIndex, slot] of workers) {
|
|
13240
|
+
indices.push(slotIndex);
|
|
13073
13241
|
pending.push(
|
|
13074
13242
|
slot.executor.execute({ type: "memoryReport", requestId: createRequestId() }).catch(() => null)
|
|
13075
13243
|
);
|
|
@@ -13092,7 +13260,7 @@ function createWorkerPool(config) {
|
|
|
13092
13260
|
return;
|
|
13093
13261
|
}
|
|
13094
13262
|
isShutdown = true;
|
|
13095
|
-
const shutdownPromises = workers.
|
|
13263
|
+
const shutdownPromises = [...workers.values()].map((slot) => {
|
|
13096
13264
|
const timeoutPromise = new Promise((resolve) => {
|
|
13097
13265
|
setTimeout(resolve, 5e3);
|
|
13098
13266
|
});
|
|
@@ -13102,17 +13270,16 @@ function createWorkerPool(config) {
|
|
|
13102
13270
|
indexAssignment.clear();
|
|
13103
13271
|
}
|
|
13104
13272
|
function getAllExecutors() {
|
|
13105
|
-
return workers.
|
|
13273
|
+
return [...workers.values()].map((slot) => slot.executor);
|
|
13106
13274
|
}
|
|
13107
13275
|
function addIndexToAll(indexName) {
|
|
13108
13276
|
if (isShutdown) {
|
|
13109
13277
|
throw new NarsilError(ErrorCodes.WORKER_CRASHED, "Worker pool has been shut down");
|
|
13110
13278
|
}
|
|
13111
13279
|
for (let i = 0; i < workerCount; i++) {
|
|
13112
|
-
|
|
13113
|
-
slot.indexes.add(indexName);
|
|
13280
|
+
ensureWorker(i)?.indexes.add(indexName);
|
|
13114
13281
|
}
|
|
13115
|
-
indexAssignment.set(indexName,
|
|
13282
|
+
indexAssignment.set(indexName, assignSlot(indexName));
|
|
13116
13283
|
}
|
|
13117
13284
|
return {
|
|
13118
13285
|
getExecutor,
|
|
@@ -14242,6 +14409,9 @@ function createDirectExecutor() {
|
|
|
14242
14409
|
}
|
|
14243
14410
|
function dropIndex(indexName) {
|
|
14244
14411
|
const entry = requireIndex(indexName);
|
|
14412
|
+
for (const vectorIndex of entry.vectorIndexes.values()) {
|
|
14413
|
+
vectorIndex.dispose();
|
|
14414
|
+
}
|
|
14245
14415
|
for (const partition of entry.manager.getAllPartitions()) {
|
|
14246
14416
|
partition.clear();
|
|
14247
14417
|
}
|
|
@@ -14365,6 +14535,12 @@ function createDirectExecutor() {
|
|
|
14365
14535
|
}
|
|
14366
14536
|
case "preflight": {
|
|
14367
14537
|
const entry = requireIndex(action.indexName);
|
|
14538
|
+
if (countsWithoutScores(action.params)) {
|
|
14539
|
+
const count = fanOutMatchCount(entry.manager, action.params, entry.language, entry.config.schema, {
|
|
14540
|
+
searchOptions: entry.searchOptions
|
|
14541
|
+
});
|
|
14542
|
+
return { count };
|
|
14543
|
+
}
|
|
14368
14544
|
const result = await fanOutQuery(
|
|
14369
14545
|
entry.manager,
|
|
14370
14546
|
action.params,
|
|
@@ -14530,7 +14706,7 @@ function createAnalysisRebuildCoordinator(config, deps) {
|
|
|
14530
14706
|
async function rebuildNow(indexName, entry) {
|
|
14531
14707
|
const manager = deps.getManager(indexName);
|
|
14532
14708
|
if (manager === void 0) {
|
|
14533
|
-
|
|
14709
|
+
entry.run = null;
|
|
14534
14710
|
return;
|
|
14535
14711
|
}
|
|
14536
14712
|
const partitionCount = manager.partitionCount;
|
|
@@ -14582,6 +14758,23 @@ function createAnalysisRebuildCoordinator(config, deps) {
|
|
|
14582
14758
|
const documentCount = deps.getManager(indexName)?.countDocuments() ?? 0;
|
|
14583
14759
|
await config?.onStaleAnalysis?.({ ...entry.index, documentCount }, () => enqueue2(indexName, entry));
|
|
14584
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
|
+
}
|
|
14585
14778
|
return {
|
|
14586
14779
|
markStale(index) {
|
|
14587
14780
|
const existing = stale.get(index.indexName);
|
|
@@ -14596,23 +14789,16 @@ function createAnalysisRebuildCoordinator(config, deps) {
|
|
|
14596
14789
|
isStale(indexName) {
|
|
14597
14790
|
return stale.has(indexName);
|
|
14598
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
|
+
},
|
|
14599
14799
|
async reviewStaleIndexes() {
|
|
14600
14800
|
for (const [indexName, entry] of [...stale]) {
|
|
14601
|
-
|
|
14602
|
-
try {
|
|
14603
|
-
await announce(indexName, entry);
|
|
14604
|
-
} catch (err) {
|
|
14605
|
-
deps.emit({
|
|
14606
|
-
indexName,
|
|
14607
|
-
status: "failed",
|
|
14608
|
-
partitionsRebuilt: 0,
|
|
14609
|
-
partitionCount: deps.getManager(indexName)?.partitionCount ?? 0,
|
|
14610
|
-
error: toError(err)
|
|
14611
|
-
});
|
|
14612
|
-
}
|
|
14613
|
-
if (rebuildMode === "auto") {
|
|
14614
|
-
void enqueue2(indexName, entry).catch(() => void 0);
|
|
14615
|
-
}
|
|
14801
|
+
await review(indexName, entry);
|
|
14616
14802
|
}
|
|
14617
14803
|
},
|
|
14618
14804
|
rebuild(indexName) {
|
|
@@ -15178,6 +15364,9 @@ function writeHeader(header) {
|
|
|
15178
15364
|
}
|
|
15179
15365
|
return buffer;
|
|
15180
15366
|
}
|
|
15367
|
+
function hasNrslMagic(data) {
|
|
15368
|
+
return data.length >= MAGIC_BYTES.length && data[0] === MAGIC_BYTES[0] && data[1] === MAGIC_BYTES[1] && data[2] === MAGIC_BYTES[2] && data[3] === MAGIC_BYTES[3];
|
|
15369
|
+
}
|
|
15181
15370
|
function readHeader(data) {
|
|
15182
15371
|
if (data.length < HEADER_SIZE) {
|
|
15183
15372
|
throw new NarsilError(
|
|
@@ -15213,6 +15402,7 @@ function readHeader(data) {
|
|
|
15213
15402
|
// src/serialization/envelope.ts
|
|
15214
15403
|
var CURRENT_ENVELOPE_VERSION = 1;
|
|
15215
15404
|
var SNAPSHOT_ENVELOPE_VERSION = 2;
|
|
15405
|
+
var INDEX_SNAPSHOT_ENVELOPE_VERSION = 3;
|
|
15216
15406
|
function parseEngineVersion(version) {
|
|
15217
15407
|
const parts = version.split(".");
|
|
15218
15408
|
return [parseInt(parts[0] ?? "0", 10), parseInt(parts[1] ?? "0", 10), parseInt(parts[2] ?? "0", 10)];
|
|
@@ -15357,6 +15547,19 @@ function concatEnvelopeParts(parts) {
|
|
|
15357
15547
|
async function unpackEnvelopeBytes(data) {
|
|
15358
15548
|
return unpackEnvelope(data, SNAPSHOT_ENVELOPE_VERSION);
|
|
15359
15549
|
}
|
|
15550
|
+
async function packIndexSnapshotEnvelope(payloadBytes) {
|
|
15551
|
+
return packEnvelope(payloadBytes, { checksum: true, envelopeFormatVersion: INDEX_SNAPSHOT_ENVELOPE_VERSION });
|
|
15552
|
+
}
|
|
15553
|
+
async function unpackIndexSnapshotEnvelope(data) {
|
|
15554
|
+
const { header, payloadBytes } = await unpackEnvelope(data, INDEX_SNAPSHOT_ENVELOPE_VERSION);
|
|
15555
|
+
if (header.envelopeFormatVersion !== INDEX_SNAPSHOT_ENVELOPE_VERSION) {
|
|
15556
|
+
throw new NarsilError(
|
|
15557
|
+
ErrorCodes.ENVELOPE_VERSION_MISMATCH,
|
|
15558
|
+
`Expected an index snapshot envelope of format v${INDEX_SNAPSHOT_ENVELOPE_VERSION}, received format v${header.envelopeFormatVersion}`
|
|
15559
|
+
);
|
|
15560
|
+
}
|
|
15561
|
+
return payloadBytes;
|
|
15562
|
+
}
|
|
15360
15563
|
async function writeMetadataEnvelope(meta, options = {}) {
|
|
15361
15564
|
const payloadBytes = serializeMetadata(meta);
|
|
15362
15565
|
return packEnvelope(payloadBytes, options);
|
|
@@ -16044,6 +16247,9 @@ function mergeTimeOrderedSegments(ordered, fallback) {
|
|
|
16044
16247
|
validateMergedPartition(merged);
|
|
16045
16248
|
return merged;
|
|
16046
16249
|
}
|
|
16250
|
+
function countLiveDocuments(ordered) {
|
|
16251
|
+
return resolveWinners(ordered).size;
|
|
16252
|
+
}
|
|
16047
16253
|
function resolveWinners(ordered) {
|
|
16048
16254
|
const winner = /* @__PURE__ */ new Map();
|
|
16049
16255
|
for (let segIndex = 0; segIndex < ordered.length; segIndex += 1) {
|
|
@@ -17297,6 +17503,7 @@ async function writeSegmentedCheckpoint(input) {
|
|
|
17297
17503
|
});
|
|
17298
17504
|
}
|
|
17299
17505
|
const partitions = [];
|
|
17506
|
+
let wholeDocumentCount = input.wholePartitionPayload === void 0 ? null : 0;
|
|
17300
17507
|
for (const target of input.targets) {
|
|
17301
17508
|
const priorPartition = priorManifest?.partitions.find((p) => p.partitionId === target.partitionId);
|
|
17302
17509
|
const priorSeqNo = snapshotCheckpointFor(priorManifest?.checkpoint ?? [], target.partitionId);
|
|
@@ -17307,15 +17514,13 @@ async function writeSegmentedCheckpoint(input) {
|
|
|
17307
17514
|
priorSeqNo,
|
|
17308
17515
|
target.lastSeqNo
|
|
17309
17516
|
);
|
|
17310
|
-
|
|
17311
|
-
|
|
17312
|
-
|
|
17313
|
-
|
|
17314
|
-
|
|
17315
|
-
|
|
17316
|
-
|
|
17317
|
-
)
|
|
17318
|
-
);
|
|
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
|
+
}
|
|
17319
17524
|
}
|
|
17320
17525
|
carryForwardUncheckpointedPartitions(priorManifest, input.targets, partitions, checkpointByPartition);
|
|
17321
17526
|
const manifest = {
|
|
@@ -17327,6 +17532,7 @@ async function writeSegmentedCheckpoint(input) {
|
|
|
17327
17532
|
const parts = await encodeSegmentManifest(manifest);
|
|
17328
17533
|
await directory.atomicWrite(manifestKey(indexName), [parts.header, parts.payload]);
|
|
17329
17534
|
await collectGarbage(directory, indexName, priorManifest, manifest);
|
|
17535
|
+
return wholeDocumentCount;
|
|
17330
17536
|
}
|
|
17331
17537
|
async function writePartition(context, partitionId, priorPartition, entries) {
|
|
17332
17538
|
let segments = priorPartition ? [...priorPartition.segments] : [];
|
|
@@ -17446,9 +17652,9 @@ async function writeIndexCheckpoint(input) {
|
|
|
17446
17652
|
const { directory, metadata, targets, compactionThreshold, manager } = input;
|
|
17447
17653
|
const offloaded = !input.fromMemory && input.canOffload && metadata.tokenizer === void 0 && metadata.stopWords === void 0 && await runCheckpointOnWorker({ root: directory.root, metadata, targets, compactionThreshold });
|
|
17448
17654
|
if (offloaded) {
|
|
17449
|
-
return;
|
|
17655
|
+
return null;
|
|
17450
17656
|
}
|
|
17451
|
-
|
|
17657
|
+
return writeSegmentedCheckpoint({
|
|
17452
17658
|
directory,
|
|
17453
17659
|
metadata,
|
|
17454
17660
|
targets,
|
|
@@ -17480,9 +17686,171 @@ function createSeqOwner(startSeqNo, primaryTerm = SINGLE_NODE_PRIMARY_TERM) {
|
|
|
17480
17686
|
};
|
|
17481
17687
|
}
|
|
17482
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
|
+
|
|
17483
17850
|
// src/persistence/durability/types.ts
|
|
17484
17851
|
var DEFAULT_CHECKPOINT_INTERVAL_MS = 3e5;
|
|
17485
17852
|
var DEFAULT_CHECKPOINT_MUTATION_THRESHOLD = 1e5;
|
|
17853
|
+
var DEFAULT_ASYNC_FLUSH_INTERVAL_MS = 1e3;
|
|
17486
17854
|
|
|
17487
17855
|
// src/persistence/durability/group-commit.ts
|
|
17488
17856
|
function createGroupCommitCoordinator(sync) {
|
|
@@ -17728,7 +18096,6 @@ function createWalWriter(directory, config) {
|
|
|
17728
18096
|
}
|
|
17729
18097
|
|
|
17730
18098
|
// src/persistence/durability/manager.ts
|
|
17731
|
-
var DEFAULT_ASYNC_FLUSH_INTERVAL_MS = 1e3;
|
|
17732
18099
|
function createDurabilityManager(config, hooks, directoryOverride) {
|
|
17733
18100
|
const directory = createDurableDirectory(config.directory ?? "");
|
|
17734
18101
|
const canOffloadCheckpoint = directoryOverride === void 0;
|
|
@@ -17759,20 +18126,20 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
17759
18126
|
}
|
|
17760
18127
|
hooks.onFatalError(error);
|
|
17761
18128
|
}
|
|
17762
|
-
function
|
|
18129
|
+
function toError4(err) {
|
|
17763
18130
|
return err instanceof Error ? err : new Error(String(err));
|
|
17764
18131
|
}
|
|
17765
18132
|
async function closeWriterReportingFailure(closePromise) {
|
|
17766
18133
|
try {
|
|
17767
18134
|
await closePromise;
|
|
17768
18135
|
} catch (err) {
|
|
17769
|
-
markFatal(
|
|
18136
|
+
markFatal(toError4(err));
|
|
17770
18137
|
}
|
|
17771
18138
|
}
|
|
17772
18139
|
function getOrCreateIndexState(indexName) {
|
|
17773
18140
|
let state = indexes.get(indexName);
|
|
17774
18141
|
if (state === void 0) {
|
|
17775
|
-
state = { partitions: /* @__PURE__ */ new Map(), mutationsSinceCheckpoint: 0, checkpointInFlight: null };
|
|
18142
|
+
state = { partitions: /* @__PURE__ */ new Map(), mutationsSinceCheckpoint: 0, checkpointInFlight: null, unloading: false };
|
|
17776
18143
|
indexes.set(indexName, state);
|
|
17777
18144
|
}
|
|
17778
18145
|
return state;
|
|
@@ -17835,7 +18202,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
17835
18202
|
try {
|
|
17836
18203
|
await partition.walWriter.commit();
|
|
17837
18204
|
} catch (err) {
|
|
17838
|
-
partition.failed =
|
|
18205
|
+
partition.failed = toError4(err);
|
|
17839
18206
|
markFatal(partition.failed);
|
|
17840
18207
|
}
|
|
17841
18208
|
}
|
|
@@ -17849,7 +18216,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
17849
18216
|
try {
|
|
17850
18217
|
await checkpointIndex(indexName);
|
|
17851
18218
|
} catch (err) {
|
|
17852
|
-
markFatal(
|
|
18219
|
+
markFatal(toError4(err));
|
|
17853
18220
|
}
|
|
17854
18221
|
}
|
|
17855
18222
|
}
|
|
@@ -17866,7 +18233,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
17866
18233
|
}
|
|
17867
18234
|
async function checkpointIndex(indexName, fromMemory = false) {
|
|
17868
18235
|
const indexState = indexes.get(indexName);
|
|
17869
|
-
if (indexState === void 0) {
|
|
18236
|
+
if (indexState === void 0 || indexState.unloading) {
|
|
17870
18237
|
return;
|
|
17871
18238
|
}
|
|
17872
18239
|
if (indexState.checkpointInFlight !== null) {
|
|
@@ -17882,82 +18249,46 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
17882
18249
|
return run;
|
|
17883
18250
|
}
|
|
17884
18251
|
async function performCheckpoint(indexName, indexState, fromMemory = false) {
|
|
17885
|
-
|
|
17886
|
-
if (manager === void 0) {
|
|
17887
|
-
return;
|
|
17888
|
-
}
|
|
17889
|
-
const metadata = hooks.buildMetadata(indexName);
|
|
17890
|
-
if (metadata === void 0) {
|
|
17891
|
-
return;
|
|
17892
|
-
}
|
|
17893
|
-
const targets = [];
|
|
17894
|
-
for (let i = 0; i < manager.partitionCount; i += 1) {
|
|
17895
|
-
const partition = indexState.partitions.get(i);
|
|
17896
|
-
targets.push({
|
|
17897
|
-
partitionId: i,
|
|
17898
|
-
lastSeqNo: partition?.appliedSeqNo ?? 0,
|
|
17899
|
-
primaryTerm: partition?.seqOwner.primaryTerm ?? SINGLE_NODE_PRIMARY_TERM
|
|
17900
|
-
});
|
|
17901
|
-
if (partition !== void 0) {
|
|
17902
|
-
try {
|
|
17903
|
-
await partition.walWriter.commit();
|
|
17904
|
-
} catch (err) {
|
|
17905
|
-
partition.failed = toError3(err);
|
|
17906
|
-
markFatal(partition.failed);
|
|
17907
|
-
throw partition.failed;
|
|
17908
|
-
}
|
|
17909
|
-
}
|
|
17910
|
-
}
|
|
17911
|
-
await writeIndexCheckpoint({
|
|
18252
|
+
await runDurableCheckpoint({
|
|
17912
18253
|
directory,
|
|
17913
|
-
|
|
17914
|
-
|
|
18254
|
+
hooks,
|
|
18255
|
+
indexName,
|
|
18256
|
+
indexState,
|
|
17915
18257
|
compactionThreshold,
|
|
17916
|
-
manager,
|
|
17917
18258
|
canOffload: canOffloadCheckpoint,
|
|
17918
|
-
fromMemory
|
|
18259
|
+
fromMemory,
|
|
18260
|
+
queueMetadataWrite,
|
|
18261
|
+
markFatal
|
|
17919
18262
|
});
|
|
17920
|
-
await truncateCoveredSegments(directory, indexName, targets);
|
|
17921
|
-
await reclaimWalBeyondCount(directory, indexName, manager.partitionCount, indexState.partitions, markFatal);
|
|
17922
|
-
indexState.mutationsSinceCheckpoint = 0;
|
|
17923
18263
|
}
|
|
17924
|
-
|
|
17925
|
-
|
|
17926
|
-
|
|
17927
|
-
|
|
17928
|
-
|
|
17929
|
-
|
|
17930
|
-
|
|
17931
|
-
|
|
17932
|
-
|
|
17933
|
-
|
|
17934
|
-
|
|
17935
|
-
|
|
17936
|
-
|
|
17937
|
-
vectorIndexes: hooks.getVectorIndexes(indexName)
|
|
17938
|
-
};
|
|
17939
|
-
const checkpoint = await loadSnapshot(directory, indexName, deps);
|
|
17940
|
-
const manifest = await readSegmentManifest(directory, indexName);
|
|
17941
|
-
if (manifest !== null) {
|
|
17942
|
-
await reclaimOrphanedSegments(directory, indexName, manifest);
|
|
17943
|
-
}
|
|
17944
|
-
for (let partitionId = 0; partitionId < manager.partitionCount; partitionId += 1) {
|
|
17945
|
-
const fromSeqNo = snapshotCheckpointFor(checkpoint, partitionId);
|
|
17946
|
-
const { highestSeqNo } = await replayWal(directory, indexName, partitionId, fromSeqNo, deps);
|
|
17947
|
-
getOrCreatePartition(indexName, partitionId, highestSeqNo);
|
|
17948
|
-
}
|
|
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
|
+
);
|
|
17949
18277
|
}
|
|
17950
18278
|
return {
|
|
17951
18279
|
isActive() {
|
|
17952
18280
|
return true;
|
|
17953
18281
|
},
|
|
17954
|
-
async recover() {
|
|
18282
|
+
async recover(metadataOnly = false) {
|
|
17955
18283
|
const names = await listPersistedIndexes(directory);
|
|
17956
18284
|
for (const indexName of names) {
|
|
17957
|
-
await recoverIndex(indexName);
|
|
18285
|
+
await recoverIndex(indexName, metadataOnly);
|
|
17958
18286
|
}
|
|
17959
18287
|
startCheckpointTimer();
|
|
17960
18288
|
},
|
|
18289
|
+
recoverIndex(indexName) {
|
|
18290
|
+
return recoverIndex(indexName);
|
|
18291
|
+
},
|
|
17961
18292
|
highestPersistedSeqNo(indexName, partitionId) {
|
|
17962
18293
|
return indexes.get(indexName)?.partitions.get(partitionId)?.appliedSeqNo ?? 0;
|
|
17963
18294
|
},
|
|
@@ -17981,7 +18312,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
17981
18312
|
const entry = buildMutationEntry(record, allocatedSeqNo);
|
|
17982
18313
|
await partition.walWriter.append(entry);
|
|
17983
18314
|
} catch (err) {
|
|
17984
|
-
partition.failed =
|
|
18315
|
+
partition.failed = toError4(err);
|
|
17985
18316
|
markFatal(partition.failed);
|
|
17986
18317
|
throw partition.failed;
|
|
17987
18318
|
}
|
|
@@ -17993,7 +18324,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
17993
18324
|
try {
|
|
17994
18325
|
await partition.walWriter.commit();
|
|
17995
18326
|
} catch (err) {
|
|
17996
|
-
partition.failed =
|
|
18327
|
+
partition.failed = toError4(err);
|
|
17997
18328
|
markFatal(partition.failed);
|
|
17998
18329
|
throw partition.failed;
|
|
17999
18330
|
}
|
|
@@ -18003,7 +18334,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
18003
18334
|
startAsyncFlushTimer();
|
|
18004
18335
|
if (indexState.mutationsSinceCheckpoint >= checkpointMutationThreshold) {
|
|
18005
18336
|
void checkpointIndex(record.indexName).catch((err) => {
|
|
18006
|
-
markFatal(
|
|
18337
|
+
markFatal(toError4(err));
|
|
18007
18338
|
});
|
|
18008
18339
|
}
|
|
18009
18340
|
return allocatedSeqNo;
|
|
@@ -18033,15 +18364,23 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
18033
18364
|
async removeIndex(indexName) {
|
|
18034
18365
|
const indexState = indexes.get(indexName);
|
|
18035
18366
|
if (indexState !== void 0) {
|
|
18036
|
-
|
|
18037
|
-
await closeWriterReportingFailure(partition.walWriter.close());
|
|
18038
|
-
}
|
|
18367
|
+
await drainIndexStateForUnload(indexState, metadataWrites.get(indexName), closeWriterReportingFailure);
|
|
18039
18368
|
indexes.delete(indexName);
|
|
18369
|
+
metadataWrites.delete(indexName);
|
|
18040
18370
|
}
|
|
18041
18371
|
for (const key of await directory.list(`${indexName}/`)) {
|
|
18042
18372
|
await directory.remove(key);
|
|
18043
18373
|
}
|
|
18044
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
|
+
},
|
|
18045
18384
|
async shutdown() {
|
|
18046
18385
|
shuttingDown = true;
|
|
18047
18386
|
if (checkpointTimer !== null) {
|
|
@@ -18080,7 +18419,13 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18080
18419
|
function getOrCreateIndexState(indexName) {
|
|
18081
18420
|
let state = indexes.get(indexName);
|
|
18082
18421
|
if (state === void 0) {
|
|
18083
|
-
state = {
|
|
18422
|
+
state = {
|
|
18423
|
+
mutationsSinceCheckpoint: 0,
|
|
18424
|
+
checkpointInFlight: null,
|
|
18425
|
+
applyChain: Promise.resolve(),
|
|
18426
|
+
unloading: false,
|
|
18427
|
+
appliedSeqNoByPartition: /* @__PURE__ */ new Map()
|
|
18428
|
+
};
|
|
18084
18429
|
indexes.set(indexName, state);
|
|
18085
18430
|
}
|
|
18086
18431
|
return state;
|
|
@@ -18111,10 +18456,11 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18111
18456
|
if (manager === void 0 || metadata === void 0) {
|
|
18112
18457
|
return;
|
|
18113
18458
|
}
|
|
18459
|
+
const documentCount = manager.countDocuments();
|
|
18114
18460
|
const seqNoByPartition = /* @__PURE__ */ new Map();
|
|
18115
18461
|
const primaryTermByPartition = /* @__PURE__ */ new Map();
|
|
18116
18462
|
for (let i = 0; i < manager.partitionCount; i += 1) {
|
|
18117
|
-
seqNoByPartition.set(i, 0);
|
|
18463
|
+
seqNoByPartition.set(i, indexState.appliedSeqNoByPartition.get(i) ?? 0);
|
|
18118
18464
|
primaryTermByPartition.set(i, SINGLE_NODE_PRIMARY_TERM);
|
|
18119
18465
|
}
|
|
18120
18466
|
const { parts } = await buildSnapshotBundleBytes({
|
|
@@ -18130,6 +18476,13 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18130
18476
|
primaryTermByPartition
|
|
18131
18477
|
});
|
|
18132
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
|
+
});
|
|
18133
18486
|
indexState.mutationsSinceCheckpoint = 0;
|
|
18134
18487
|
if (publisher !== void 0) {
|
|
18135
18488
|
const partitionIds = [];
|
|
@@ -18139,15 +18492,18 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18139
18492
|
await publisher.publishPartitions(indexName, partitionIds);
|
|
18140
18493
|
}
|
|
18141
18494
|
}
|
|
18142
|
-
async function checkpointIndex(indexName) {
|
|
18495
|
+
async function checkpointIndex(indexName, forceFresh = false) {
|
|
18143
18496
|
const indexState = indexes.get(indexName);
|
|
18144
|
-
if (indexState === void 0) {
|
|
18497
|
+
if (indexState === void 0 || indexState.unloading) {
|
|
18145
18498
|
return;
|
|
18146
18499
|
}
|
|
18147
18500
|
if (indexState.checkpointInFlight !== null) {
|
|
18148
|
-
|
|
18501
|
+
await indexState.checkpointInFlight;
|
|
18502
|
+
if (!forceFresh) {
|
|
18503
|
+
return;
|
|
18504
|
+
}
|
|
18149
18505
|
}
|
|
18150
|
-
const run = performCheckpoint(indexName, indexState).finally(() => {
|
|
18506
|
+
const run = indexState.applyChain.then(() => performCheckpoint(indexName, indexState)).finally(() => {
|
|
18151
18507
|
indexState.checkpointInFlight = null;
|
|
18152
18508
|
});
|
|
18153
18509
|
indexState.checkpointInFlight = run;
|
|
@@ -18162,13 +18518,35 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18162
18518
|
);
|
|
18163
18519
|
return queued;
|
|
18164
18520
|
}
|
|
18165
|
-
async function recoverIndex(indexName) {
|
|
18521
|
+
async function recoverIndex(indexName, metadataOnly = false) {
|
|
18166
18522
|
const metaBytes = await adapter.load(metadataKey(indexName));
|
|
18167
18523
|
if (metaBytes === null) {
|
|
18168
18524
|
return;
|
|
18169
18525
|
}
|
|
18170
18526
|
const { metadata } = await readMetadataEnvelope(metaBytes);
|
|
18171
|
-
|
|
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
|
+
}
|
|
18172
18550
|
const manager = hooks.getManager(indexName);
|
|
18173
18551
|
if (manager === void 0) {
|
|
18174
18552
|
return;
|
|
@@ -18177,33 +18555,58 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18177
18555
|
if (snapshotBytes === null) {
|
|
18178
18556
|
return;
|
|
18179
18557
|
}
|
|
18180
|
-
await loadSnapshotBundleBytes(snapshotBytes, {
|
|
18558
|
+
const checkpoint = await loadSnapshotBundleBytes(snapshotBytes, {
|
|
18181
18559
|
manager,
|
|
18182
18560
|
vectorFieldPaths: hooks.getVectorFieldPaths(indexName),
|
|
18183
18561
|
vectorIndexes: hooks.getVectorIndexes(indexName)
|
|
18184
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);
|
|
18185
18577
|
}
|
|
18186
18578
|
return {
|
|
18187
18579
|
isActive() {
|
|
18188
18580
|
return true;
|
|
18189
18581
|
},
|
|
18190
|
-
async recover() {
|
|
18582
|
+
async recover(metadataOnly = false) {
|
|
18191
18583
|
const keys = await adapter.list("");
|
|
18192
18584
|
for (const key of keys) {
|
|
18193
18585
|
if (key.endsWith("/meta")) {
|
|
18194
|
-
await recoverIndex(key.slice(0, -"/meta".length));
|
|
18586
|
+
await recoverIndex(key.slice(0, -"/meta".length), metadataOnly);
|
|
18195
18587
|
}
|
|
18196
18588
|
}
|
|
18197
18589
|
startCheckpointTimer();
|
|
18198
18590
|
},
|
|
18199
|
-
|
|
18200
|
-
return
|
|
18591
|
+
recoverIndex(indexName) {
|
|
18592
|
+
return recoverIndex(indexName);
|
|
18593
|
+
},
|
|
18594
|
+
highestPersistedSeqNo(indexName, partitionId) {
|
|
18595
|
+
return indexes.get(indexName)?.appliedSeqNoByPartition.get(partitionId) ?? 0;
|
|
18201
18596
|
},
|
|
18202
18597
|
async recordMutation(record) {
|
|
18203
18598
|
const indexState = getOrCreateIndexState(record.indexName);
|
|
18204
|
-
const buffered = indexState.applyChain.then(() =>
|
|
18205
|
-
|
|
18206
|
-
|
|
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;
|
|
18207
18610
|
indexState.mutationsSinceCheckpoint += 1;
|
|
18208
18611
|
startCheckpointTimer();
|
|
18209
18612
|
if (indexState.mutationsSinceCheckpoint >= checkpointMutationThreshold) {
|
|
@@ -18211,7 +18614,7 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18211
18614
|
hooks.onFatalError(err instanceof Error ? err : new Error(String(err)));
|
|
18212
18615
|
});
|
|
18213
18616
|
}
|
|
18214
|
-
return
|
|
18617
|
+
return appliedSeqNo;
|
|
18215
18618
|
},
|
|
18216
18619
|
persistMetadata(indexName) {
|
|
18217
18620
|
return queueMetadataWrite(indexName, async () => {
|
|
@@ -18227,17 +18630,49 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18227
18630
|
checkpoint(indexName) {
|
|
18228
18631
|
return checkpointIndex(indexName);
|
|
18229
18632
|
},
|
|
18633
|
+
checkpointFromMemory(indexName) {
|
|
18634
|
+
return checkpointIndex(indexName, true);
|
|
18635
|
+
},
|
|
18230
18636
|
async checkpointAll() {
|
|
18231
18637
|
for (const indexName of [...indexes.keys()]) {
|
|
18232
18638
|
await checkpointIndex(indexName);
|
|
18233
18639
|
}
|
|
18234
18640
|
},
|
|
18235
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
|
+
}
|
|
18236
18654
|
indexes.delete(indexName);
|
|
18655
|
+
metadataWrites.delete(indexName);
|
|
18237
18656
|
for (const key of await adapter.list(`${indexName}/`)) {
|
|
18238
18657
|
await adapter.delete(key);
|
|
18239
18658
|
}
|
|
18240
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
|
+
},
|
|
18241
18676
|
async reloadIndex(indexName) {
|
|
18242
18677
|
const indexState = getOrCreateIndexState(indexName);
|
|
18243
18678
|
const reload = indexState.applyChain.then(() => recoverIndex(indexName));
|
|
@@ -18261,21 +18696,22 @@ function createSnapshotOnlyManager(adapter, config, hooks, publisher) {
|
|
|
18261
18696
|
}
|
|
18262
18697
|
|
|
18263
18698
|
// src/engine/durability-integration.ts
|
|
18264
|
-
function buildMetadata(indexName, hooks) {
|
|
18699
|
+
function buildMetadata(indexName, hooks, documentCount) {
|
|
18265
18700
|
const config = hooks.getIndexConfig(indexName);
|
|
18266
18701
|
const manager = hooks.getManager(indexName);
|
|
18267
|
-
if (config === void 0
|
|
18702
|
+
if (config === void 0) {
|
|
18268
18703
|
return void 0;
|
|
18269
18704
|
}
|
|
18270
18705
|
const metadata = {
|
|
18271
18706
|
indexName,
|
|
18272
18707
|
schema: config.schema,
|
|
18273
18708
|
language: config.language,
|
|
18274
|
-
partitionCount: manager.partitionCount,
|
|
18709
|
+
partitionCount: manager?.partitionCount ?? config.partitionCount,
|
|
18275
18710
|
bm25Params: { k1: config.k1, b: config.b },
|
|
18276
18711
|
createdAt: Date.now(),
|
|
18277
18712
|
engineVersion: VERSION
|
|
18278
18713
|
};
|
|
18714
|
+
metadata.documentCount = documentCount ?? config.documentCount;
|
|
18279
18715
|
if (config.embedding) {
|
|
18280
18716
|
metadata.embedding = config.embedding;
|
|
18281
18717
|
}
|
|
@@ -18325,8 +18761,9 @@ function createDurabilityIntegration(tier, hooks) {
|
|
|
18325
18761
|
getManager: hooks.getManager,
|
|
18326
18762
|
getVectorFieldPaths: hooks.getVectorFieldPaths,
|
|
18327
18763
|
getVectorIndexes: hooks.getVectorIndexes,
|
|
18328
|
-
buildMetadata: (indexName) => buildMetadata(indexName, hooks),
|
|
18764
|
+
buildMetadata: (indexName, documentCount) => buildMetadata(indexName, hooks, documentCount),
|
|
18329
18765
|
createIndexFromMetadata: hooks.createIndexFromMetadata,
|
|
18766
|
+
recordCheckpoint: hooks.recordCheckpoint,
|
|
18330
18767
|
onFatalError: hooks.onFatalError
|
|
18331
18768
|
};
|
|
18332
18769
|
const manager = tier.kind === "wal" ? createDurabilityManager(tier.config, managerHooks) : createSnapshotOnlyManager(tier.adapter, tier.config, managerHooks, hooks.checkpointPublisher);
|
|
@@ -18365,9 +18802,9 @@ function createDurabilityFromTier(tier, wiring) {
|
|
|
18365
18802
|
}
|
|
18366
18803
|
return createDurabilityIntegration(tier, {
|
|
18367
18804
|
checkpointPublisher: { publishPartitions: wiring.publishCheckpointedPartitions },
|
|
18368
|
-
getManager: (indexName) => wiring.indexRegistry.has(indexName) ? wiring.
|
|
18805
|
+
getManager: (indexName) => wiring.indexRegistry.has(indexName) ? wiring.getManager(indexName) : void 0,
|
|
18369
18806
|
getVectorFieldPaths: (indexName) => wiring.indexRegistry.get(indexName)?.vectorFieldPaths ?? /* @__PURE__ */ new Set(),
|
|
18370
|
-
getVectorIndexes: (indexName) => wiring.indexRegistry.has(indexName) ? wiring.
|
|
18807
|
+
getVectorIndexes: (indexName) => (wiring.indexRegistry.has(indexName) ? wiring.getManager(indexName)?.getVectorIndexes() : void 0) ?? /* @__PURE__ */ new Map(),
|
|
18371
18808
|
getIndexConfig: (indexName) => {
|
|
18372
18809
|
const entry = wiring.indexRegistry.get(indexName);
|
|
18373
18810
|
if (entry === void 0) {
|
|
@@ -18380,6 +18817,8 @@ function createDurabilityFromTier(tier, wiring) {
|
|
|
18380
18817
|
return {
|
|
18381
18818
|
...entry.indexUuid !== null ? { indexUuid: entry.indexUuid } : {},
|
|
18382
18819
|
...entry.heldPartitions !== null ? { heldPartitions: entry.heldPartitions } : {},
|
|
18820
|
+
documentCount: entry.documentCount,
|
|
18821
|
+
partitionCount: entry.partitionCount,
|
|
18383
18822
|
schema: flattenSchema(entry.config.schema),
|
|
18384
18823
|
language: entry.language.name,
|
|
18385
18824
|
k1: entry.config.bm25?.k1 ?? 1.2,
|
|
@@ -18399,10 +18838,401 @@ function createDurabilityFromTier(tier, wiring) {
|
|
|
18399
18838
|
};
|
|
18400
18839
|
},
|
|
18401
18840
|
createIndexFromMetadata: wiring.createIndexFromMetadata,
|
|
18841
|
+
recordCheckpoint: wiring.recordCheckpoint,
|
|
18402
18842
|
onFatalError: wiring.emitFatalError
|
|
18403
18843
|
});
|
|
18404
18844
|
}
|
|
18405
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
|
+
|
|
18406
19236
|
// src/engine/invalidation.ts
|
|
18407
19237
|
var STATISTICS_BROADCAST_INTERVAL_MS = 5e3;
|
|
18408
19238
|
var FOREIGN_STATS_TTL_MS = 6e4;
|
|
@@ -18428,7 +19258,7 @@ function createInvalidationIntegration(deps) {
|
|
|
18428
19258
|
const reloadChains = /* @__PURE__ */ new Map();
|
|
18429
19259
|
let broadcastTimer = null;
|
|
18430
19260
|
let stopped = false;
|
|
18431
|
-
function
|
|
19261
|
+
function toError4(err) {
|
|
18432
19262
|
return err instanceof Error ? err : new Error(String(err));
|
|
18433
19263
|
}
|
|
18434
19264
|
function pruneStaleForeignStats(entries) {
|
|
@@ -18441,7 +19271,7 @@ function createInvalidationIntegration(deps) {
|
|
|
18441
19271
|
}
|
|
18442
19272
|
function scheduleReload(indexName) {
|
|
18443
19273
|
const chain = reloadChains.get(indexName) ?? Promise.resolve();
|
|
18444
|
-
const next = chain.then(() => reloadIndex(indexName)).catch((err) => onError(
|
|
19274
|
+
const next = chain.then(() => reloadIndex(indexName)).catch((err) => onError(toError4(err)));
|
|
18445
19275
|
reloadChains.set(indexName, next);
|
|
18446
19276
|
}
|
|
18447
19277
|
function storeForeignStats(indexName, sourceInstanceId, stats) {
|
|
@@ -18467,7 +19297,7 @@ function createInvalidationIntegration(deps) {
|
|
|
18467
19297
|
}
|
|
18468
19298
|
storeForeignStats(event.indexName, event.instanceId, event.stats);
|
|
18469
19299
|
} catch (err) {
|
|
18470
|
-
onError(
|
|
19300
|
+
onError(toError4(err));
|
|
18471
19301
|
}
|
|
18472
19302
|
}
|
|
18473
19303
|
async function publishOwnStatistics() {
|
|
@@ -18489,7 +19319,7 @@ function createInvalidationIntegration(deps) {
|
|
|
18489
19319
|
}
|
|
18490
19320
|
});
|
|
18491
19321
|
} catch (err) {
|
|
18492
|
-
onError(
|
|
19322
|
+
onError(toError4(err));
|
|
18493
19323
|
}
|
|
18494
19324
|
}
|
|
18495
19325
|
}
|
|
@@ -18508,7 +19338,7 @@ function createInvalidationIntegration(deps) {
|
|
|
18508
19338
|
sourceInstanceId: instanceId
|
|
18509
19339
|
});
|
|
18510
19340
|
} catch (err) {
|
|
18511
|
-
onError(
|
|
19341
|
+
onError(toError4(err));
|
|
18512
19342
|
}
|
|
18513
19343
|
},
|
|
18514
19344
|
broadcastStats(indexName) {
|
|
@@ -18553,7 +19383,7 @@ function createInvalidationIntegration(deps) {
|
|
|
18553
19383
|
try {
|
|
18554
19384
|
await adapter.shutdown();
|
|
18555
19385
|
} catch (err) {
|
|
18556
|
-
onError(
|
|
19386
|
+
onError(toError4(err));
|
|
18557
19387
|
}
|
|
18558
19388
|
}
|
|
18559
19389
|
};
|
|
@@ -18595,7 +19425,7 @@ async function transferIndexToPool(indexName, pool, config, manager) {
|
|
|
18595
19425
|
}
|
|
18596
19426
|
|
|
18597
19427
|
// src/engine/orchestration/eligibility.ts
|
|
18598
|
-
function
|
|
19428
|
+
function toError3(value) {
|
|
18599
19429
|
return value instanceof Error ? value : new Error(String(value));
|
|
18600
19430
|
}
|
|
18601
19431
|
function isDeterministicFailure(error) {
|
|
@@ -18933,14 +19763,34 @@ async function awaitCompactions(state) {
|
|
|
18933
19763
|
var DEFAULT_BACKPRESSURE_LIMIT = 100;
|
|
18934
19764
|
var DEFAULT_REQUEST_TIMEOUT = 3e4;
|
|
18935
19765
|
var SHUTDOWN_TIMEOUT = 5e3;
|
|
19766
|
+
var CONSECUTIVE_TIMEOUTS_BEFORE_DEATH = 3;
|
|
19767
|
+
function errorFromEventLike(event) {
|
|
19768
|
+
if (event instanceof Error) {
|
|
19769
|
+
return event;
|
|
19770
|
+
}
|
|
19771
|
+
if (typeof event === "object" && event !== null) {
|
|
19772
|
+
const detail = event;
|
|
19773
|
+
if (detail.error instanceof Error) {
|
|
19774
|
+
return detail.error;
|
|
19775
|
+
}
|
|
19776
|
+
if (typeof detail.message === "string" && detail.message.length > 0) {
|
|
19777
|
+
return new Error(detail.message);
|
|
19778
|
+
}
|
|
19779
|
+
}
|
|
19780
|
+
return new Error("The worker reported an error event carrying no detail");
|
|
19781
|
+
}
|
|
18936
19782
|
function createWorkerExecutor(worker, config) {
|
|
18937
|
-
const backpressureLimit = DEFAULT_BACKPRESSURE_LIMIT;
|
|
18938
|
-
const requestTimeout = DEFAULT_REQUEST_TIMEOUT;
|
|
19783
|
+
const backpressureLimit = config?.backpressureLimit ?? DEFAULT_BACKPRESSURE_LIMIT;
|
|
19784
|
+
const requestTimeout = config?.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT;
|
|
18939
19785
|
const pending = /* @__PURE__ */ new Map();
|
|
19786
|
+
let deathError = null;
|
|
19787
|
+
let shutdownRequested = false;
|
|
19788
|
+
let consecutiveTimeouts = 0;
|
|
18940
19789
|
function processResponse(msg) {
|
|
18941
19790
|
if (!isValidWorkerResponse(msg)) {
|
|
18942
19791
|
return;
|
|
18943
19792
|
}
|
|
19793
|
+
consecutiveTimeouts = 0;
|
|
18944
19794
|
const response = msg;
|
|
18945
19795
|
const entry = pending.get(response.requestId);
|
|
18946
19796
|
if (!entry) {
|
|
@@ -18954,15 +19804,36 @@ function createWorkerExecutor(worker, config) {
|
|
|
18954
19804
|
entry.resolve(response.data);
|
|
18955
19805
|
}
|
|
18956
19806
|
}
|
|
19807
|
+
function handleDeath(cause) {
|
|
19808
|
+
if (deathError !== null) {
|
|
19809
|
+
return;
|
|
19810
|
+
}
|
|
19811
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
19812
|
+
deathError = new NarsilError(ErrorCodes.WORKER_CRASHED, `Worker died: ${message}`);
|
|
19813
|
+
for (const [id, entry] of pending) {
|
|
19814
|
+
clearTimeout(entry.timeoutId);
|
|
19815
|
+
entry.reject(deathError);
|
|
19816
|
+
pending.delete(id);
|
|
19817
|
+
}
|
|
19818
|
+
if (!shutdownRequested) {
|
|
19819
|
+
config?.onDeath?.(cause instanceof Error ? cause : new Error(message));
|
|
19820
|
+
}
|
|
19821
|
+
}
|
|
18957
19822
|
if (typeof worker.on === "function") {
|
|
18958
19823
|
worker.on("message", (msg) => processResponse(msg));
|
|
19824
|
+
worker.on("error", (cause) => handleDeath(cause));
|
|
19825
|
+
worker.on("exit", (code) => handleDeath(new Error(`Worker exited with code ${String(code)}`)));
|
|
18959
19826
|
} else if (typeof worker.addEventListener === "function") {
|
|
18960
19827
|
worker.addEventListener("message", (event) => {
|
|
18961
19828
|
const msg = event.data;
|
|
18962
19829
|
processResponse(msg);
|
|
18963
19830
|
});
|
|
19831
|
+
worker.addEventListener("error", (event) => handleDeath(errorFromEventLike(event)));
|
|
18964
19832
|
}
|
|
18965
19833
|
function execute(action) {
|
|
19834
|
+
if (deathError !== null) {
|
|
19835
|
+
return Promise.reject(deathError);
|
|
19836
|
+
}
|
|
18966
19837
|
if (pending.size >= backpressureLimit) {
|
|
18967
19838
|
return Promise.reject(
|
|
18968
19839
|
new NarsilError(ErrorCodes.WORKER_BUSY, `Backpressure limit of ${backpressureLimit} pending requests reached`)
|
|
@@ -18974,6 +19845,10 @@ function createWorkerExecutor(worker, config) {
|
|
|
18974
19845
|
const timeoutId = setTimeout(() => {
|
|
18975
19846
|
pending.delete(requestId);
|
|
18976
19847
|
reject2(new NarsilError(ErrorCodes.WORKER_TIMEOUT, `Request ${requestId} timed out after ${requestTimeout}ms`));
|
|
19848
|
+
consecutiveTimeouts += 1;
|
|
19849
|
+
if (consecutiveTimeouts >= CONSECUTIVE_TIMEOUTS_BEFORE_DEATH) {
|
|
19850
|
+
handleDeath(new Error(`no answer to ${CONSECUTIVE_TIMEOUTS_BEFORE_DEATH} consecutive requests`));
|
|
19851
|
+
}
|
|
18977
19852
|
}, requestTimeout);
|
|
18978
19853
|
pending.set(requestId, {
|
|
18979
19854
|
resolve,
|
|
@@ -18984,6 +19859,10 @@ function createWorkerExecutor(worker, config) {
|
|
|
18984
19859
|
});
|
|
18985
19860
|
}
|
|
18986
19861
|
async function shutdown() {
|
|
19862
|
+
shutdownRequested = true;
|
|
19863
|
+
if (deathError !== null) {
|
|
19864
|
+
return;
|
|
19865
|
+
}
|
|
18987
19866
|
const requestId = createRequestId();
|
|
18988
19867
|
const shutdownAction = { type: "shutdown", requestId };
|
|
18989
19868
|
const shutdownPromise = new Promise((resolve, reject2) => {
|
|
@@ -19024,9 +19903,9 @@ async function createWorkerFactory(entryPoint) {
|
|
|
19024
19903
|
const runtime = detectRuntime();
|
|
19025
19904
|
const resolvedEntry = resolveEntryPoint();
|
|
19026
19905
|
if (runtime.supportsWebWorkers) {
|
|
19027
|
-
return function webFactory(_workerId) {
|
|
19906
|
+
return function webFactory(_workerId, onDeath) {
|
|
19028
19907
|
const instance = new Worker(resolvedEntry, { type: "module" });
|
|
19029
|
-
return createWorkerExecutor(instance);
|
|
19908
|
+
return createWorkerExecutor(instance, { onDeath });
|
|
19030
19909
|
};
|
|
19031
19910
|
}
|
|
19032
19911
|
throw new NarsilError(ErrorCodes.WORKER_CRASHED, `No worker support available on runtime "${runtime.runtime}"`);
|
|
@@ -19082,7 +19961,13 @@ async function runPromotion(state, reason) {
|
|
|
19082
19961
|
return;
|
|
19083
19962
|
}
|
|
19084
19963
|
const factory = await createWorkerFactory();
|
|
19085
|
-
const pool = createWorkerPool({
|
|
19964
|
+
const pool = createWorkerPool({
|
|
19965
|
+
count: state.config?.workers?.count,
|
|
19966
|
+
workerFactory: factory,
|
|
19967
|
+
onWorkerCrash(workerId, indexNames, error) {
|
|
19968
|
+
state.callbacks?.onWorkerCrash?.(workerId, indexNames, error);
|
|
19969
|
+
}
|
|
19970
|
+
});
|
|
19086
19971
|
for (const name of promotable) {
|
|
19087
19972
|
pool.addIndexToAll(name);
|
|
19088
19973
|
}
|
|
@@ -19119,7 +20004,7 @@ async function promoteBeforeBatch(state, indexName, incomingCount) {
|
|
|
19119
20004
|
if (!result.shouldPromote) return;
|
|
19120
20005
|
state.promotionInProgress = true;
|
|
19121
20006
|
const run = runPromotion(state, result.reason).catch((err) => {
|
|
19122
|
-
const error =
|
|
20007
|
+
const error = toError3(err);
|
|
19123
20008
|
state.promotionBlocked = isDeterministicFailure(error);
|
|
19124
20009
|
state.promotionInProgress = false;
|
|
19125
20010
|
state.callbacks?.onPromotionFailure?.(result.reason, error, !state.promotionBlocked);
|
|
@@ -19140,7 +20025,7 @@ async function checkPromotion(state) {
|
|
|
19140
20025
|
state.promotionInProgress = true;
|
|
19141
20026
|
setTimeout(() => {
|
|
19142
20027
|
const run = runPromotion(state, result.reason).catch((err) => {
|
|
19143
|
-
const error =
|
|
20028
|
+
const error = toError3(err);
|
|
19144
20029
|
state.promotionBlocked = isDeterministicFailure(error);
|
|
19145
20030
|
state.promotionInProgress = false;
|
|
19146
20031
|
state.callbacks?.onPromotionFailure?.(result.reason, error, !state.promotionBlocked);
|
|
@@ -19310,6 +20195,56 @@ function createWorkerOrchestrator(config, executor, promoter, indexRegistry, cal
|
|
|
19310
20195
|
state.segmentLedger.clear();
|
|
19311
20196
|
}
|
|
19312
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
|
+
}
|
|
19313
20248
|
async function replicate(action) {
|
|
19314
20249
|
await replicateToWorkers(state, action);
|
|
19315
20250
|
if (action.type === "attachSegments") {
|
|
@@ -19322,6 +20257,9 @@ function createWorkerOrchestrator(config, executor, promoter, indexRegistry, cal
|
|
|
19322
20257
|
replicateToWorkers: replicate,
|
|
19323
20258
|
awaitReplication: (indexName) => awaitReplicationIdle(state, indexName),
|
|
19324
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),
|
|
19325
20263
|
buildSegments: (requests) => buildSegments(state, requests),
|
|
19326
20264
|
segmentBuildConcurrency: (indexName) => segmentBuildConcurrency(state, indexName),
|
|
19327
20265
|
searchViaWorker: (indexName, params, globalStats, partitionIds) => searchViaWorker(state, indexName, params, globalStats, partitionIds),
|
|
@@ -19333,6 +20271,11 @@ function createWorkerOrchestrator(config, executor, promoter, indexRegistry, cal
|
|
|
19333
20271
|
};
|
|
19334
20272
|
}
|
|
19335
20273
|
|
|
20274
|
+
// src/engine/vector-fields.ts
|
|
20275
|
+
function getVectorFieldPaths(schema) {
|
|
20276
|
+
return new Set(extractVectorFieldsFromSchema(schema).keys());
|
|
20277
|
+
}
|
|
20278
|
+
|
|
19336
20279
|
// src/engine/watermark.ts
|
|
19337
20280
|
function createWatermarkNotifier(deps) {
|
|
19338
20281
|
const latchedCapacity = /* @__PURE__ */ new Map();
|
|
@@ -19368,10 +20311,7 @@ function createWatermarkNotifier(deps) {
|
|
|
19368
20311
|
}
|
|
19369
20312
|
|
|
19370
20313
|
// src/engine/core.ts
|
|
19371
|
-
function
|
|
19372
|
-
return new Set(extractVectorFieldsFromSchema(schema).keys());
|
|
19373
|
-
}
|
|
19374
|
-
function createEngineCore(config) {
|
|
20314
|
+
function createEngineCore(config, hooks) {
|
|
19375
20315
|
const executor = createDirectExecutor();
|
|
19376
20316
|
const promoter = createExecutionPromoter({
|
|
19377
20317
|
perIndexThreshold: config?.workers?.promotionThreshold,
|
|
@@ -19403,6 +20343,9 @@ function createEngineCore(config) {
|
|
|
19403
20343
|
if (handlers) {
|
|
19404
20344
|
for (const handler of handlers) handler({ workerCount, reason });
|
|
19405
20345
|
}
|
|
20346
|
+
void Promise.resolve(pluginRegistry.runHook("onWorkerPromote", { workerCount, reason })).catch((err) => {
|
|
20347
|
+
console.warn("onWorkerPromote plugin hook failed:", err instanceof Error ? err.message : String(err));
|
|
20348
|
+
});
|
|
19406
20349
|
},
|
|
19407
20350
|
onPromotionFailure(reason, error, retryable) {
|
|
19408
20351
|
const handlers = eventHandlers.get("workerPromoteFailure");
|
|
@@ -19411,6 +20354,14 @@ function createEngineCore(config) {
|
|
|
19411
20354
|
return;
|
|
19412
20355
|
}
|
|
19413
20356
|
for (const handler of handlers) handler({ reason, error, retryable });
|
|
20357
|
+
},
|
|
20358
|
+
onWorkerCrash(workerId, indexNames, error) {
|
|
20359
|
+
const handlers = eventHandlers.get("workerCrash");
|
|
20360
|
+
if (!handlers || handlers.size === 0) {
|
|
20361
|
+
console.warn(`Worker ${workerId} crashed:`, error);
|
|
20362
|
+
return;
|
|
20363
|
+
}
|
|
20364
|
+
for (const handler of handlers) handler({ workerId, indexNames, error });
|
|
19414
20365
|
}
|
|
19415
20366
|
});
|
|
19416
20367
|
const rebalancer = createRebalancer();
|
|
@@ -19444,8 +20395,12 @@ function createEngineCore(config) {
|
|
|
19444
20395
|
}
|
|
19445
20396
|
return manager;
|
|
19446
20397
|
}
|
|
19447
|
-
async function createIndexFromMetadata(metadata) {
|
|
19448
|
-
|
|
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
|
+
}
|
|
19449
20404
|
return;
|
|
19450
20405
|
}
|
|
19451
20406
|
const indexConfig = reconstructSchemaFromMetadata(metadata);
|
|
@@ -19474,7 +20429,7 @@ function createEngineCore(config) {
|
|
|
19474
20429
|
}
|
|
19475
20430
|
}
|
|
19476
20431
|
try {
|
|
19477
|
-
executor.createIndex(metadata.indexName, indexConfig, language);
|
|
20432
|
+
if (loadData) executor.createIndex(metadata.indexName, indexConfig, language);
|
|
19478
20433
|
} catch (err) {
|
|
19479
20434
|
if (err instanceof NarsilError && err.code === ErrorCodes.CONFIG_INVALID) {
|
|
19480
20435
|
throw new NarsilError(err.code, `Recovery of index "${metadata.indexName}" failed: ${err.message}`, {
|
|
@@ -19492,8 +20447,12 @@ function createEngineCore(config) {
|
|
|
19492
20447
|
embeddingAdapterName: adapterName,
|
|
19493
20448
|
vectorFieldPaths: getVectorFieldPaths(indexConfig.schema),
|
|
19494
20449
|
indexUuid: metadata.indexUuid ?? null,
|
|
19495
|
-
heldPartitions: metadata.heldPartitions ?? null
|
|
20450
|
+
heldPartitions: metadata.heldPartitions ?? null,
|
|
20451
|
+
documentCount: metadata.documentCount ?? 0,
|
|
20452
|
+
partitionCount: metadata.partitionCount
|
|
19496
20453
|
});
|
|
20454
|
+
if (loadData) await indexState.registerOpen(metadata.indexName);
|
|
20455
|
+
else indexState.registerClosed(metadata.indexName);
|
|
19497
20456
|
if (metadata.analysisRevision !== language.revision) {
|
|
19498
20457
|
analysisRebuild.markStale({
|
|
19499
20458
|
indexName: metadata.indexName,
|
|
@@ -19504,9 +20463,12 @@ function createEngineCore(config) {
|
|
|
19504
20463
|
}
|
|
19505
20464
|
}
|
|
19506
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
|
+
}
|
|
19507
20469
|
let invalidation = null;
|
|
19508
20470
|
const durability = createDurabilityFromTier(durabilityTier, {
|
|
19509
|
-
|
|
20471
|
+
getManager: (indexName) => executor.getManager(indexName),
|
|
19510
20472
|
indexRegistry,
|
|
19511
20473
|
createIndexFromMetadata,
|
|
19512
20474
|
emitFatalError(error) {
|
|
@@ -19515,7 +20477,13 @@ function createEngineCore(config) {
|
|
|
19515
20477
|
for (const handler of handlers) handler({ error });
|
|
19516
20478
|
}
|
|
19517
20479
|
},
|
|
19518
|
-
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
|
+
}
|
|
19519
20487
|
});
|
|
19520
20488
|
invalidation = createInvalidationFromConfig(config, durabilityTier?.kind ?? null, {
|
|
19521
20489
|
getManager: (indexName) => executor.getManager(indexName) ?? void 0,
|
|
@@ -19561,6 +20529,18 @@ function createEngineCore(config) {
|
|
|
19561
20529
|
resyncIndex: (indexName, wasPromoted) => orchestrator.resyncIndex(indexName, wasPromoted),
|
|
19562
20530
|
durabilityManager: durability?.manager ?? null
|
|
19563
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
|
+
});
|
|
19564
20544
|
const mutationCtx = {
|
|
19565
20545
|
executor,
|
|
19566
20546
|
pluginRegistry,
|
|
@@ -19614,6 +20594,7 @@ function createEngineCore(config) {
|
|
|
19614
20594
|
bufferIfRebalancing,
|
|
19615
20595
|
watermarkNotifier,
|
|
19616
20596
|
analysisRebuild,
|
|
20597
|
+
indexState,
|
|
19617
20598
|
mutationCtx,
|
|
19618
20599
|
rebalanceCtx
|
|
19619
20600
|
};
|
|
@@ -19773,8 +20754,11 @@ async function createEngineIndex(core, config, name, indexConfig, indexUuid) {
|
|
|
19773
20754
|
embeddingAdapterName,
|
|
19774
20755
|
vectorFieldPaths: getVectorFieldPaths(indexConfig.schema),
|
|
19775
20756
|
indexUuid: null,
|
|
19776
|
-
heldPartitions: null
|
|
20757
|
+
heldPartitions: null,
|
|
20758
|
+
documentCount: 0,
|
|
20759
|
+
partitionCount: indexConfig.partitions?.maxPartitions ?? 1
|
|
19777
20760
|
});
|
|
20761
|
+
await core.indexState.registerOpen(name);
|
|
19778
20762
|
if (core.durability) {
|
|
19779
20763
|
await core.durability.manager.persistMetadata(name);
|
|
19780
20764
|
}
|
|
@@ -19783,13 +20767,21 @@ async function createEngineIndex(core, config, name, indexConfig, indexUuid) {
|
|
|
19783
20767
|
async function dropEngineIndex(core, name) {
|
|
19784
20768
|
core.guardShutdown();
|
|
19785
20769
|
const entry = core.requireIndex(name);
|
|
19786
|
-
core.
|
|
19787
|
-
|
|
19788
|
-
|
|
19789
|
-
|
|
19790
|
-
|
|
19791
|
-
|
|
19792
|
-
|
|
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
|
+
});
|
|
19793
20785
|
await core.pluginRegistry.runHook("onIndexDrop", { indexName: name, config: entry.config });
|
|
19794
20786
|
}
|
|
19795
20787
|
function registerEngineEmbeddingAdapter(core, name, adapter) {
|
|
@@ -19824,6 +20816,7 @@ function registerEngineEmbeddingAdapter(core, name, adapter) {
|
|
|
19824
20816
|
// src/engine/lifecycle.ts
|
|
19825
20817
|
async function shutdownEngine(core) {
|
|
19826
20818
|
const { executor, durability, indexRegistry, eventHandlers, orchestrator } = core;
|
|
20819
|
+
core.indexState.dispose();
|
|
19827
20820
|
for (const [name] of indexRegistry) {
|
|
19828
20821
|
const manager = executor.getManager(name);
|
|
19829
20822
|
if (manager) {
|
|
@@ -21370,6 +22363,15 @@ async function executeRebalance(manager, indexName, targetPartitionCount, ctx) {
|
|
|
21370
22363
|
}
|
|
21371
22364
|
}
|
|
21372
22365
|
});
|
|
22366
|
+
try {
|
|
22367
|
+
await ctx.pluginRegistry.runHook("onPartitionSplit", {
|
|
22368
|
+
indexName,
|
|
22369
|
+
oldPartitionCount: oldCount,
|
|
22370
|
+
newPartitionCount: targetPartitionCount
|
|
22371
|
+
});
|
|
22372
|
+
} catch (hookErr) {
|
|
22373
|
+
console.warn("onPartitionSplit plugin hook failed:", hookErr instanceof Error ? hookErr.message : String(hookErr));
|
|
22374
|
+
}
|
|
21373
22375
|
await replayQueued(manager, indexName, waq, ctx);
|
|
21374
22376
|
if (ctx.durabilityManager) {
|
|
21375
22377
|
await ctx.durabilityManager.persistMetadata(indexName);
|
|
@@ -21584,7 +22586,7 @@ async function createSnapshot(manager, entry) {
|
|
|
21584
22586
|
const config = entry.config;
|
|
21585
22587
|
const bm25 = config.bm25;
|
|
21586
22588
|
const { encode: encode14 } = await import('@msgpack/msgpack');
|
|
21587
|
-
|
|
22589
|
+
const payload = encode14({
|
|
21588
22590
|
version: 2,
|
|
21589
22591
|
schema: config.schema,
|
|
21590
22592
|
language: entry.language.name,
|
|
@@ -21614,15 +22616,17 @@ async function createSnapshot(manager, entry) {
|
|
|
21614
22616
|
partitions: partitionBuffers,
|
|
21615
22617
|
vectorIndexes: vectorPayloads
|
|
21616
22618
|
});
|
|
22619
|
+
return packIndexSnapshotEnvelope(payload);
|
|
21617
22620
|
}
|
|
21618
22621
|
async function restoreFromSnapshot(indexName, data, deps) {
|
|
21619
22622
|
if (!(data instanceof Uint8Array)) {
|
|
21620
22623
|
throw new NarsilError(ErrorCodes.DOC_VALIDATION_FAILED, "Snapshot data must be a Uint8Array");
|
|
21621
22624
|
}
|
|
22625
|
+
const payloadBytes = hasNrslMagic(data) ? await unpackIndexSnapshotEnvelope(data) : data;
|
|
21622
22626
|
const { decode: decode14 } = await import('@msgpack/msgpack');
|
|
21623
22627
|
let decoded;
|
|
21624
22628
|
try {
|
|
21625
|
-
decoded = decode14(
|
|
22629
|
+
decoded = decode14(payloadBytes);
|
|
21626
22630
|
} catch (err) {
|
|
21627
22631
|
throw new NarsilError(
|
|
21628
22632
|
ErrorCodes.DOC_VALIDATION_FAILED,
|
|
@@ -21704,8 +22708,12 @@ async function restoreFromSnapshot(indexName, data, deps) {
|
|
|
21704
22708
|
embeddingAdapterName: adapterName,
|
|
21705
22709
|
vectorFieldPaths: vectorFieldPaths2,
|
|
21706
22710
|
indexUuid: null,
|
|
21707
|
-
heldPartitions: null
|
|
22711
|
+
heldPartitions: null,
|
|
22712
|
+
documentCount: 0,
|
|
22713
|
+
partitionCount: envelope.partitions.length
|
|
21708
22714
|
});
|
|
22715
|
+
await deps.indexState.registerOpen(indexName);
|
|
22716
|
+
const release = await deps.indexState.acquire(indexName, false);
|
|
21709
22717
|
try {
|
|
21710
22718
|
const manager = deps.requireManager(indexName);
|
|
21711
22719
|
while (manager.partitionCount < envelope.partitions.length) {
|
|
@@ -21725,26 +22733,36 @@ async function restoreFromSnapshot(indexName, data, deps) {
|
|
|
21725
22733
|
}
|
|
21726
22734
|
}
|
|
21727
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
|
+
}
|
|
21728
22753
|
} catch (err) {
|
|
21729
22754
|
try {
|
|
21730
22755
|
deps.executor.dropIndex(indexName);
|
|
21731
|
-
deps.indexRegistry.delete(indexName);
|
|
21732
22756
|
} catch (_) {
|
|
21733
22757
|
}
|
|
21734
|
-
|
|
21735
|
-
|
|
21736
|
-
|
|
21737
|
-
|
|
21738
|
-
for (let partitionId = 0; partitionId < manager.partitionCount; partitionId++) {
|
|
21739
|
-
for (const docId of manager.getPartition(partitionId).docIds()) {
|
|
21740
|
-
const document = manager.get(docId);
|
|
21741
|
-
if (document !== void 0) {
|
|
21742
|
-
await deps.durability.recordInsertOrUpdate(indexName, docId, document, noopApply2);
|
|
21743
|
-
}
|
|
21744
|
-
}
|
|
22758
|
+
deps.indexRegistry.delete(indexName);
|
|
22759
|
+
deps.indexState.forget(indexName);
|
|
22760
|
+
if (deps.durability) {
|
|
22761
|
+
await deps.durability.manager.removeIndex(indexName).catch(() => void 0);
|
|
21745
22762
|
}
|
|
21746
|
-
|
|
21747
|
-
|
|
22763
|
+
throw err;
|
|
22764
|
+
} finally {
|
|
22765
|
+
release();
|
|
21748
22766
|
}
|
|
21749
22767
|
}
|
|
21750
22768
|
function noopApply2() {
|
|
@@ -21817,6 +22835,137 @@ function readProcessMemory() {
|
|
|
21817
22835
|
}
|
|
21818
22836
|
}
|
|
21819
22837
|
|
|
22838
|
+
// src/search/cursor-binding.ts
|
|
22839
|
+
var ABSENT_TAG = 0;
|
|
22840
|
+
var NULL_TAG = 1;
|
|
22841
|
+
var FALSE_TAG = 2;
|
|
22842
|
+
var TRUE_TAG = 3;
|
|
22843
|
+
var NUMBER_TAG = 4;
|
|
22844
|
+
var STRING_TAG = 5;
|
|
22845
|
+
var LIST_TAG = 6;
|
|
22846
|
+
var MAP_TAG = 7;
|
|
22847
|
+
var encoder4 = new TextEncoder();
|
|
22848
|
+
function ensureCapacity2(stream, extra) {
|
|
22849
|
+
if (stream.length + extra <= stream.bytes.length) return;
|
|
22850
|
+
const grown = new Uint8Array(Math.max(stream.bytes.length * 2, stream.length + extra));
|
|
22851
|
+
grown.set(stream.bytes.subarray(0, stream.length));
|
|
22852
|
+
stream.bytes = grown;
|
|
22853
|
+
}
|
|
22854
|
+
function writeByte(stream, value) {
|
|
22855
|
+
ensureCapacity2(stream, 1);
|
|
22856
|
+
stream.bytes[stream.length] = value;
|
|
22857
|
+
stream.length += 1;
|
|
22858
|
+
}
|
|
22859
|
+
function writeUint32(stream, value) {
|
|
22860
|
+
ensureCapacity2(stream, 4);
|
|
22861
|
+
stream.bytes[stream.length] = value >>> 24 & 255;
|
|
22862
|
+
stream.bytes[stream.length + 1] = value >>> 16 & 255;
|
|
22863
|
+
stream.bytes[stream.length + 2] = value >>> 8 & 255;
|
|
22864
|
+
stream.bytes[stream.length + 3] = value & 255;
|
|
22865
|
+
stream.length += 4;
|
|
22866
|
+
}
|
|
22867
|
+
var numberBytes = new DataView(new ArrayBuffer(8));
|
|
22868
|
+
function writeNumber(stream, value) {
|
|
22869
|
+
numberBytes.setFloat64(0, value, false);
|
|
22870
|
+
ensureCapacity2(stream, 8);
|
|
22871
|
+
for (let i = 0; i < 8; i++) {
|
|
22872
|
+
stream.bytes[stream.length + i] = numberBytes.getUint8(i);
|
|
22873
|
+
}
|
|
22874
|
+
stream.length += 8;
|
|
22875
|
+
}
|
|
22876
|
+
function writeString(stream, value) {
|
|
22877
|
+
const encoded = encoder4.encode(value);
|
|
22878
|
+
writeUint32(stream, encoded.length);
|
|
22879
|
+
ensureCapacity2(stream, encoded.length);
|
|
22880
|
+
stream.bytes.set(encoded, stream.length);
|
|
22881
|
+
stream.length += encoded.length;
|
|
22882
|
+
}
|
|
22883
|
+
function writeValue(stream, value) {
|
|
22884
|
+
if (value === void 0) {
|
|
22885
|
+
writeByte(stream, ABSENT_TAG);
|
|
22886
|
+
return;
|
|
22887
|
+
}
|
|
22888
|
+
if (value === null) {
|
|
22889
|
+
writeByte(stream, NULL_TAG);
|
|
22890
|
+
return;
|
|
22891
|
+
}
|
|
22892
|
+
if (typeof value === "boolean") {
|
|
22893
|
+
writeByte(stream, value ? TRUE_TAG : FALSE_TAG);
|
|
22894
|
+
return;
|
|
22895
|
+
}
|
|
22896
|
+
if (typeof value === "number") {
|
|
22897
|
+
writeByte(stream, NUMBER_TAG);
|
|
22898
|
+
writeNumber(stream, value);
|
|
22899
|
+
return;
|
|
22900
|
+
}
|
|
22901
|
+
if (typeof value === "string") {
|
|
22902
|
+
writeByte(stream, STRING_TAG);
|
|
22903
|
+
writeString(stream, value);
|
|
22904
|
+
return;
|
|
22905
|
+
}
|
|
22906
|
+
if (Array.isArray(value)) {
|
|
22907
|
+
writeByte(stream, LIST_TAG);
|
|
22908
|
+
writeUint32(stream, value.length);
|
|
22909
|
+
for (const member of value) {
|
|
22910
|
+
writeValue(stream, member ?? null);
|
|
22911
|
+
}
|
|
22912
|
+
return;
|
|
22913
|
+
}
|
|
22914
|
+
const record = value;
|
|
22915
|
+
const keys = Object.keys(record).sort(compareCodePoints);
|
|
22916
|
+
writeByte(stream, MAP_TAG);
|
|
22917
|
+
writeUint32(stream, keys.length);
|
|
22918
|
+
for (const key of keys) {
|
|
22919
|
+
writeByte(stream, STRING_TAG);
|
|
22920
|
+
writeString(stream, key);
|
|
22921
|
+
writeValue(stream, record[key] ?? null);
|
|
22922
|
+
}
|
|
22923
|
+
}
|
|
22924
|
+
function boundVectorOf(vector) {
|
|
22925
|
+
if (vector === void 0) return void 0;
|
|
22926
|
+
const bound = { ...vector };
|
|
22927
|
+
if (vector.value !== void 0) {
|
|
22928
|
+
bound.value = Array.from(vector.value, Math.fround);
|
|
22929
|
+
}
|
|
22930
|
+
if (vector.text !== void 0) {
|
|
22931
|
+
delete bound.value;
|
|
22932
|
+
}
|
|
22933
|
+
return bound;
|
|
22934
|
+
}
|
|
22935
|
+
var FILTERS_SLOT = 2;
|
|
22936
|
+
var BOUND_SLOT_COUNT = 15;
|
|
22937
|
+
function bindingOf(values) {
|
|
22938
|
+
const stream = { bytes: new Uint8Array(256), length: 0 };
|
|
22939
|
+
for (const value of values) {
|
|
22940
|
+
writeValue(stream, value);
|
|
22941
|
+
}
|
|
22942
|
+
return fnv1aBytes(stream.bytes.subarray(0, stream.length)).toString(16).padStart(8, "0");
|
|
22943
|
+
}
|
|
22944
|
+
function queryBindingOf(params) {
|
|
22945
|
+
return bindingOf([
|
|
22946
|
+
params.term,
|
|
22947
|
+
params.fields,
|
|
22948
|
+
params.filters,
|
|
22949
|
+
params.boost,
|
|
22950
|
+
params.minScore,
|
|
22951
|
+
params.termMatch,
|
|
22952
|
+
params.tolerance,
|
|
22953
|
+
params.prefixLength,
|
|
22954
|
+
params.prefix,
|
|
22955
|
+
params.exact,
|
|
22956
|
+
params.pinned,
|
|
22957
|
+
params.mode,
|
|
22958
|
+
params.hybrid,
|
|
22959
|
+
boundVectorOf(params.vector),
|
|
22960
|
+
params.scoring ?? "local"
|
|
22961
|
+
]);
|
|
22962
|
+
}
|
|
22963
|
+
function listBindingOf(filters) {
|
|
22964
|
+
const values = new Array(BOUND_SLOT_COUNT).fill(void 0);
|
|
22965
|
+
values[FILTERS_SLOT] = filters;
|
|
22966
|
+
return bindingOf(values);
|
|
22967
|
+
}
|
|
22968
|
+
|
|
21820
22969
|
// src/engine/list-documents/index.ts
|
|
21821
22970
|
function clampListLimit(limit) {
|
|
21822
22971
|
const clamped = Math.max(1, clampLimit(limit));
|
|
@@ -21851,7 +23000,7 @@ function collectCandidates(partition, anchor, matches, wanted, out) {
|
|
|
21851
23000
|
taken++;
|
|
21852
23001
|
}
|
|
21853
23002
|
}
|
|
21854
|
-
function pageInIdOrder(partitions, cursor, filtered, limit) {
|
|
23003
|
+
function pageInIdOrder(partitions, cursor, filtered, limit, binding) {
|
|
21855
23004
|
const anchor = cursor === null ? null : cursor.anchor;
|
|
21856
23005
|
const candidates = [];
|
|
21857
23006
|
for (let index = 0; index < partitions.length; index++) {
|
|
@@ -21866,10 +23015,10 @@ function pageInIdOrder(partitions, cursor, filtered, limit) {
|
|
|
21866
23015
|
candidates.sort(compareCodePoints);
|
|
21867
23016
|
const hasMore = candidates.length > limit;
|
|
21868
23017
|
const ids = hasMore ? candidates.slice(0, limit) : candidates;
|
|
21869
|
-
const nextCursor = hasMore && ids.length > 0 ? { anchor: ids[ids.length - 1], score: null, sortKey: null, sortSignature: null } : null;
|
|
23018
|
+
const nextCursor = hasMore && ids.length > 0 ? { anchor: ids[ids.length - 1], score: null, sortKey: null, sortSignature: null, binding } : null;
|
|
21870
23019
|
return { ids, nextCursor };
|
|
21871
23020
|
}
|
|
21872
|
-
function pageInSortOrder(partitions, cursor, filtered, limit, sort, signature, schema) {
|
|
23021
|
+
function pageInSortOrder(partitions, cursor, filtered, limit, sort, signature, schema, binding) {
|
|
21873
23022
|
const normalized = normalizeSort(sort);
|
|
21874
23023
|
const fields = normalized.map((entry) => entry.field);
|
|
21875
23024
|
const directions = normalized.map((entry) => entry.direction);
|
|
@@ -21892,7 +23041,7 @@ function pageInSortOrder(partitions, cursor, filtered, limit, sort, signature, s
|
|
|
21892
23041
|
const hasMore = merged.length > limit;
|
|
21893
23042
|
const page = hasMore ? merged.slice(0, limit) : merged;
|
|
21894
23043
|
const last = page[page.length - 1];
|
|
21895
|
-
const nextCursor = hasMore && last !== void 0 ? { anchor: last.id, score: null, sortKey: last.key, sortSignature: signature } : null;
|
|
23044
|
+
const nextCursor = hasMore && last !== void 0 ? { anchor: last.id, score: null, sortKey: last.key, sortSignature: signature, binding } : null;
|
|
21896
23045
|
return { ids: page.map((entry) => entry.id), nextCursor };
|
|
21897
23046
|
}
|
|
21898
23047
|
function countAcrossPartitions(manager, partitionIds, partitions) {
|
|
@@ -21911,15 +23060,16 @@ function executeListDocuments(params, context) {
|
|
|
21911
23060
|
const limit = clampListLimit(params.limit);
|
|
21912
23061
|
const signature = sortSignatureOf(params.sort);
|
|
21913
23062
|
requireSortableFields(params.sort, schema);
|
|
23063
|
+
const binding = listBindingOf(params.filters);
|
|
21914
23064
|
let cursor = null;
|
|
21915
23065
|
if (params.cursor !== void 0) {
|
|
21916
23066
|
cursor = decodePageCursor(params.cursor);
|
|
21917
|
-
requireMatchingCursor(cursor, params.cursor, signature, false);
|
|
23067
|
+
requireMatchingCursor(cursor, params.cursor, signature, false, binding);
|
|
21918
23068
|
}
|
|
21919
23069
|
const partitions = context.partitionIds === void 0 ? manager.getAllPartitions() : context.partitionIds.map((partitionId) => manager.partitionAt(partitionId)).filter((partition) => partition !== void 0);
|
|
21920
23070
|
const filtered = params.filters === void 0 ? null : collectFilterMatches(partitions, params.filters, schema);
|
|
21921
23071
|
const total = filtered === null ? countAcrossPartitions(manager, context.partitionIds, partitions) : filtered.total;
|
|
21922
|
-
const page = params.sort === void 0 || signature === null ? pageInIdOrder(partitions, cursor, filtered, limit) : pageInSortOrder(partitions, cursor, filtered, limit, params.sort, signature, schema);
|
|
23072
|
+
const page = params.sort === void 0 || signature === null ? pageInIdOrder(partitions, cursor, filtered, limit, binding) : pageInSortOrder(partitions, cursor, filtered, limit, params.sort, signature, schema, binding);
|
|
21923
23073
|
const projection = resolveProjection(params.document);
|
|
21924
23074
|
const documents = [];
|
|
21925
23075
|
for (const docId of page.ids) {
|
|
@@ -21987,18 +23137,14 @@ function applyGrouping(hits, group, getDocument) {
|
|
|
21987
23137
|
}
|
|
21988
23138
|
const result = { values: entry.values, hits: groupHits };
|
|
21989
23139
|
if (group.reduce) {
|
|
21990
|
-
|
|
21991
|
-
|
|
21992
|
-
|
|
21993
|
-
|
|
21994
|
-
|
|
21995
|
-
|
|
21996
|
-
|
|
21997
|
-
|
|
21998
|
-
;
|
|
21999
|
-
result.reduced = accumulator;
|
|
22000
|
-
} catch (err) {
|
|
22001
|
-
result.reducerError = err instanceof Error ? err.message : String(err);
|
|
23140
|
+
const folded = foldGroupReducer(
|
|
23141
|
+
group.reduce,
|
|
23142
|
+
groupHits.map((hit) => ({ document: getDocument(hit.id), score: hit.score ?? 0 }))
|
|
23143
|
+
);
|
|
23144
|
+
if (folded.reducerError !== void 0) {
|
|
23145
|
+
result.reducerError = folded.reducerError;
|
|
23146
|
+
} else {
|
|
23147
|
+
result.reduced = folded.reduced;
|
|
22002
23148
|
}
|
|
22003
23149
|
}
|
|
22004
23150
|
groups.push(result);
|
|
@@ -22011,19 +23157,49 @@ function applyGrouping(hits, group, getDocument) {
|
|
|
22011
23157
|
const bId = b.hits.length > 0 ? b.hits[0].id : "";
|
|
22012
23158
|
return compareCodePoints(aId, bId);
|
|
22013
23159
|
});
|
|
23160
|
+
if (group.limit !== void 0 && Number.isFinite(group.limit) && group.limit >= 0) {
|
|
23161
|
+
const cap = Math.floor(group.limit);
|
|
23162
|
+
if (groups.length > cap) {
|
|
23163
|
+
groups.length = cap;
|
|
23164
|
+
}
|
|
23165
|
+
}
|
|
22014
23166
|
return groups;
|
|
22015
23167
|
}
|
|
23168
|
+
function foldGroupReducer(reduce, rows) {
|
|
23169
|
+
try {
|
|
23170
|
+
let accumulator = reduce.initialValue();
|
|
23171
|
+
for (const row of rows) {
|
|
23172
|
+
if (row.document !== void 0) {
|
|
23173
|
+
accumulator = reduce.reducer(accumulator, row.document, row.score);
|
|
23174
|
+
}
|
|
23175
|
+
}
|
|
23176
|
+
return { reduced: accumulator };
|
|
23177
|
+
} catch (err) {
|
|
23178
|
+
return { reducerError: err instanceof Error ? err.message : String(err) };
|
|
23179
|
+
}
|
|
23180
|
+
}
|
|
22016
23181
|
|
|
22017
23182
|
// src/search/pinning.ts
|
|
23183
|
+
function dedupePinnedEntries(pinned) {
|
|
23184
|
+
const deduped = [];
|
|
23185
|
+
const seen = /* @__PURE__ */ new Set();
|
|
23186
|
+
for (const entry of pinned) {
|
|
23187
|
+
if (seen.has(entry.docId)) continue;
|
|
23188
|
+
seen.add(entry.docId);
|
|
23189
|
+
deduped.push(entry);
|
|
23190
|
+
}
|
|
23191
|
+
return deduped;
|
|
23192
|
+
}
|
|
22018
23193
|
function applyPinning(hits, pinned, resolveDoc) {
|
|
22019
23194
|
const result = hits.slice();
|
|
22020
|
-
const
|
|
23195
|
+
const deduped = dedupePinnedEntries(pinned);
|
|
23196
|
+
const pinnedDocIds = new Set(deduped.map((entry) => entry.docId));
|
|
22021
23197
|
for (let i = result.length - 1; i >= 0; i--) {
|
|
22022
23198
|
if (pinnedDocIds.has(result[i].id)) {
|
|
22023
23199
|
result.splice(i, 1);
|
|
22024
23200
|
}
|
|
22025
23201
|
}
|
|
22026
|
-
const sorted =
|
|
23202
|
+
const sorted = deduped.sort((a, b) => a.position - b.position);
|
|
22027
23203
|
for (const entry of sorted) {
|
|
22028
23204
|
const doc = resolveDoc(entry.docId);
|
|
22029
23205
|
if (!doc) continue;
|
|
@@ -22217,17 +23393,7 @@ function applyHighlights(hits, params, language, analysis) {
|
|
|
22217
23393
|
|
|
22218
23394
|
// src/engine/query/shared.ts
|
|
22219
23395
|
function partitionsFor(manager, partitionIds) {
|
|
22220
|
-
|
|
22221
|
-
return manager.getAllPartitions();
|
|
22222
|
-
}
|
|
22223
|
-
const partitions = [];
|
|
22224
|
-
for (const partitionId of partitionIds) {
|
|
22225
|
-
const partition = manager.partitionAt(partitionId);
|
|
22226
|
-
if (partition !== void 0) {
|
|
22227
|
-
partitions.push(partition);
|
|
22228
|
-
}
|
|
22229
|
-
}
|
|
22230
|
-
return partitions;
|
|
23396
|
+
return partitionsIn(manager, partitionIds);
|
|
22231
23397
|
}
|
|
22232
23398
|
function coverageFor(manager, partitionIds) {
|
|
22233
23399
|
if (partitionIds === void 0) {
|
|
@@ -22321,7 +23487,7 @@ function executeSortedQueryPage(params, context, limit, offset, sortSignature) {
|
|
|
22321
23487
|
let anchorId = null;
|
|
22322
23488
|
if (params.searchAfter !== void 0) {
|
|
22323
23489
|
const decoded = decodePageCursor(params.searchAfter);
|
|
22324
|
-
requireMatchingCursor(decoded, params.searchAfter, sortSignature, true);
|
|
23490
|
+
requireMatchingCursor(decoded, params.searchAfter, sortSignature, true, context.cursorBinding);
|
|
22325
23491
|
anchorKey = decoded.sortKey;
|
|
22326
23492
|
anchorId = decoded.anchor;
|
|
22327
23493
|
}
|
|
@@ -22366,7 +23532,13 @@ function executeSortedQueryPage(params, context, limit, offset, sortSignature) {
|
|
|
22366
23532
|
const last = page[page.length - 1];
|
|
22367
23533
|
let cursor;
|
|
22368
23534
|
if (hasMore && last !== void 0) {
|
|
22369
|
-
cursor = encodePageCursor({
|
|
23535
|
+
cursor = encodePageCursor({
|
|
23536
|
+
anchor: last.id,
|
|
23537
|
+
score: null,
|
|
23538
|
+
sortKey: last.key,
|
|
23539
|
+
sortSignature,
|
|
23540
|
+
binding: context.cursorBinding
|
|
23541
|
+
});
|
|
22370
23542
|
}
|
|
22371
23543
|
return {
|
|
22372
23544
|
hits: page.map((entry) => ({ id: entry.id, document: void 0 })),
|
|
@@ -22652,7 +23824,7 @@ async function executeQuery(params, context) {
|
|
|
22652
23824
|
if (params.group) {
|
|
22653
23825
|
groups = applyGrouping(hits, params.group, (docId) => manager.getRef(docId));
|
|
22654
23826
|
}
|
|
22655
|
-
if (params.pinned) {
|
|
23827
|
+
if (params.pinned && params.searchAfter === void 0 && context.partitionIds === void 0) {
|
|
22656
23828
|
hits = applyPinning(hits, params.pinned, (docId) => {
|
|
22657
23829
|
const doc = manager.getRef(docId);
|
|
22658
23830
|
if (!doc) return void 0;
|
|
@@ -22667,7 +23839,16 @@ async function executeQuery(params, context) {
|
|
|
22667
23839
|
sortKeyOf
|
|
22668
23840
|
};
|
|
22669
23841
|
}
|
|
22670
|
-
const
|
|
23842
|
+
const pinnedIds = params.pinned !== void 0 && params.searchAfter === void 0 ? new Set(params.pinned.map((entry) => entry.docId)) : void 0;
|
|
23843
|
+
const paged = applyPagination(
|
|
23844
|
+
hits,
|
|
23845
|
+
limit,
|
|
23846
|
+
offset,
|
|
23847
|
+
context.cursorBinding,
|
|
23848
|
+
params.searchAfter,
|
|
23849
|
+
sortContext,
|
|
23850
|
+
pinnedIds
|
|
23851
|
+
);
|
|
22671
23852
|
paginated = paged.paginated;
|
|
22672
23853
|
nextCursor = paged.nextCursor;
|
|
22673
23854
|
count = fanOutResult.totalMatched;
|
|
@@ -22736,6 +23917,11 @@ async function executePreflight(params, context) {
|
|
|
22736
23917
|
} else if (isHybridMode && hasGlobalVectorIndex) {
|
|
22737
23918
|
const result = await executeHybridSearch(params, context, preflightLimit, preflightOffset);
|
|
22738
23919
|
totalMatched = result.totalMatched;
|
|
23920
|
+
} else if (countsWithoutScores(params)) {
|
|
23921
|
+
totalMatched = fanOutMatchCount(manager, params, language, config.schema, {
|
|
23922
|
+
searchOptions: searchOptionsFor(manager),
|
|
23923
|
+
partitionIds: context.partitionIds
|
|
23924
|
+
});
|
|
22739
23925
|
} else {
|
|
22740
23926
|
const scoring = scoringConfigFor(params, context);
|
|
22741
23927
|
const workerResult = workerSearch ? await workerSearch(indexName, params, broadcastStatsForWorker(params, context, scoring), context.partitionIds) : null;
|
|
@@ -22864,83 +24050,107 @@ function broadcastStatsFor(core, options) {
|
|
|
22864
24050
|
}
|
|
22865
24051
|
async function runEngineQuery(core, indexName, params, options) {
|
|
22866
24052
|
core.guardShutdown();
|
|
22867
|
-
const
|
|
22868
|
-
const manager = core.requireManager(indexName);
|
|
22869
|
-
const resolvedParams = await resolveVectorText(
|
|
22870
|
-
scopedParams(params),
|
|
22871
|
-
entry.embeddingAdapter,
|
|
22872
|
-
core.abortController.signal,
|
|
22873
|
-
entry.embeddingAdapterName
|
|
22874
|
-
);
|
|
22875
|
-
await core.pluginRegistry.runHook("beforeSearch", { indexName, params: resolvedParams });
|
|
22876
|
-
const workerSearch = core.orchestrator.isPromoted() ? core.orchestrator.searchViaWorker.bind(core.orchestrator) : void 0;
|
|
22877
|
-
const result = await executeQuery(resolvedParams, {
|
|
22878
|
-
manager,
|
|
22879
|
-
language: entry.language,
|
|
22880
|
-
config: entry.config,
|
|
22881
|
-
workerSearch,
|
|
22882
|
-
indexName,
|
|
22883
|
-
broadcastStats: broadcastStatsFor(core),
|
|
22884
|
-
partitionIds: options?.partitionIds
|
|
22885
|
-
});
|
|
24053
|
+
const release = await core.indexState.acquire(indexName);
|
|
22886
24054
|
try {
|
|
22887
|
-
|
|
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,
|
|
22888
24070
|
indexName,
|
|
22889
|
-
|
|
22890
|
-
|
|
24071
|
+
broadcastStats: broadcastStatsFor(core, options),
|
|
24072
|
+
partitionIds: options?.partitionIds,
|
|
24073
|
+
cursorBinding: queryBindingOf(params)
|
|
22891
24074
|
});
|
|
22892
|
-
|
|
22893
|
-
|
|
22894
|
-
|
|
22895
|
-
|
|
22896
|
-
|
|
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();
|
|
22897
24090
|
}
|
|
22898
|
-
return result;
|
|
22899
24091
|
}
|
|
22900
24092
|
async function runEnginePreflight(core, indexName, params, options) {
|
|
22901
24093
|
core.guardShutdown();
|
|
22902
|
-
const
|
|
22903
|
-
|
|
22904
|
-
|
|
22905
|
-
|
|
22906
|
-
|
|
22907
|
-
|
|
22908
|
-
|
|
22909
|
-
|
|
22910
|
-
|
|
22911
|
-
|
|
22912
|
-
|
|
22913
|
-
|
|
22914
|
-
|
|
22915
|
-
|
|
22916
|
-
|
|
22917
|
-
|
|
22918
|
-
|
|
22919
|
-
|
|
22920
|
-
|
|
22921
|
-
|
|
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();
|
|
22922
24121
|
}
|
|
22923
|
-
return result;
|
|
22924
24122
|
}
|
|
22925
24123
|
async function runEngineSuggest(core, indexName, params, partitionIds) {
|
|
22926
24124
|
core.guardShutdown();
|
|
22927
|
-
const
|
|
22928
|
-
|
|
22929
|
-
|
|
22930
|
-
|
|
22931
|
-
|
|
22932
|
-
|
|
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();
|
|
22933
24139
|
}
|
|
22934
|
-
return result;
|
|
22935
24140
|
}
|
|
22936
24141
|
async function runEngineListDocuments(core, indexName, params, partitionIds) {
|
|
22937
24142
|
core.guardShutdown();
|
|
22938
|
-
const
|
|
22939
|
-
|
|
22940
|
-
|
|
22941
|
-
|
|
22942
|
-
|
|
22943
|
-
|
|
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
|
+
}
|
|
22944
24154
|
}
|
|
22945
24155
|
|
|
22946
24156
|
// src/narsil/operations.ts
|
|
@@ -22960,6 +24170,16 @@ function createNarsilFromCore(core, config) {
|
|
|
22960
24170
|
mutationCtx,
|
|
22961
24171
|
rebalanceCtx
|
|
22962
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
|
+
}
|
|
22963
24183
|
const narsil = {
|
|
22964
24184
|
createIndex(name, indexConfig) {
|
|
22965
24185
|
return createEngineIndex(core, config, name, indexConfig);
|
|
@@ -22970,15 +24190,27 @@ function createNarsilFromCore(core, config) {
|
|
|
22970
24190
|
dropIndex(name) {
|
|
22971
24191
|
return dropEngineIndex(core, name);
|
|
22972
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
|
+
},
|
|
22973
24203
|
listIndexes() {
|
|
22974
24204
|
const infos = [];
|
|
22975
24205
|
for (const [name, entry] of indexRegistry) {
|
|
22976
24206
|
const manager = executor.getManager(name);
|
|
22977
24207
|
infos.push({
|
|
22978
24208
|
name,
|
|
22979
|
-
documentCount: manager?.countDocuments() ??
|
|
22980
|
-
partitionCount: manager?.partitionCount ??
|
|
24209
|
+
documentCount: manager?.countDocuments() ?? entry.documentCount,
|
|
24210
|
+
partitionCount: manager?.partitionCount ?? entry.partitionCount,
|
|
22981
24211
|
language: entry.language.name,
|
|
24212
|
+
state: core.indexState.stateOf(name),
|
|
24213
|
+
reopenCount: core.indexState.reopenCount(name),
|
|
22982
24214
|
...core.analysisRebuild.isStale(name) ? { analysisStale: true } : {}
|
|
22983
24215
|
});
|
|
22984
24216
|
}
|
|
@@ -22989,8 +24221,8 @@ function createNarsilFromCore(core, config) {
|
|
|
22989
24221
|
const entry = requireIndex(indexName);
|
|
22990
24222
|
const manager = executor.getManager(indexName);
|
|
22991
24223
|
return {
|
|
22992
|
-
documentCount: manager?.countDocuments() ??
|
|
22993
|
-
partitionCount: manager?.partitionCount ??
|
|
24224
|
+
documentCount: manager?.countDocuments() ?? entry.documentCount,
|
|
24225
|
+
partitionCount: manager?.partitionCount ?? entry.partitionCount,
|
|
22994
24226
|
estimatedMemoryBytes: manager?.estimateMemoryBytes() ?? 0,
|
|
22995
24227
|
language: entry.language.name,
|
|
22996
24228
|
schema: entry.config.schema
|
|
@@ -23002,47 +24234,48 @@ function createNarsilFromCore(core, config) {
|
|
|
23002
24234
|
return executor.getManager(indexName)?.getPartitionStats() ?? [];
|
|
23003
24235
|
},
|
|
23004
24236
|
insert(indexName, document, docId, options) {
|
|
23005
|
-
return insertDocument(mutationCtx, indexName, document, docId, options);
|
|
24237
|
+
return withOpenIndex(indexName, () => insertDocument(mutationCtx, indexName, document, docId, options));
|
|
23006
24238
|
},
|
|
23007
24239
|
insertBatch(indexName, documents, options) {
|
|
23008
|
-
return insertDocumentBatch(mutationCtx, indexName, documents, options);
|
|
24240
|
+
return withOpenIndex(indexName, () => insertDocumentBatch(mutationCtx, indexName, documents, options));
|
|
23009
24241
|
},
|
|
23010
24242
|
remove(indexName, docId) {
|
|
23011
|
-
return removeDocument(mutationCtx, indexName, docId);
|
|
24243
|
+
return withOpenIndex(indexName, () => removeDocument(mutationCtx, indexName, docId));
|
|
23012
24244
|
},
|
|
23013
24245
|
removeBatch(indexName, docIds) {
|
|
23014
|
-
return removeDocumentBatch(mutationCtx, indexName, docIds);
|
|
24246
|
+
return withOpenIndex(indexName, () => removeDocumentBatch(mutationCtx, indexName, docIds));
|
|
23015
24247
|
},
|
|
23016
24248
|
update(indexName, docId, document) {
|
|
23017
|
-
return updateDocument(mutationCtx, indexName, docId, document);
|
|
24249
|
+
return withOpenIndex(indexName, () => updateDocument(mutationCtx, indexName, docId, document));
|
|
23018
24250
|
},
|
|
23019
24251
|
updateBatch(indexName, updates) {
|
|
23020
|
-
return updateDocumentBatch(mutationCtx, indexName, updates);
|
|
24252
|
+
return withOpenIndex(indexName, () => updateDocumentBatch(mutationCtx, indexName, updates));
|
|
23021
24253
|
},
|
|
23022
24254
|
async get(indexName, docId) {
|
|
23023
|
-
|
|
23024
|
-
requireIndex(indexName);
|
|
23025
|
-
return executor.execute({ type: "get", indexName, docId, requestId: docId });
|
|
24255
|
+
return withOpenIndex(indexName, () => executor.execute({ type: "get", indexName, docId, requestId: docId }));
|
|
23026
24256
|
},
|
|
23027
24257
|
async getMultiple(indexName, docIds) {
|
|
23028
24258
|
guardShutdown();
|
|
23029
24259
|
requireIndex(indexName);
|
|
23030
|
-
|
|
23031
|
-
|
|
23032
|
-
const
|
|
23033
|
-
|
|
23034
|
-
|
|
23035
|
-
|
|
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
|
+
});
|
|
23036
24273
|
},
|
|
23037
24274
|
async has(indexName, docId) {
|
|
23038
|
-
|
|
23039
|
-
requireIndex(indexName);
|
|
23040
|
-
return executor.execute({ type: "has", indexName, docId, requestId: docId });
|
|
24275
|
+
return withOpenIndex(indexName, () => executor.execute({ type: "has", indexName, docId, requestId: docId }));
|
|
23041
24276
|
},
|
|
23042
24277
|
async countDocuments(indexName) {
|
|
23043
|
-
|
|
23044
|
-
requireIndex(indexName);
|
|
23045
|
-
return executor.execute({ type: "count", indexName, requestId: indexName });
|
|
24278
|
+
return withOpenIndex(indexName, () => executor.execute({ type: "count", indexName, requestId: indexName }));
|
|
23046
24279
|
},
|
|
23047
24280
|
async listDocuments(indexName, params) {
|
|
23048
24281
|
return runEngineListDocuments(core, indexName, params ?? {});
|
|
@@ -23059,15 +24292,15 @@ function createNarsilFromCore(core, config) {
|
|
|
23059
24292
|
async rebuildAnalysis(indexName) {
|
|
23060
24293
|
guardShutdown();
|
|
23061
24294
|
requireIndex(indexName);
|
|
23062
|
-
await core.analysisRebuild.rebuild(indexName);
|
|
24295
|
+
await withOpenIndex(indexName, () => core.analysisRebuild.rebuild(indexName));
|
|
23063
24296
|
},
|
|
23064
24297
|
async snapshot(indexName) {
|
|
23065
24298
|
guardShutdown();
|
|
23066
|
-
return createSnapshot(requireManager(indexName), requireIndex(indexName));
|
|
24299
|
+
return withOpenIndex(indexName, () => createSnapshot(requireManager(indexName), requireIndex(indexName)));
|
|
23067
24300
|
},
|
|
23068
24301
|
async restore(indexName, data) {
|
|
23069
24302
|
guardShutdown();
|
|
23070
|
-
|
|
24303
|
+
await restoreFromSnapshot(indexName, data, {
|
|
23071
24304
|
executor,
|
|
23072
24305
|
indexRegistry,
|
|
23073
24306
|
getVectorFieldPaths,
|
|
@@ -23077,66 +24310,74 @@ function createNarsilFromCore(core, config) {
|
|
|
23077
24310
|
embeddingAdapters: core.embeddingAdapters,
|
|
23078
24311
|
defaultEmbeddingAdapter: config?.embedding ?? null,
|
|
23079
24312
|
markAnalysisStale: core.analysisRebuild.markStale,
|
|
23080
|
-
clearAnalysisStale: core.analysisRebuild.clearStale
|
|
24313
|
+
clearAnalysisStale: core.analysisRebuild.clearStale,
|
|
24314
|
+
indexState: core.indexState
|
|
23081
24315
|
});
|
|
23082
24316
|
},
|
|
23083
24317
|
async checkpoint(indexName) {
|
|
23084
24318
|
guardShutdown();
|
|
23085
24319
|
requireIndex(indexName);
|
|
23086
|
-
|
|
23087
|
-
await durability.manager.checkpoint(indexName);
|
|
23088
|
-
}
|
|
24320
|
+
await withOpenIndex(indexName, async () => {
|
|
24321
|
+
if (durability) await durability.manager.checkpoint(indexName);
|
|
24322
|
+
});
|
|
23089
24323
|
},
|
|
23090
24324
|
async clear(indexName) {
|
|
23091
24325
|
guardShutdown();
|
|
23092
24326
|
requireIndex(indexName);
|
|
23093
|
-
await
|
|
23094
|
-
|
|
23095
|
-
|
|
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
|
+
});
|
|
23096
24332
|
},
|
|
23097
24333
|
async rebalance(indexName, targetPartitionCount) {
|
|
23098
24334
|
guardShutdown();
|
|
23099
24335
|
requireIndex(indexName);
|
|
23100
|
-
return
|
|
24336
|
+
return withOpenIndex(
|
|
24337
|
+
indexName,
|
|
24338
|
+
() => executeRebalance(requireManager(indexName), indexName, targetPartitionCount, rebalanceCtx)
|
|
24339
|
+
);
|
|
23101
24340
|
},
|
|
23102
24341
|
async updatePartitionConfig(indexName, partitionConfig) {
|
|
23103
24342
|
guardShutdown();
|
|
23104
|
-
|
|
23105
|
-
|
|
23106
|
-
|
|
23107
|
-
|
|
23108
|
-
|
|
23109
|
-
|
|
23110
|
-
|
|
23111
|
-
|
|
23112
|
-
|
|
23113
|
-
|
|
23114
|
-
|
|
23115
|
-
ErrorCodes.PARTITION_CAPACITY_EXCEEDED,
|
|
23116
|
-
`maxPartitions (${partitionConfig.maxPartitions}) is less than the current partition count (${manager.partitionCount})`,
|
|
23117
|
-
{ maxPartitions: partitionConfig.maxPartitions, partitionCount: manager.partitionCount }
|
|
23118
|
-
);
|
|
23119
|
-
}
|
|
23120
|
-
const currentDocCount = manager.countDocuments();
|
|
23121
|
-
const newMaxDocs = partitionConfig.maxDocsPerPartition ?? entry.config.partitions?.maxDocsPerPartition;
|
|
23122
|
-
if (newMaxDocs !== void 0) {
|
|
23123
|
-
const newTotalCapacity = newMaxDocs * manager.partitionCount;
|
|
23124
|
-
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) {
|
|
23125
24354
|
throw new NarsilError(
|
|
23126
24355
|
ErrorCodes.PARTITION_CAPACITY_EXCEEDED,
|
|
23127
|
-
`
|
|
23128
|
-
{
|
|
24356
|
+
`maxPartitions (${partitionConfig.maxPartitions}) is less than the current partition count (${manager.partitionCount})`,
|
|
24357
|
+
{ maxPartitions: partitionConfig.maxPartitions, partitionCount: manager.partitionCount }
|
|
23129
24358
|
);
|
|
23130
24359
|
}
|
|
23131
|
-
|
|
23132
|
-
|
|
23133
|
-
|
|
23134
|
-
|
|
23135
|
-
|
|
23136
|
-
|
|
23137
|
-
|
|
23138
|
-
|
|
23139
|
-
|
|
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
|
+
});
|
|
23140
24381
|
},
|
|
23141
24382
|
async getMemoryStats() {
|
|
23142
24383
|
let estimatedIndexBytes = 0;
|
|
@@ -23144,22 +24385,33 @@ function createNarsilFromCore(core, config) {
|
|
|
23144
24385
|
estimatedIndexBytes += executor.getManager(name)?.estimateMemoryBytes() ?? 0;
|
|
23145
24386
|
}
|
|
23146
24387
|
const workers = await orchestrator.getWorkerMemoryStats();
|
|
23147
|
-
|
|
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
|
+
};
|
|
23148
24397
|
},
|
|
23149
24398
|
async compactVectors(indexName, fieldName) {
|
|
23150
24399
|
guardShutdown();
|
|
23151
24400
|
requireIndex(indexName);
|
|
23152
|
-
|
|
24401
|
+
await withOpenIndex(indexName, async () => {
|
|
24402
|
+
compactVectors(requireManager(indexName), indexName, fieldName);
|
|
24403
|
+
});
|
|
23153
24404
|
},
|
|
23154
24405
|
async optimizeVectors(indexName, fieldName) {
|
|
23155
24406
|
guardShutdown();
|
|
23156
24407
|
requireIndex(indexName);
|
|
23157
|
-
await optimizeVectors(requireManager(indexName), indexName, fieldName);
|
|
24408
|
+
await withOpenIndex(indexName, () => optimizeVectors(requireManager(indexName), indexName, fieldName));
|
|
23158
24409
|
},
|
|
23159
24410
|
vectorMaintenanceStatus(indexName) {
|
|
23160
24411
|
guardShutdown();
|
|
23161
24412
|
requireIndex(indexName);
|
|
23162
|
-
|
|
24413
|
+
const manager = executor.getManager(indexName);
|
|
24414
|
+
return manager === void 0 ? [] : getVectorMaintenanceStatus(manager);
|
|
23163
24415
|
},
|
|
23164
24416
|
on(event, handler) {
|
|
23165
24417
|
const key = event;
|
|
@@ -23192,12 +24444,12 @@ function createNarsilFromCore(core, config) {
|
|
|
23192
24444
|
async function createNarsil(config) {
|
|
23193
24445
|
const core = createEngineCore(config);
|
|
23194
24446
|
if (core.durability) {
|
|
23195
|
-
await core.durability.manager.recover();
|
|
24447
|
+
await core.durability.manager.recover(config?.lifecycle !== void 0);
|
|
23196
24448
|
}
|
|
23197
24449
|
if (core.invalidation) {
|
|
23198
24450
|
await core.invalidation.start();
|
|
23199
24451
|
}
|
|
23200
|
-
await core.analysisRebuild.reviewStaleIndexes();
|
|
24452
|
+
if (config?.lifecycle === void 0) await core.analysisRebuild.reviewStaleIndexes();
|
|
23201
24453
|
return createNarsilFromCore(core, config);
|
|
23202
24454
|
}
|
|
23203
24455
|
|