@delali/narsil 0.2.4-next.55 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -28
- package/dist/adapters/filesystem.mjs +1 -1
- package/dist/adapters/indexeddb.mjs +1 -1
- package/dist/{capabilities-DlYw0Ov0.d.ts → capabilities-DSsOGz6F.d.ts} +2 -2
- package/dist/{chunk-BH5SYB5W.mjs → chunk-24RXHMEL.mjs} +604 -150
- package/dist/{chunk-D2S642IV.mjs → chunk-3LHGNRYE.mjs} +1 -1
- package/dist/{chunk-TVT5V4E7.mjs → chunk-B2TFROFX.mjs} +299 -168
- package/dist/{chunk-L225SHNB.mjs → chunk-L6TNPLAR.mjs} +19 -5
- package/dist/{chunk-U2AL3SON.mjs → chunk-THYRHHO2.mjs} +3 -3
- package/dist/{chunk-NTZYA3SJ.mjs → chunk-U7KYW4BA.mjs} +3 -3
- package/dist/{chunk-FRQVMFY7.mjs → chunk-USKFU7J5.mjs} +1 -0
- package/dist/{chunk-4NVPQ6S5.mjs → chunk-UXNWQZST.mjs} +12 -3
- package/dist/{chunk-RGS7FMCB.mjs → chunk-VMRR3DQU.mjs} +74 -16
- package/dist/{chunk-2GWVQWGA.mjs → chunk-YMZCMH5M.mjs} +45 -1
- package/dist/{chunk-UTOFOJOR.mjs → chunk-YNVIUGF5.mjs} +6 -2
- package/dist/{chunk-5T6AOR37.mjs → chunk-ZNZBXMP5.mjs} +601 -60
- package/dist/{chunk-XNQAIO74.mjs → chunk-ZZT3EAUC.mjs} +177 -309
- package/dist/{client-CQUqt9Ir.d.ts → client-BQK6Tf6A.d.ts} +3 -3
- package/dist/client.d.ts +8 -8
- package/dist/client.mjs +1 -0
- package/dist/{config-CNas6wJH.d.ts → config-BhldZgaK.d.ts} +2 -2
- package/dist/{config-bXxnJrQc.d.ts → config-DmXULPa-.d.ts} +15 -7
- package/dist/direct-executor-ALAVE7FC.mjs +12 -0
- package/dist/distribution/coordinator/etcd.d.ts +2 -2
- package/dist/distribution/coordinator/etcd.mjs +2 -2
- package/dist/distribution/coordinator/in-memory.d.ts +2 -2
- package/dist/distribution/coordinator/in-memory.mjs +1 -1
- package/dist/distribution/transport/grpc.mjs +1 -1
- package/dist/distribution.d.ts +8 -8
- package/dist/distribution.mjs +382 -162
- package/dist/embeddings/openai.mjs +2 -2
- package/dist/{engine-Dj4I91Jv.d.ts → engine-CIOyFozK.d.ts} +15 -12
- package/dist/index.browser.mjs +1565 -528
- package/dist/index.d.ts +7 -7
- package/dist/index.mjs +11 -11
- package/dist/invalidation/filesystem.mjs +1 -1
- package/dist/{memory-xrXOZMbw.d.ts → memory-yFaVn2xZ.d.ts} +199 -107
- package/dist/persistence/durability/checkpoint-worker.d.ts +1 -1
- package/dist/persistence/durability/checkpoint-worker.mjs +11 -11
- package/dist/react.d.ts +6 -6
- package/dist/react.mjs +1 -0
- package/dist/{schema-mLpXZBEh.d.ts → schema-d5Afu03A.d.ts} +7 -2
- package/dist/server.d.ts +10 -10
- package/dist/server.mjs +10 -10
- package/dist/{tasks-Df73Tjgq.d.ts → tasks-CH2Dod8w.d.ts} +1 -1
- package/dist/{thread-5UP6MGED.mjs → thread-DYHEQRVY.mjs} +14 -10
- package/dist/{types-BTIVO8DI.d.ts → types-DAK7mnUZ.d.ts} +1 -1
- package/dist/vector/search-worker.d.ts +1 -1
- package/dist/vector/search-worker.mjs +4 -4
- package/dist/workers/entry.mjs +4 -4
- package/package.json +9 -9
- package/dist/direct-executor-RBA5O7EN.mjs +0 -12
|
@@ -1,19 +1,110 @@
|
|
|
1
1
|
import { ReplicationMessageTypes } from './chunk-GCM3JFVA.mjs';
|
|
2
|
-
import { WIRE_BATCH_BUDGET, resolveVectorText, bindEngineCore, collectRequiredFieldFailures, embedChunkDocuments, providedDocId, admitInsert, asBatchInsertError, embedDocumentFields, assertDocumentCarriesMappedVectors } from './chunk-
|
|
3
|
-
import {
|
|
2
|
+
import { WIRE_BATCH_BUDGET, resolveVectorText, bindEngineCore, collectRequiredFieldFailures, embedChunkDocuments, providedDocId, admitInsert, asBatchInsertError, embedDocumentFields, assertDocumentCarriesMappedVectors } from './chunk-UXNWQZST.mjs';
|
|
3
|
+
import { generateId, getLanguage, splitWorkerBudget, resolveWorkerCount, createDirectExecutor, createPartitionRouter, createCompositePartition, deserializePayloadV2, createSharedFrozenSegment, createPartitionManager, buildSegmentPayload, resolvePartitionInsertOptions, resolveWorkerEntry, missingWorkerEntry, readHeapStatistics, resolveIndexAnalysis, deserializePayloadV1, decodeVectorIndexParts, nodeThreadId, resolveRequestThreadCount, createPartitionIndex, VECTOR_FILE_MAX_VECTORS, deadBytesFor, mergeFrozenSegments, isCompositePartition, workerResourceLimits, deserializeMetadata, isNodeMainThread, spawnNodeWorker, freezeSegmentShared, DEFAULT_BACKPRESSURE_LIMIT, DEFAULT_REQUEST_TIMEOUT_MS, SHUTDOWN_TIMEOUT_MS, serializeMetadata, encodeSegmentState, CONSECUTIVE_TIMEOUTS_BEFORE_DEATH, decodeVectorFilePayload, documentsOfPayloadV2, countDead, decodeVectorGraphPayload, deadBitsWhere, NO_LIVE_VECTOR_AT_POSITION, createFrozenSegment } from './chunk-ZZT3EAUC.mjs';
|
|
4
4
|
import { detectRuntime } from './chunk-AV4IJKD2.mjs';
|
|
5
|
-
import { validateWorkerConfig, validateIndexName, validateBM25Params, validatePartitionConfig, executeQuery, queryBindingOf, executePreflight, executeSuggest, executeListDocuments, validateDocId } from './chunk-
|
|
6
|
-
import { BATCH_CHUNK_SIZE, STATISTICS_BROADCAST_INTERVAL_MS, MAX_SWEEP_INTERVAL_MS, MIN_SWEEP_INTERVAL_MS, HEAP_PRESSURE_WARNING_FRACTION, FOREIGN_STATS_TTL_MS, MIN_DOCUMENTS_FOR_SEGMENTS, HEAP_PRESSURE_REARM_FRACTION } from './chunk-
|
|
7
|
-
import { collectQueryTermStats, mergePartitionStats, fnv1a, kWayMerge, mergeFacets } from './chunk-
|
|
8
|
-
import { extractVectorFieldsFromSchema, validateSchema, validateVectorPromotion, validateVectorStorage, flattenSchema, isTextFieldType, validateRequiredFields, validateDocument, validateDocumentStrict } from './chunk-
|
|
9
|
-
import { releaseLocksHeldBy } from './chunk-
|
|
5
|
+
import { validateWorkerConfig, validateIndexName, validateBM25Params, validatePartitionConfig, executeQuery, queryBindingOf, executePreflight, executeSuggest, executeListDocuments, validateDocId } from './chunk-B2TFROFX.mjs';
|
|
6
|
+
import { BATCH_CHUNK_SIZE, STATISTICS_BROADCAST_INTERVAL_MS, MAX_SWEEP_INTERVAL_MS, MIN_SWEEP_INTERVAL_MS, HEAP_PRESSURE_WARNING_FRACTION, FOREIGN_STATS_TTL_MS, MIN_DOCUMENTS_FOR_SEGMENTS, HEAP_PRESSURE_REARM_FRACTION } from './chunk-3LHGNRYE.mjs';
|
|
7
|
+
import { getNestedValue, collectQueryTermStats, mergePartitionStats, oversampledFacetConfig, fnv1a, kWayMerge, mergeFacets } from './chunk-ZNZBXMP5.mjs';
|
|
8
|
+
import { extractVectorFieldsFromSchema, validateSchema, validateVectorPromotion, validateVectorStorage, flattenSchema, isTextFieldType, validateRequiredFields, validateDocument, validateDocumentStrict } from './chunk-L6TNPLAR.mjs';
|
|
9
|
+
import { releaseLocksHeldBy } from './chunk-THYRHHO2.mjs';
|
|
10
|
+
import { requireSearchBackendSettings } from './chunk-YNVIUGF5.mjs';
|
|
10
11
|
import { crc32, nativeCrc32, crc32Final, crc32Init, crc32Update } from './chunk-3L4U6EJQ.mjs';
|
|
11
12
|
import { CHECKSUM_TIMEOUT_MS, CHECKSUM_YIELD_CHUNK_BYTES } from './chunk-IFBKLBGB.mjs';
|
|
12
|
-
import { compareCodePoints } from './chunk-
|
|
13
|
+
import { compareCodePoints } from './chunk-YMZCMH5M.mjs';
|
|
13
14
|
import { createRequestId, isValidWorkerResponse, threadSlotOfWorker } from './chunk-POWOTUKJ.mjs';
|
|
14
|
-
import { NarsilError, ErrorCodes } from './chunk-
|
|
15
|
+
import { NarsilError, ErrorCodes } from './chunk-USKFU7J5.mjs';
|
|
15
16
|
import { encode, decode } from '@msgpack/msgpack';
|
|
16
17
|
|
|
18
|
+
// src/engine/config-keys.ts
|
|
19
|
+
var NARSIL_CONFIG_KEYS = {
|
|
20
|
+
persistence: true,
|
|
21
|
+
invalidation: true,
|
|
22
|
+
plugins: true,
|
|
23
|
+
idGenerator: true,
|
|
24
|
+
workers: true,
|
|
25
|
+
embedding: true,
|
|
26
|
+
embeddingAdapters: true,
|
|
27
|
+
durability: true,
|
|
28
|
+
analysis: true,
|
|
29
|
+
lifecycle: true
|
|
30
|
+
};
|
|
31
|
+
var DURABILITY_CONFIG_KEYS = {
|
|
32
|
+
tier: true,
|
|
33
|
+
directory: true,
|
|
34
|
+
mode: true,
|
|
35
|
+
flushIntervalMs: true,
|
|
36
|
+
segmentMaxBytes: true,
|
|
37
|
+
checkpointIntervalMs: true,
|
|
38
|
+
checkpointMutationThreshold: true,
|
|
39
|
+
compactionThreshold: true
|
|
40
|
+
};
|
|
41
|
+
var WORKER_CONFIG_KEYS = {
|
|
42
|
+
enabled: true,
|
|
43
|
+
count: true,
|
|
44
|
+
promotionThreshold: true,
|
|
45
|
+
idleTimeoutMs: true,
|
|
46
|
+
mainCopyQueries: true,
|
|
47
|
+
bootstrapModule: true
|
|
48
|
+
};
|
|
49
|
+
var LIFECYCLE_CONFIG_KEYS = {
|
|
50
|
+
idleTimeoutMs: true,
|
|
51
|
+
maxOpenIndexes: true,
|
|
52
|
+
maxOpenBytes: true,
|
|
53
|
+
maxReopenWaiters: true
|
|
54
|
+
};
|
|
55
|
+
var ANALYSIS_CONFIG_KEYS = {
|
|
56
|
+
rebuild: true,
|
|
57
|
+
onStaleAnalysis: true
|
|
58
|
+
};
|
|
59
|
+
function requireKnownKeys(section, value, known) {
|
|
60
|
+
if (value === void 0 || value === null || typeof value !== "object") return;
|
|
61
|
+
for (const key of Object.keys(value)) {
|
|
62
|
+
if (Object.hasOwn(known, key)) continue;
|
|
63
|
+
throw new NarsilError(
|
|
64
|
+
ErrorCodes.CONFIG_INVALID,
|
|
65
|
+
`The configuration sets "${section}${key}", which is no setting that createNarsil accepts. The settings at that level are ${Object.keys(known).join(", ")}`,
|
|
66
|
+
{ setting: `${section}${key}`, settings: Object.keys(known) }
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function requireNamedPlugins(plugins) {
|
|
71
|
+
if (plugins === void 0) return;
|
|
72
|
+
if (!Array.isArray(plugins)) {
|
|
73
|
+
throw new NarsilError(ErrorCodes.CONFIG_INVALID, 'The configuration sets "plugins" to something other than a list');
|
|
74
|
+
}
|
|
75
|
+
plugins.forEach((plugin, position) => {
|
|
76
|
+
const name = plugin?.name;
|
|
77
|
+
if (typeof name !== "string" || name.trim().length === 0) {
|
|
78
|
+
throw new NarsilError(
|
|
79
|
+
ErrorCodes.CONFIG_INVALID,
|
|
80
|
+
`The plugin at position ${position} carries no name, and the engine names each plugin in its errors and event payloads`,
|
|
81
|
+
{ position }
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function requireKnownConfig(config) {
|
|
87
|
+
if (config === void 0) return;
|
|
88
|
+
requireKnownKeys("", config, NARSIL_CONFIG_KEYS);
|
|
89
|
+
requireKnownKeys("durability.", config.durability, DURABILITY_CONFIG_KEYS);
|
|
90
|
+
requireKnownKeys("workers.", config.workers, WORKER_CONFIG_KEYS);
|
|
91
|
+
requireKnownKeys("lifecycle.", config.lifecycle, LIFECYCLE_CONFIG_KEYS);
|
|
92
|
+
requireKnownKeys("analysis.", config.analysis, ANALYSIS_CONFIG_KEYS);
|
|
93
|
+
requireNamedPlugins(config.plugins);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/engine/orchestration/turn.ts
|
|
97
|
+
function afterCurrentTurn(callback) {
|
|
98
|
+
if (typeof setImmediate === "function") {
|
|
99
|
+
setImmediate(callback);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
setTimeout(callback, 0);
|
|
103
|
+
}
|
|
104
|
+
function yieldToEventLoop() {
|
|
105
|
+
return new Promise((resolve) => afterCurrentTurn(resolve));
|
|
106
|
+
}
|
|
107
|
+
|
|
17
108
|
// src/partitioning/constants.ts
|
|
18
109
|
var REBALANCE_CHUNK_SIZE = 1e3;
|
|
19
110
|
var DEFAULT_WRITE_AHEAD_QUEUE_MAX_SIZE = 1e4;
|
|
@@ -88,7 +179,7 @@ function createRebalancer() {
|
|
|
88
179
|
documentsProcessed,
|
|
89
180
|
documentsTotal
|
|
90
181
|
});
|
|
91
|
-
await
|
|
182
|
+
await yieldToEventLoop();
|
|
92
183
|
}
|
|
93
184
|
}
|
|
94
185
|
}
|
|
@@ -98,7 +189,7 @@ function createRebalancer() {
|
|
|
98
189
|
documentsProcessed,
|
|
99
190
|
documentsTotal
|
|
100
191
|
});
|
|
101
|
-
await
|
|
192
|
+
await yieldToEventLoop();
|
|
102
193
|
}
|
|
103
194
|
onProgress?.({
|
|
104
195
|
phase: "swapping",
|
|
@@ -125,13 +216,43 @@ function createRebalancer() {
|
|
|
125
216
|
}
|
|
126
217
|
|
|
127
218
|
// src/plugins/registry.ts
|
|
219
|
+
var WRITE_REJECTING_HOOKS = /* @__PURE__ */ new Set(["beforeInsert", "beforeUpdate", "beforeRemove"]);
|
|
128
220
|
function isThenable(value) {
|
|
129
221
|
return value !== null && typeof value === "object" && typeof value.then === "function";
|
|
130
222
|
}
|
|
131
|
-
|
|
223
|
+
function thrownText(err, plugin, hookName) {
|
|
224
|
+
if (err instanceof Error) return err.message;
|
|
225
|
+
try {
|
|
226
|
+
return String(err);
|
|
227
|
+
} catch {
|
|
228
|
+
return `The ${hookName} hook of plugin "${plugin}" rejects the write`;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function asWriteRejection(err, plugin, hookName) {
|
|
232
|
+
if (err instanceof NarsilError) return err;
|
|
233
|
+
return new NarsilError(ErrorCodes.DOC_VALIDATION_FAILED, thrownText(err, plugin, hookName), {
|
|
234
|
+
plugin,
|
|
235
|
+
hook: hookName
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function invoke(hook, hookName, context) {
|
|
239
|
+
const rejectsWrites = WRITE_REJECTING_HOOKS.has(hookName);
|
|
240
|
+
let result;
|
|
241
|
+
try {
|
|
242
|
+
result = hook.run(context);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
throw rejectsWrites ? asWriteRejection(err, hook.plugin, hookName) : err;
|
|
245
|
+
}
|
|
246
|
+
if (!isThenable(result)) return null;
|
|
247
|
+
if (!rejectsWrites) return result;
|
|
248
|
+
return Promise.resolve(result).catch((err) => {
|
|
249
|
+
throw asWriteRejection(err, hook.plugin, hookName);
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
async function continueAsync(pending, hooks, startIndex, hookName, context) {
|
|
132
253
|
await pending;
|
|
133
254
|
for (let i = startIndex; i < hooks.length; i++) {
|
|
134
|
-
await hooks[i]
|
|
255
|
+
await invoke(hooks[i], hookName, context);
|
|
135
256
|
}
|
|
136
257
|
}
|
|
137
258
|
function createPluginRegistry() {
|
|
@@ -148,14 +269,14 @@ function createPluginRegistry() {
|
|
|
148
269
|
for (const plugin of plugins) {
|
|
149
270
|
const hook = plugin[hookName];
|
|
150
271
|
if (typeof hook === "function") {
|
|
151
|
-
hooks.push(hook.bind(plugin));
|
|
272
|
+
hooks.push({ plugin: plugin.name, run: hook.bind(plugin) });
|
|
152
273
|
}
|
|
153
274
|
}
|
|
154
275
|
if (hooks.length === 0) return;
|
|
155
276
|
for (let i = 0; i < hooks.length; i++) {
|
|
156
|
-
const
|
|
157
|
-
if (
|
|
158
|
-
return continueAsync(
|
|
277
|
+
const pending = invoke(hooks[i], hookName, context);
|
|
278
|
+
if (pending !== null) {
|
|
279
|
+
return continueAsync(pending, hooks, i + 1, hookName, context);
|
|
159
280
|
}
|
|
160
281
|
}
|
|
161
282
|
}
|
|
@@ -294,7 +415,7 @@ function emitEngineEvent(eventHandlers, event, payload) {
|
|
|
294
415
|
}
|
|
295
416
|
|
|
296
417
|
// src/engine/analysis-rebuild.ts
|
|
297
|
-
function
|
|
418
|
+
function yieldToEventLoop2() {
|
|
298
419
|
return new Promise((resolve) => {
|
|
299
420
|
setTimeout(resolve, 0);
|
|
300
421
|
});
|
|
@@ -335,7 +456,7 @@ function createAnalysisRebuildCoordinator(config, deps) {
|
|
|
335
456
|
for (let partitionId = 0; partitionId < manager.partitionCount; partitionId += 1) {
|
|
336
457
|
manager.rebuildTextIndex(partitionId);
|
|
337
458
|
entry.partitionsRebuilt = partitionId + 1;
|
|
338
|
-
await
|
|
459
|
+
await yieldToEventLoop2();
|
|
339
460
|
}
|
|
340
461
|
stale.delete(indexName);
|
|
341
462
|
await deps.persistAnalysisRevision(indexName);
|
|
@@ -514,6 +635,10 @@ function resolveDurabilityTier(config) {
|
|
|
514
635
|
var DEFAULT_CHECKPOINT_INTERVAL_MS = 3e5;
|
|
515
636
|
var DEFAULT_CHECKPOINT_MUTATION_THRESHOLD = 1e5;
|
|
516
637
|
var DEFAULT_ASYNC_FLUSH_INTERVAL_MS = 1e3;
|
|
638
|
+
var DIRECTORY_IDENTITY_CHECK_INTERVAL_MS = 1e3;
|
|
639
|
+
var DIRECTORY_LOCK_ATTEMPTS = 3;
|
|
640
|
+
var DIRECTORY_LOCK_SETTLE_MS = 50;
|
|
641
|
+
var DIRECTORY_LOCK_PROCESS_START_TOLERANCE_MS = 1e3;
|
|
517
642
|
var DEFAULT_SEGMENT_MAX_BYTES = 67108864;
|
|
518
643
|
var DEFAULT_COMPACTION_THRESHOLD = 12;
|
|
519
644
|
var COMPACTION_LARGEST_MERGED_DOCUMENT_COUNT = 2e5;
|
|
@@ -542,6 +667,163 @@ async function getPath() {
|
|
|
542
667
|
return cachedPath;
|
|
543
668
|
}
|
|
544
669
|
|
|
670
|
+
// src/persistence/durability/directory-lock.ts
|
|
671
|
+
var LOCK_FILE_NAME = ".narsil.lock";
|
|
672
|
+
var HARD_LINK_UNSUPPORTED_CODES = /* @__PURE__ */ new Set(["EPERM", "ENOTSUP", "EOPNOTSUPP", "ENOSYS"]);
|
|
673
|
+
var lockedDirectories = /* @__PURE__ */ new Set();
|
|
674
|
+
function errnoOf(err) {
|
|
675
|
+
return err?.code;
|
|
676
|
+
}
|
|
677
|
+
function processIsRunning(pid) {
|
|
678
|
+
try {
|
|
679
|
+
process.kill(pid, 0);
|
|
680
|
+
return true;
|
|
681
|
+
} catch (err) {
|
|
682
|
+
return errnoOf(err) === "EPERM";
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
function integerOrNull(text) {
|
|
686
|
+
const value = Number.parseInt(text ?? "", 10);
|
|
687
|
+
return Number.isSafeInteger(value) ? value : null;
|
|
688
|
+
}
|
|
689
|
+
function parseHolder(text) {
|
|
690
|
+
const [pidText, threadText, startText, nonceText] = text.split("\n");
|
|
691
|
+
const pid = integerOrNull(pidText);
|
|
692
|
+
if (pid === null || pid <= 0) return null;
|
|
693
|
+
const nonce = nonceText?.trim() ?? "";
|
|
694
|
+
return {
|
|
695
|
+
pid,
|
|
696
|
+
threadId: integerOrNull(threadText),
|
|
697
|
+
processStartMs: integerOrNull(startText),
|
|
698
|
+
nonce: nonce.length > 0 ? nonce : null
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
function formatHolder(holder) {
|
|
702
|
+
return `${holder.pid}
|
|
703
|
+
${holder.threadId ?? ""}
|
|
704
|
+
${holder.processStartMs ?? ""}
|
|
705
|
+
${holder.nonce ?? ""}
|
|
706
|
+
`;
|
|
707
|
+
}
|
|
708
|
+
async function readHolder(fs, lockPath) {
|
|
709
|
+
try {
|
|
710
|
+
return parseHolder(await fs.readFile(lockPath, "utf8")) ?? "unreadable";
|
|
711
|
+
} catch (err) {
|
|
712
|
+
if (errnoOf(err) === "ENOENT") return "absent";
|
|
713
|
+
throw err;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
async function settledHolder(fs, lockPath) {
|
|
717
|
+
const first = await readHolder(fs, lockPath);
|
|
718
|
+
if (first !== "unreadable") return first;
|
|
719
|
+
await new Promise((resolve) => setTimeout(resolve, DIRECTORY_LOCK_SETTLE_MS));
|
|
720
|
+
return readHolder(fs, lockPath);
|
|
721
|
+
}
|
|
722
|
+
function holderIsLive(holder, self) {
|
|
723
|
+
if (holder.pid !== self.pid) return processIsRunning(holder.pid);
|
|
724
|
+
if (holder.processStartMs === null || self.processStartMs === null) return false;
|
|
725
|
+
return Math.abs(holder.processStartMs - self.processStartMs) <= DIRECTORY_LOCK_PROCESS_START_TOLERANCE_MS;
|
|
726
|
+
}
|
|
727
|
+
function heldElsewhere(root, holder, lockPath) {
|
|
728
|
+
return new NarsilError(
|
|
729
|
+
ErrorCodes.CONFIG_INVALID,
|
|
730
|
+
`The durability directory "${root}" is in use by ${holder}, and only one engine may write a write-ahead log directory. Point this engine at another directory, or shut the other engine down first. Where no engine uses the directory, delete "${lockPath}"`,
|
|
731
|
+
{ directory: root }
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
function holderLabel(holder, self) {
|
|
735
|
+
if (holder.pid !== self.pid) return `process ${holder.pid}`;
|
|
736
|
+
return holder.threadId === self.threadId ? "another copy of Narsil in this thread" : "another thread of this process";
|
|
737
|
+
}
|
|
738
|
+
async function createLockFileExclusively(fs, lockPath, content) {
|
|
739
|
+
try {
|
|
740
|
+
await fs.writeFile(lockPath, content, { flag: "wx" });
|
|
741
|
+
return true;
|
|
742
|
+
} catch (err) {
|
|
743
|
+
if (errnoOf(err) === "EEXIST") return false;
|
|
744
|
+
throw err;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
async function createLockFile(fs, lockPath, draftPath, content) {
|
|
748
|
+
await fs.writeFile(draftPath, content, { flag: "w" });
|
|
749
|
+
try {
|
|
750
|
+
await fs.link(draftPath, lockPath);
|
|
751
|
+
return true;
|
|
752
|
+
} catch (err) {
|
|
753
|
+
const code = errnoOf(err);
|
|
754
|
+
if (code === "EEXIST") return false;
|
|
755
|
+
if (code !== void 0 && HARD_LINK_UNSUPPORTED_CODES.has(code)) {
|
|
756
|
+
return createLockFileExclusively(fs, lockPath, content);
|
|
757
|
+
}
|
|
758
|
+
throw err;
|
|
759
|
+
} finally {
|
|
760
|
+
await fs.unlink(draftPath).catch(() => void 0);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
async function acquire(fs, root, lockPath, self) {
|
|
764
|
+
const draftPath = `${lockPath}.${self.pid}.${self.threadId ?? 0}`;
|
|
765
|
+
const content = formatHolder(self);
|
|
766
|
+
for (let attempt = 0; attempt < DIRECTORY_LOCK_ATTEMPTS; attempt++) {
|
|
767
|
+
if (await createLockFile(fs, lockPath, draftPath, content)) return;
|
|
768
|
+
const holder = await settledHolder(fs, lockPath);
|
|
769
|
+
if (typeof holder === "object" && holderIsLive(holder, self)) {
|
|
770
|
+
throw heldElsewhere(root, holderLabel(holder, self), lockPath);
|
|
771
|
+
}
|
|
772
|
+
await fs.unlink(lockPath).catch((err) => {
|
|
773
|
+
if (errnoOf(err) !== "ENOENT") throw err;
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
throw heldElsewhere(root, "another process", lockPath);
|
|
777
|
+
}
|
|
778
|
+
async function describeSelf() {
|
|
779
|
+
return {
|
|
780
|
+
pid: process.pid,
|
|
781
|
+
threadId: await nodeThreadId(),
|
|
782
|
+
processStartMs: Math.round(Date.now() - process.uptime() * 1e3),
|
|
783
|
+
nonce: globalThis.crypto.randomUUID()
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
async function lockDirectory(root) {
|
|
787
|
+
const fs = await getFs();
|
|
788
|
+
const pathMod = await getPath();
|
|
789
|
+
const resolvedRoot = pathMod.resolve(root);
|
|
790
|
+
const lockPath = pathMod.join(resolvedRoot, LOCK_FILE_NAME);
|
|
791
|
+
const self = await describeSelf();
|
|
792
|
+
let directoryKey = null;
|
|
793
|
+
try {
|
|
794
|
+
await fs.mkdir(resolvedRoot, { recursive: true });
|
|
795
|
+
const stat = await fs.stat(resolvedRoot, { bigint: true });
|
|
796
|
+
const key = `${stat.dev}:${stat.ino}`;
|
|
797
|
+
if (lockedDirectories.has(key)) throw heldElsewhere(root, "another engine in this process", lockPath);
|
|
798
|
+
lockedDirectories.add(key);
|
|
799
|
+
directoryKey = key;
|
|
800
|
+
await acquire(fs, root, lockPath, self);
|
|
801
|
+
} catch (err) {
|
|
802
|
+
if (directoryKey !== null) lockedDirectories.delete(directoryKey);
|
|
803
|
+
if (err instanceof NarsilError) throw err;
|
|
804
|
+
throw new NarsilError(
|
|
805
|
+
ErrorCodes.PERSISTENCE_SAVE_FAILED,
|
|
806
|
+
`The engine cannot lock the durability directory "${root}"`,
|
|
807
|
+
{ directory: root, cause: err instanceof Error ? err.message : String(err) }
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
const heldKey = directoryKey;
|
|
811
|
+
let released = false;
|
|
812
|
+
async function holds() {
|
|
813
|
+
const holder = await readHolder(fs, lockPath);
|
|
814
|
+
return typeof holder === "object" && holder.nonce === self.nonce;
|
|
815
|
+
}
|
|
816
|
+
return {
|
|
817
|
+
holds,
|
|
818
|
+
async release() {
|
|
819
|
+
if (released) return;
|
|
820
|
+
released = true;
|
|
821
|
+
lockedDirectories.delete(heldKey);
|
|
822
|
+
if (await holds().catch(() => false)) await fs.unlink(lockPath).catch(() => void 0);
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
545
827
|
// src/persistence/durability/durable-filesystem.ts
|
|
546
828
|
function validateRelativeKey(key) {
|
|
547
829
|
if (key.length === 0) {
|
|
@@ -627,6 +909,20 @@ function createDurableDirectory(root) {
|
|
|
627
909
|
get root() {
|
|
628
910
|
return root;
|
|
629
911
|
},
|
|
912
|
+
lock() {
|
|
913
|
+
return lockDirectory(root);
|
|
914
|
+
},
|
|
915
|
+
async identity() {
|
|
916
|
+
const fs = await getFs();
|
|
917
|
+
const pathMod = await getPath();
|
|
918
|
+
try {
|
|
919
|
+
const stat = await fs.stat(pathMod.resolve(root), { bigint: true });
|
|
920
|
+
return `${stat.dev}:${stat.ino}`;
|
|
921
|
+
} catch (err) {
|
|
922
|
+
if (err.code === "ENOENT") return null;
|
|
923
|
+
throw err;
|
|
924
|
+
}
|
|
925
|
+
},
|
|
630
926
|
async appendHandle(key) {
|
|
631
927
|
const { path: filePath, pathMod } = await resolve(key);
|
|
632
928
|
const fs = await getFs();
|
|
@@ -817,22 +1113,20 @@ async function isOnMainThread() {
|
|
|
817
1113
|
}
|
|
818
1114
|
return onMainThread;
|
|
819
1115
|
}
|
|
820
|
-
function resolveWorkerEntryPoint() {
|
|
821
|
-
const base = import.meta.url;
|
|
822
|
-
const distIndex = base.lastIndexOf("/dist/");
|
|
823
|
-
if (distIndex !== -1) {
|
|
824
|
-
return new URL("serialization/crc32-worker.mjs", base.slice(0, distIndex + 6)).href;
|
|
825
|
-
}
|
|
826
|
-
return base.replace(/\/src\/serialization\/[^/]+$/, "/dist/serialization/crc32-worker.mjs");
|
|
827
|
-
}
|
|
828
1116
|
async function spawnWorker() {
|
|
1117
|
+
const entryPoint = resolveWorkerEntry(
|
|
1118
|
+
import.meta.url,
|
|
1119
|
+
/\/src\/serialization\/[^/]+$/,
|
|
1120
|
+
"serialization/crc32-worker.mjs"
|
|
1121
|
+
);
|
|
1122
|
+
if (entryPoint === null) return null;
|
|
829
1123
|
try {
|
|
830
|
-
return await spawnNodeWorker(new URL(
|
|
1124
|
+
return await spawnNodeWorker(new URL(entryPoint));
|
|
831
1125
|
} catch {
|
|
832
1126
|
return null;
|
|
833
1127
|
}
|
|
834
1128
|
}
|
|
835
|
-
function
|
|
1129
|
+
function yieldToEventLoop3() {
|
|
836
1130
|
if (typeof setImmediate === "function") {
|
|
837
1131
|
return new Promise((resolve) => setImmediate(resolve));
|
|
838
1132
|
}
|
|
@@ -844,7 +1138,7 @@ async function advanceChecksum(start, payload) {
|
|
|
844
1138
|
const end = Math.min(offset + CHECKSUM_YIELD_CHUNK_BYTES, payload.length);
|
|
845
1139
|
state = crc32Update(state, payload.subarray(offset, end));
|
|
846
1140
|
if (end < payload.length) {
|
|
847
|
-
await
|
|
1141
|
+
await yieldToEventLoop3();
|
|
848
1142
|
}
|
|
849
1143
|
}
|
|
850
1144
|
return state;
|
|
@@ -2132,7 +2426,7 @@ function attachDocuments(manager, partitionId, segment) {
|
|
|
2132
2426
|
}
|
|
2133
2427
|
manager.attachFrozenSegment(partitionId, createSharedFrozenSegment(snapshot));
|
|
2134
2428
|
}
|
|
2135
|
-
function
|
|
2429
|
+
function yieldToEventLoop4() {
|
|
2136
2430
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
2137
2431
|
}
|
|
2138
2432
|
async function loadPartitionSegmentBySegment(directory, partition, manager) {
|
|
@@ -2142,7 +2436,7 @@ async function loadPartitionSegmentBySegment(directory, partition, manager) {
|
|
|
2142
2436
|
addSegmentSchema(agreedSchema, segment.schema);
|
|
2143
2437
|
removeWhatTheSegmentReplaces(manager, segment);
|
|
2144
2438
|
attachDocuments(manager, partition.partitionId, segment);
|
|
2145
|
-
await
|
|
2439
|
+
await yieldToEventLoop4();
|
|
2146
2440
|
}
|
|
2147
2441
|
}
|
|
2148
2442
|
|
|
@@ -3807,17 +4101,15 @@ var workerBusy = false;
|
|
|
3807
4101
|
var spawnedWorkerCount = 0;
|
|
3808
4102
|
var workerIdleMs = CHECKPOINT_WORKER_IDLE_MS;
|
|
3809
4103
|
var idleTimer = null;
|
|
3810
|
-
function resolveWorkerEntryPoint2() {
|
|
3811
|
-
const base = import.meta.url;
|
|
3812
|
-
const distIndex = base.lastIndexOf("/dist/");
|
|
3813
|
-
if (distIndex !== -1) {
|
|
3814
|
-
return new URL("persistence/durability/checkpoint-worker.mjs", base.slice(0, distIndex + 6)).href;
|
|
3815
|
-
}
|
|
3816
|
-
return base.replace(/\/src\/persistence\/durability\/[^/]+$/, "/dist/persistence/durability/checkpoint-worker.mjs");
|
|
3817
|
-
}
|
|
3818
4104
|
async function spawnWorker2() {
|
|
4105
|
+
const entryPoint = resolveWorkerEntry(
|
|
4106
|
+
import.meta.url,
|
|
4107
|
+
/\/src\/persistence\/durability\/[^/]+$/,
|
|
4108
|
+
"persistence/durability/checkpoint-worker.mjs"
|
|
4109
|
+
);
|
|
4110
|
+
if (entryPoint === null) return null;
|
|
3819
4111
|
try {
|
|
3820
|
-
const worker = await spawnNodeWorker(new URL(
|
|
4112
|
+
const worker = await spawnNodeWorker(new URL(entryPoint));
|
|
3821
4113
|
if (worker === null) {
|
|
3822
4114
|
return null;
|
|
3823
4115
|
}
|
|
@@ -4048,11 +4340,29 @@ async function garbageThatNoFieldStillReads(directory, vectorIndexes, keys) {
|
|
|
4048
4340
|
return unread;
|
|
4049
4341
|
}
|
|
4050
4342
|
async function runDurableCheckpoint(input) {
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
recordsLeftInLog
|
|
4343
|
+
try {
|
|
4344
|
+
let recordsLeftInLog = await writeOneBoundedCheckpoint(input);
|
|
4345
|
+
while (recordsLeftInLog > 0 && input.mayContinue()) {
|
|
4346
|
+
recordsLeftInLog = await writeOneBoundedCheckpoint(input);
|
|
4347
|
+
}
|
|
4348
|
+
} catch (err) {
|
|
4349
|
+
if (!isSystemError(err)) throw err;
|
|
4350
|
+
const error = new NarsilError(
|
|
4351
|
+
ErrorCodes.PERSISTENCE_SAVE_FAILED,
|
|
4352
|
+
`The engine cannot write the checkpoint of index "${input.indexName}" to the durability directory "${input.directory.root}"`,
|
|
4353
|
+
{
|
|
4354
|
+
indexName: input.indexName,
|
|
4355
|
+
directory: input.directory.root,
|
|
4356
|
+
cause: err.message
|
|
4357
|
+
}
|
|
4358
|
+
);
|
|
4359
|
+
input.markFatal(error);
|
|
4360
|
+
throw error;
|
|
4054
4361
|
}
|
|
4055
4362
|
}
|
|
4363
|
+
function isSystemError(err) {
|
|
4364
|
+
return err instanceof Error && !(err instanceof NarsilError) && typeof err.code === "string";
|
|
4365
|
+
}
|
|
4056
4366
|
async function writeOneBoundedCheckpoint(input) {
|
|
4057
4367
|
const manager = input.hooks.getManager(input.indexName);
|
|
4058
4368
|
if (manager === void 0) {
|
|
@@ -4132,6 +4442,94 @@ function recordsBeyond(indexState, targets) {
|
|
|
4132
4442
|
return records;
|
|
4133
4443
|
}
|
|
4134
4444
|
|
|
4445
|
+
// src/persistence/durability/directory-watch.ts
|
|
4446
|
+
function unrefInterval(tick, intervalMs) {
|
|
4447
|
+
const timer = setInterval(tick, intervalMs);
|
|
4448
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
4449
|
+
return timer;
|
|
4450
|
+
}
|
|
4451
|
+
function createDirectoryWatch(directory, isStopped, onLost) {
|
|
4452
|
+
let expected = null;
|
|
4453
|
+
let timer = null;
|
|
4454
|
+
let lock = null;
|
|
4455
|
+
function cannotCheck(err) {
|
|
4456
|
+
return new NarsilError(
|
|
4457
|
+
ErrorCodes.PERSISTENCE_SAVE_FAILED,
|
|
4458
|
+
`The engine cannot check the durability directory "${directory.root}", so it cannot confirm that a write is durable`,
|
|
4459
|
+
{ directory: directory.root, cause: err instanceof Error ? err.message : String(err) }
|
|
4460
|
+
);
|
|
4461
|
+
}
|
|
4462
|
+
async function currentIdentity() {
|
|
4463
|
+
try {
|
|
4464
|
+
return await directory.identity?.() ?? null;
|
|
4465
|
+
} catch (err) {
|
|
4466
|
+
return cannotCheck(err);
|
|
4467
|
+
}
|
|
4468
|
+
}
|
|
4469
|
+
async function lockTakenOver() {
|
|
4470
|
+
if (lock === null) return null;
|
|
4471
|
+
try {
|
|
4472
|
+
if (await lock.holds()) return null;
|
|
4473
|
+
} catch (err) {
|
|
4474
|
+
return cannotCheck(err);
|
|
4475
|
+
}
|
|
4476
|
+
return new NarsilError(
|
|
4477
|
+
ErrorCodes.PERSISTENCE_SAVE_FAILED,
|
|
4478
|
+
`The lock file of the durability directory "${directory.root}" records another engine, which may be writing the same log`,
|
|
4479
|
+
{ directory: directory.root }
|
|
4480
|
+
);
|
|
4481
|
+
}
|
|
4482
|
+
async function verify() {
|
|
4483
|
+
if (directory.identity === void 0 || isStopped()) return true;
|
|
4484
|
+
const current = await currentIdentity();
|
|
4485
|
+
if (current instanceof Error) {
|
|
4486
|
+
onLost(current);
|
|
4487
|
+
return false;
|
|
4488
|
+
}
|
|
4489
|
+
if (expected === null) {
|
|
4490
|
+
expected = current;
|
|
4491
|
+
} else if (current !== expected) {
|
|
4492
|
+
onLost(
|
|
4493
|
+
new NarsilError(
|
|
4494
|
+
ErrorCodes.PERSISTENCE_SAVE_FAILED,
|
|
4495
|
+
`The durability directory "${directory.root}" is missing or is a different directory from the one that this instance opens at start-up, so a write to it is not durable`,
|
|
4496
|
+
{ directory: directory.root }
|
|
4497
|
+
)
|
|
4498
|
+
);
|
|
4499
|
+
return false;
|
|
4500
|
+
}
|
|
4501
|
+
const takenOver = await lockTakenOver();
|
|
4502
|
+
if (takenOver === null) return true;
|
|
4503
|
+
onLost(takenOver);
|
|
4504
|
+
return false;
|
|
4505
|
+
}
|
|
4506
|
+
function stop() {
|
|
4507
|
+
if (timer !== null) {
|
|
4508
|
+
clearInterval(timer);
|
|
4509
|
+
timer = null;
|
|
4510
|
+
}
|
|
4511
|
+
}
|
|
4512
|
+
return {
|
|
4513
|
+
async claim() {
|
|
4514
|
+
if (lock === null && directory.lock !== void 0) lock = await directory.lock();
|
|
4515
|
+
},
|
|
4516
|
+
async release() {
|
|
4517
|
+
const held = lock;
|
|
4518
|
+
lock = null;
|
|
4519
|
+
await held?.release();
|
|
4520
|
+
},
|
|
4521
|
+
verify,
|
|
4522
|
+
async verifyOnce() {
|
|
4523
|
+
if (expected === null && directory.identity !== void 0) await verify();
|
|
4524
|
+
},
|
|
4525
|
+
start() {
|
|
4526
|
+
if (directory.identity === void 0 || timer !== null || isStopped()) return;
|
|
4527
|
+
timer = unrefInterval(() => void verify(), DIRECTORY_IDENTITY_CHECK_INTERVAL_MS);
|
|
4528
|
+
},
|
|
4529
|
+
stop
|
|
4530
|
+
};
|
|
4531
|
+
}
|
|
4532
|
+
|
|
4135
4533
|
// src/persistence/durability/manager-state.ts
|
|
4136
4534
|
async function drainIndexStateForUnload(indexState, pendingMetadataWrite, closeWalWriter) {
|
|
4137
4535
|
indexState.unloading = true;
|
|
@@ -4604,11 +5002,13 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
4604
5002
|
let asyncFlushTimer = null;
|
|
4605
5003
|
let shuttingDown = false;
|
|
4606
5004
|
let fatalError = null;
|
|
5005
|
+
const directoryWatch = createDirectoryWatch(directory, () => shuttingDown || fatalError !== null, markFatal);
|
|
4607
5006
|
function markFatal(error) {
|
|
4608
5007
|
if (fatalError !== null) {
|
|
4609
5008
|
return;
|
|
4610
5009
|
}
|
|
4611
5010
|
fatalError = error;
|
|
5011
|
+
directoryWatch.stop();
|
|
4612
5012
|
if (checkpointTimer !== null) {
|
|
4613
5013
|
clearInterval(checkpointTimer);
|
|
4614
5014
|
checkpointTimer = null;
|
|
@@ -4672,23 +5072,13 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
4672
5072
|
if (checkpointTimer !== null || shuttingDown || fatalError !== null || checkpointIntervalMs <= 0) {
|
|
4673
5073
|
return;
|
|
4674
5074
|
}
|
|
4675
|
-
checkpointTimer =
|
|
4676
|
-
void runScheduledCheckpoints();
|
|
4677
|
-
}, checkpointIntervalMs);
|
|
4678
|
-
if (typeof checkpointTimer.unref === "function") {
|
|
4679
|
-
checkpointTimer.unref();
|
|
4680
|
-
}
|
|
5075
|
+
checkpointTimer = unrefInterval(() => void runScheduledCheckpoints(), checkpointIntervalMs);
|
|
4681
5076
|
}
|
|
4682
5077
|
function startAsyncFlushTimer() {
|
|
4683
5078
|
if (mode !== "async" || asyncFlushTimer !== null || shuttingDown || fatalError !== null || flushIntervalMs <= 0) {
|
|
4684
5079
|
return;
|
|
4685
5080
|
}
|
|
4686
|
-
asyncFlushTimer =
|
|
4687
|
-
void flushAllPartitions();
|
|
4688
|
-
}, flushIntervalMs);
|
|
4689
|
-
if (typeof asyncFlushTimer.unref === "function") {
|
|
4690
|
-
asyncFlushTimer.unref();
|
|
4691
|
-
}
|
|
5081
|
+
asyncFlushTimer = unrefInterval(() => void flushAllPartitions(), flushIntervalMs);
|
|
4692
5082
|
}
|
|
4693
5083
|
async function flushAllPartitions() {
|
|
4694
5084
|
if (fatalError !== null) {
|
|
@@ -4750,6 +5140,8 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
4750
5140
|
return run;
|
|
4751
5141
|
}
|
|
4752
5142
|
async function performCheckpoint(indexName, indexState, fromMemory = false) {
|
|
5143
|
+
await directoryWatch.verify();
|
|
5144
|
+
if (fatalError !== null) throw fatalError;
|
|
4753
5145
|
await runDurableCheckpoint({
|
|
4754
5146
|
directory,
|
|
4755
5147
|
hooks,
|
|
@@ -4799,10 +5191,14 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
4799
5191
|
}
|
|
4800
5192
|
await stallWhileCheckpointsFallBehind(indexState);
|
|
4801
5193
|
}
|
|
4802
|
-
if (recordedByIndex.size
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
}
|
|
5194
|
+
if (recordedByIndex.size === 0) return outcomes;
|
|
5195
|
+
if (mode === "sync") await directoryWatch.verify();
|
|
5196
|
+
const lost = mode === "sync" ? fatalError : null;
|
|
5197
|
+
if (lost !== null) return outcomes.map((outcome) => outcome.ok ? { ok: false, error: lost } : outcome);
|
|
5198
|
+
await directoryWatch.verifyOnce();
|
|
5199
|
+
directoryWatch.start();
|
|
5200
|
+
startCheckpointTimer();
|
|
5201
|
+
startAsyncFlushTimer();
|
|
4806
5202
|
return outcomes;
|
|
4807
5203
|
}
|
|
4808
5204
|
function recoverIndex(indexName, metadataOnly = false) {
|
|
@@ -4824,10 +5220,13 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
4824
5220
|
return true;
|
|
4825
5221
|
},
|
|
4826
5222
|
async recover(metadataOnly = false) {
|
|
5223
|
+
await directoryWatch.claim();
|
|
4827
5224
|
const names = await listPersistedIndexes(directory);
|
|
4828
5225
|
for (const indexName of names) {
|
|
4829
5226
|
await recoverIndex(indexName, metadataOnly);
|
|
4830
5227
|
}
|
|
5228
|
+
await directoryWatch.verify();
|
|
5229
|
+
directoryWatch.start();
|
|
4831
5230
|
startCheckpointTimer();
|
|
4832
5231
|
},
|
|
4833
5232
|
recoverIndex(indexName) {
|
|
@@ -4889,6 +5288,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
4889
5288
|
},
|
|
4890
5289
|
async shutdown() {
|
|
4891
5290
|
shuttingDown = true;
|
|
5291
|
+
directoryWatch.stop();
|
|
4892
5292
|
if (checkpointTimer !== null) {
|
|
4893
5293
|
clearInterval(checkpointTimer);
|
|
4894
5294
|
checkpointTimer = null;
|
|
@@ -4907,6 +5307,7 @@ function createDurabilityManager(config, hooks, directoryOverride) {
|
|
|
4907
5307
|
}
|
|
4908
5308
|
indexes.clear();
|
|
4909
5309
|
terminateCheckpointWorker();
|
|
5310
|
+
await directoryWatch.release();
|
|
4910
5311
|
}
|
|
4911
5312
|
};
|
|
4912
5313
|
}
|
|
@@ -6298,18 +6699,6 @@ function eligibleIndexNames(state) {
|
|
|
6298
6699
|
return names;
|
|
6299
6700
|
}
|
|
6300
6701
|
|
|
6301
|
-
// src/engine/orchestration/turn.ts
|
|
6302
|
-
function afterCurrentTurn(callback) {
|
|
6303
|
-
if (typeof setImmediate === "function") {
|
|
6304
|
-
setImmediate(callback);
|
|
6305
|
-
return;
|
|
6306
|
-
}
|
|
6307
|
-
setTimeout(callback, 0);
|
|
6308
|
-
}
|
|
6309
|
-
function yieldToEventLoop4() {
|
|
6310
|
-
return new Promise((resolve) => afterCurrentTurn(resolve));
|
|
6311
|
-
}
|
|
6312
|
-
|
|
6313
6702
|
// src/engine/orchestration/replication.ts
|
|
6314
6703
|
async function dispatchToWorkers(state, action) {
|
|
6315
6704
|
const pool = state.workerPool;
|
|
@@ -6467,7 +6856,7 @@ async function replicateToWorkers(state, action) {
|
|
|
6467
6856
|
const loading = state.copyLoadBuffers.get(action.indexName);
|
|
6468
6857
|
if (loading !== void 0) {
|
|
6469
6858
|
queueForCopies(state, detached);
|
|
6470
|
-
if (loading.length % LOAD_BUFFER_YIELD_INTERVAL === 0) await
|
|
6859
|
+
if (loading.length % LOAD_BUFFER_YIELD_INTERVAL === 0) await yieldToEventLoop();
|
|
6471
6860
|
return;
|
|
6472
6861
|
}
|
|
6473
6862
|
const queue = state.replicationQueues.get(action.indexName);
|
|
@@ -6909,17 +7298,25 @@ function createWorkerExecutor(worker, config) {
|
|
|
6909
7298
|
}
|
|
6910
7299
|
|
|
6911
7300
|
// src/workers/factory.ts
|
|
6912
|
-
function
|
|
6913
|
-
|
|
6914
|
-
const
|
|
6915
|
-
|
|
6916
|
-
|
|
7301
|
+
async function entryFileExists(entry) {
|
|
7302
|
+
if (!entry.startsWith("file:")) return true;
|
|
7303
|
+
const [{ access }, { fileURLToPath }] = await Promise.all([import('fs/promises'), import('url')]);
|
|
7304
|
+
try {
|
|
7305
|
+
await access(fileURLToPath(entry));
|
|
7306
|
+
return true;
|
|
7307
|
+
} catch {
|
|
7308
|
+
return false;
|
|
6917
7309
|
}
|
|
6918
|
-
|
|
7310
|
+
}
|
|
7311
|
+
async function requireWorkerEntry() {
|
|
7312
|
+
const entry = resolveWorkerEntry(import.meta.url, /\/src\/workers\/[^/]+$/, "workers/entry.mjs");
|
|
7313
|
+
if (entry === null) throw missingWorkerEntry(import.meta.url);
|
|
7314
|
+
if (import.meta.url.includes("/dist/") && !await entryFileExists(entry)) throw missingWorkerEntry(import.meta.url);
|
|
7315
|
+
return entry;
|
|
6919
7316
|
}
|
|
6920
7317
|
async function createWorkerFactory(entryPoint) {
|
|
6921
7318
|
const runtime = detectRuntime();
|
|
6922
|
-
const resolvedEntry =
|
|
7319
|
+
const resolvedEntry = await requireWorkerEntry();
|
|
6923
7320
|
if (runtime.supportsWorkerThreads) {
|
|
6924
7321
|
const workerThreadsModule = await import('worker_threads');
|
|
6925
7322
|
return function nodeFactory(workerId, onDeath, onGone) {
|
|
@@ -7119,10 +7516,13 @@ function createWorkerPool(config) {
|
|
|
7119
7516
|
}
|
|
7120
7517
|
isShutdown = true;
|
|
7121
7518
|
const shutdownPromises = [...threadsStillRunning].map((executor) => {
|
|
7519
|
+
let timeoutId;
|
|
7122
7520
|
const timeoutPromise = new Promise((resolve) => {
|
|
7123
|
-
setTimeout(resolve,
|
|
7521
|
+
timeoutId = setTimeout(resolve, SHUTDOWN_TIMEOUT_MS);
|
|
7124
7522
|
});
|
|
7125
|
-
return Promise.race([shutDownUntilTheThreadIsGone(executor), timeoutPromise])
|
|
7523
|
+
return Promise.race([shutDownUntilTheThreadIsGone(executor), timeoutPromise]).finally(
|
|
7524
|
+
() => clearTimeout(timeoutId)
|
|
7525
|
+
);
|
|
7126
7526
|
});
|
|
7127
7527
|
await Promise.allSettled(shutdownPromises);
|
|
7128
7528
|
indexAssignment.clear();
|
|
@@ -7553,8 +7953,12 @@ async function ensurePool(state) {
|
|
|
7553
7953
|
function documentCountOf(state, indexName) {
|
|
7554
7954
|
return state.executor.getManager(indexName)?.countDocuments() ?? 0;
|
|
7555
7955
|
}
|
|
7556
|
-
function copyThresholdReason(state, indexName) {
|
|
7557
|
-
|
|
7956
|
+
function copyThresholdReason(state, indexName, incomingCount = 0) {
|
|
7957
|
+
const held = documentCountOf(state, indexName);
|
|
7958
|
+
if (incomingCount <= 0) {
|
|
7959
|
+
return `Index "${indexName}" holds ${held} documents, reaching the copy threshold of ${state.copyThreshold}`;
|
|
7960
|
+
}
|
|
7961
|
+
return `Index "${indexName}" holds ${held} documents, and the batch that the engine is writing adds ${incomingCount}, for ${held + incomingCount} against the copy threshold of ${state.copyThreshold}`;
|
|
7558
7962
|
}
|
|
7559
7963
|
function indexReadyForCopies(state, indexName, incomingCount = 0) {
|
|
7560
7964
|
if (!copiesAllowed(state)) return false;
|
|
@@ -7643,7 +8047,7 @@ async function scaleOutBeforeBatch(state, indexName, incomingCount) {
|
|
|
7643
8047
|
return;
|
|
7644
8048
|
}
|
|
7645
8049
|
if (!indexReadyForCopies(state, indexName, incomingCount)) return;
|
|
7646
|
-
await scaleOutIndex(state, indexName, copyThresholdReason(state, indexName));
|
|
8050
|
+
await scaleOutIndex(state, indexName, copyThresholdReason(state, indexName, incomingCount));
|
|
7647
8051
|
}
|
|
7648
8052
|
|
|
7649
8053
|
// src/engine/orchestration/idle.ts
|
|
@@ -7723,7 +8127,7 @@ function partitionsPerLease(scope, leaseCount) {
|
|
|
7723
8127
|
}
|
|
7724
8128
|
return assignments;
|
|
7725
8129
|
}
|
|
7726
|
-
function mergeWorkerResults(results) {
|
|
8130
|
+
function mergeWorkerResults(results, facetConfig) {
|
|
7727
8131
|
const merged = kWayMerge(results.map((result) => result.scored));
|
|
7728
8132
|
let totalMatched = 0;
|
|
7729
8133
|
const workerFacets = [];
|
|
@@ -7734,7 +8138,7 @@ function mergeWorkerResults(results) {
|
|
|
7734
8138
|
return {
|
|
7735
8139
|
scored: merged,
|
|
7736
8140
|
totalMatched,
|
|
7737
|
-
facets: workerFacets.length > 0 ? mergeFacets(workerFacets) : void 0
|
|
8141
|
+
facets: facetConfig !== void 0 && workerFacets.length > 0 ? mergeFacets(workerFacets, facetConfig) : void 0
|
|
7738
8142
|
};
|
|
7739
8143
|
}
|
|
7740
8144
|
function scoresPerPartition(state, indexName, params, globalStats) {
|
|
@@ -7754,12 +8158,13 @@ function queryAction(indexName, params, globalStats, partitionIds) {
|
|
|
7754
8158
|
}
|
|
7755
8159
|
async function runSplit(leases, scope, indexName, params, globalStats) {
|
|
7756
8160
|
const assignments = partitionsPerLease(scope, leases.length);
|
|
8161
|
+
const workerParams = params.facets !== void 0 ? { ...params, facets: oversampledFacetConfig(params.facets) } : params;
|
|
7757
8162
|
const results = await Promise.all(
|
|
7758
8163
|
assignments.map(
|
|
7759
|
-
(partitionIds, at) => leases[at].executor.execute(queryAction(indexName,
|
|
8164
|
+
(partitionIds, at) => leases[at].executor.execute(queryAction(indexName, workerParams, globalStats, partitionIds)).finally(() => leases[at].release())
|
|
7760
8165
|
)
|
|
7761
8166
|
);
|
|
7762
|
-
return mergeWorkerResults(results);
|
|
8167
|
+
return mergeWorkerResults(results, params.facets);
|
|
7763
8168
|
}
|
|
7764
8169
|
function takeMainCopyTurn(state) {
|
|
7765
8170
|
if (state.mainCopyQueries === "none" || state.mainCopyTurnTaken) return false;
|
|
@@ -8119,7 +8524,7 @@ function createEngineCore(config, hooks) {
|
|
|
8119
8524
|
const rebalanceTargets = /* @__PURE__ */ new Map();
|
|
8120
8525
|
function guardShutdown() {
|
|
8121
8526
|
if (shutdownState.isShutdown) {
|
|
8122
|
-
throw new NarsilError(ErrorCodes.
|
|
8527
|
+
throw new NarsilError(ErrorCodes.INSTANCE_SHUT_DOWN, "This Narsil instance has been shut down");
|
|
8123
8528
|
}
|
|
8124
8529
|
}
|
|
8125
8530
|
function requireIndex(indexName) {
|
|
@@ -8286,6 +8691,82 @@ function createEngineCore(config, hooks) {
|
|
|
8286
8691
|
};
|
|
8287
8692
|
}
|
|
8288
8693
|
|
|
8694
|
+
// src/engine/lifecycle.ts
|
|
8695
|
+
async function startEngineCore(core, config) {
|
|
8696
|
+
try {
|
|
8697
|
+
if (core.durability) {
|
|
8698
|
+
await core.durability.manager.recover(config?.lifecycle !== void 0);
|
|
8699
|
+
checkHeapAfterRecovery(core);
|
|
8700
|
+
}
|
|
8701
|
+
if (core.invalidation) await core.invalidation.start();
|
|
8702
|
+
if (config?.lifecycle === void 0) await core.analysisRebuild.reviewStaleIndexes();
|
|
8703
|
+
} catch (err) {
|
|
8704
|
+
await core.durability?.manager.shutdown().catch(() => void 0);
|
|
8705
|
+
await shutdownEngine(core).catch(() => void 0);
|
|
8706
|
+
throw err;
|
|
8707
|
+
}
|
|
8708
|
+
}
|
|
8709
|
+
async function shutdownEngine(core) {
|
|
8710
|
+
const { executor, durability, indexRegistry, eventHandlers, orchestrator } = core;
|
|
8711
|
+
core.indexState.dispose();
|
|
8712
|
+
for (const [name] of indexRegistry) {
|
|
8713
|
+
const manager = executor.getManager(name);
|
|
8714
|
+
if (manager) {
|
|
8715
|
+
for (const [, vectorIndex] of manager.getVectorIndexes()) {
|
|
8716
|
+
vectorIndex.dispose();
|
|
8717
|
+
}
|
|
8718
|
+
}
|
|
8719
|
+
}
|
|
8720
|
+
if (durability) {
|
|
8721
|
+
await durability.manager.shutdown();
|
|
8722
|
+
}
|
|
8723
|
+
if (core.invalidation) {
|
|
8724
|
+
await core.invalidation.shutdown();
|
|
8725
|
+
}
|
|
8726
|
+
const adaptersToShutdown = /* @__PURE__ */ new Set();
|
|
8727
|
+
for (const [, entry] of indexRegistry) {
|
|
8728
|
+
if (entry.embeddingAdapter?.shutdown) {
|
|
8729
|
+
adaptersToShutdown.add(entry.embeddingAdapter);
|
|
8730
|
+
}
|
|
8731
|
+
}
|
|
8732
|
+
for (const adapter of adaptersToShutdown) {
|
|
8733
|
+
try {
|
|
8734
|
+
await adapter.shutdown?.();
|
|
8735
|
+
} catch {
|
|
8736
|
+
}
|
|
8737
|
+
}
|
|
8738
|
+
await orchestrator.shutdown();
|
|
8739
|
+
await executor.shutdown();
|
|
8740
|
+
eventHandlers.clear();
|
|
8741
|
+
indexRegistry.clear();
|
|
8742
|
+
}
|
|
8743
|
+
|
|
8744
|
+
// src/engine/startup-checks.ts
|
|
8745
|
+
var announcedMissingWorkerEntry = false;
|
|
8746
|
+
function withoutWorkers(config, reason) {
|
|
8747
|
+
if (!announcedMissingWorkerEntry) {
|
|
8748
|
+
announcedMissingWorkerEntry = true;
|
|
8749
|
+
console.warn(
|
|
8750
|
+
`Narsil answers every query on this thread, because it finds no worker entry beside its own module at "${String(reason.details.moduleUrl)}", which happens where a bundler folds @delali/narsil into an application bundle. Keep @delali/narsil outside the bundle to use worker threads, or set workers.enabled to false to silence this notice.`
|
|
8751
|
+
);
|
|
8752
|
+
}
|
|
8753
|
+
return { ...config, workers: { ...config?.workers, enabled: false } };
|
|
8754
|
+
}
|
|
8755
|
+
async function resolveRunnableConfig(config) {
|
|
8756
|
+
requireSearchBackendSettings();
|
|
8757
|
+
if (!(config?.workers?.enabled ?? workersEnabledByDefault())) return config;
|
|
8758
|
+
const runtime = detectRuntime();
|
|
8759
|
+
if (!runtime.supportsWorkerThreads && !runtime.supportsWebWorkers) return config;
|
|
8760
|
+
try {
|
|
8761
|
+
await requireWorkerEntry();
|
|
8762
|
+
return config;
|
|
8763
|
+
} catch (err) {
|
|
8764
|
+
const missingEntry = err instanceof NarsilError && err.code === ErrorCodes.CONFIG_INVALID;
|
|
8765
|
+
if (!missingEntry || config?.workers?.enabled === true) throw err;
|
|
8766
|
+
return withoutWorkers(config, err);
|
|
8767
|
+
}
|
|
8768
|
+
}
|
|
8769
|
+
|
|
8289
8770
|
// src/engine/index-lifecycle.ts
|
|
8290
8771
|
async function runLifecycleHook(core, hookName, context) {
|
|
8291
8772
|
try {
|
|
@@ -8419,42 +8900,6 @@ function registerEngineEmbeddingAdapter(core, name, adapter) {
|
|
|
8419
8900
|
}
|
|
8420
8901
|
}
|
|
8421
8902
|
|
|
8422
|
-
// src/engine/lifecycle.ts
|
|
8423
|
-
async function shutdownEngine(core) {
|
|
8424
|
-
const { executor, durability, indexRegistry, eventHandlers, orchestrator } = core;
|
|
8425
|
-
core.indexState.dispose();
|
|
8426
|
-
for (const [name] of indexRegistry) {
|
|
8427
|
-
const manager = executor.getManager(name);
|
|
8428
|
-
if (manager) {
|
|
8429
|
-
for (const [, vectorIndex] of manager.getVectorIndexes()) {
|
|
8430
|
-
vectorIndex.dispose();
|
|
8431
|
-
}
|
|
8432
|
-
}
|
|
8433
|
-
}
|
|
8434
|
-
if (durability) {
|
|
8435
|
-
await durability.manager.shutdown();
|
|
8436
|
-
}
|
|
8437
|
-
if (core.invalidation) {
|
|
8438
|
-
await core.invalidation.shutdown();
|
|
8439
|
-
}
|
|
8440
|
-
const adaptersToShutdown = /* @__PURE__ */ new Set();
|
|
8441
|
-
for (const [, entry] of indexRegistry) {
|
|
8442
|
-
if (entry.embeddingAdapter?.shutdown) {
|
|
8443
|
-
adaptersToShutdown.add(entry.embeddingAdapter);
|
|
8444
|
-
}
|
|
8445
|
-
}
|
|
8446
|
-
for (const adapter of adaptersToShutdown) {
|
|
8447
|
-
try {
|
|
8448
|
-
await adapter.shutdown?.();
|
|
8449
|
-
} catch {
|
|
8450
|
-
}
|
|
8451
|
-
}
|
|
8452
|
-
await orchestrator.shutdown();
|
|
8453
|
-
await executor.shutdown();
|
|
8454
|
-
eventHandlers.clear();
|
|
8455
|
-
indexRegistry.clear();
|
|
8456
|
-
}
|
|
8457
|
-
|
|
8458
8903
|
// src/engine/mutations/durable-rollback.ts
|
|
8459
8904
|
function rollbackFailed(docId, originalError, rollbackError) {
|
|
8460
8905
|
throw new NarsilError(
|
|
@@ -8822,6 +9267,7 @@ async function applyAdmittedDocuments(ctx, indexName, admitted, options, failed)
|
|
|
8822
9267
|
}
|
|
8823
9268
|
const applied = chunkStart + BATCH_CHUNK_SIZE;
|
|
8824
9269
|
if (applied < admitted.length) {
|
|
9270
|
+
ctx.checkHeapPressure(indexName);
|
|
8825
9271
|
await new Promise((r) => setTimeout(r, 0));
|
|
8826
9272
|
}
|
|
8827
9273
|
}
|
|
@@ -9061,6 +9507,7 @@ async function recordMergedDocuments(ctx, indexName, built, frozen, memberIndexe
|
|
|
9061
9507
|
succeededIds.add(doc.docId);
|
|
9062
9508
|
}
|
|
9063
9509
|
if (index + 1 < chunks.length) {
|
|
9510
|
+
ctx.checkHeapPressure(indexName);
|
|
9064
9511
|
await new Promise((r) => setTimeout(r, 0));
|
|
9065
9512
|
}
|
|
9066
9513
|
}
|
|
@@ -9272,6 +9719,7 @@ async function insertDocumentBatch(ctx, indexName, documents, options) {
|
|
|
9272
9719
|
}
|
|
9273
9720
|
if (prepared.length > 0) await applyPrepared(prepared);
|
|
9274
9721
|
if (chunkEnd < documents.length) {
|
|
9722
|
+
ctx.checkHeapPressure(indexName);
|
|
9275
9723
|
await new Promise((r) => setTimeout(r, 0));
|
|
9276
9724
|
}
|
|
9277
9725
|
}
|
|
@@ -9469,6 +9917,18 @@ async function settleUpdate(ctx, indexName, prepared) {
|
|
|
9469
9917
|
async function prepareUpdate(ctx, indexName, entry, docId, document) {
|
|
9470
9918
|
ctx.guardShutdown();
|
|
9471
9919
|
validateDocId(docId);
|
|
9920
|
+
const manager = ctx.requireManager(indexName);
|
|
9921
|
+
if (ctx.pluginRegistry.hasHooks("beforeUpdate")) {
|
|
9922
|
+
await ctx.pluginRegistry.runHook("beforeUpdate", {
|
|
9923
|
+
indexName,
|
|
9924
|
+
docId,
|
|
9925
|
+
oldDocument: manager.get(docId) ?? {},
|
|
9926
|
+
newDocument: document
|
|
9927
|
+
});
|
|
9928
|
+
}
|
|
9929
|
+
if (entry.config.required && entry.config.required.length > 0) {
|
|
9930
|
+
validateRequiredFields(document, entry.config.required);
|
|
9931
|
+
}
|
|
9472
9932
|
if (entry.config.embedding) {
|
|
9473
9933
|
if (entry.embeddingAdapter) {
|
|
9474
9934
|
await embedDocumentFields(
|
|
@@ -9485,16 +9945,9 @@ async function prepareUpdate(ctx, indexName, entry, docId, document) {
|
|
|
9485
9945
|
);
|
|
9486
9946
|
}
|
|
9487
9947
|
}
|
|
9488
|
-
const manager = ctx.requireManager(indexName);
|
|
9489
9948
|
const oldDocument = manager.get(docId);
|
|
9490
9949
|
const oldPartitionDoc = manager.getRef(docId);
|
|
9491
9950
|
const rollbackDoc = oldPartitionDoc ? structuredClone(oldPartitionDoc) : void 0;
|
|
9492
|
-
await ctx.pluginRegistry.runHook("beforeUpdate", {
|
|
9493
|
-
indexName,
|
|
9494
|
-
docId,
|
|
9495
|
-
oldDocument: oldDocument ?? {},
|
|
9496
|
-
newDocument: document
|
|
9497
|
-
});
|
|
9498
9951
|
const vecIndexes = manager.getVectorIndexes();
|
|
9499
9952
|
const extractedVectors = /* @__PURE__ */ new Map();
|
|
9500
9953
|
if (vecIndexes.size > 0) {
|
|
@@ -9613,6 +10066,7 @@ async function updateDocumentBatch(ctx, indexName, updates, options) {
|
|
|
9613
10066
|
succeeded.push(update.docId);
|
|
9614
10067
|
}
|
|
9615
10068
|
if (chunkEnd < updates.length) {
|
|
10069
|
+
ctx.checkHeapPressure(indexName);
|
|
9616
10070
|
await new Promise((r) => setTimeout(r, 0));
|
|
9617
10071
|
}
|
|
9618
10072
|
}
|
|
@@ -9857,7 +10311,7 @@ async function executeRebalance(manager, indexName, targetPartitionCount, ctx) {
|
|
|
9857
10311
|
await replayQueued(manager, indexName, waq, ctx);
|
|
9858
10312
|
if (ctx.durabilityManager) {
|
|
9859
10313
|
await ctx.durabilityManager.persistMetadata(indexName);
|
|
9860
|
-
await ctx.durabilityManager.checkpoint(indexName);
|
|
10314
|
+
await (ctx.durabilityManager.checkpointFromMemory?.(indexName) ?? ctx.durabilityManager.checkpoint(indexName));
|
|
9861
10315
|
await replayQueued(manager, indexName, waq, ctx);
|
|
9862
10316
|
}
|
|
9863
10317
|
await ctx.orchestrator.resyncIndex(indexName, wasPromoted);
|
|
@@ -10336,6 +10790,10 @@ function readProcessMemory() {
|
|
|
10336
10790
|
}
|
|
10337
10791
|
|
|
10338
10792
|
// src/narsil/reads.ts
|
|
10793
|
+
async function enterRead(core, indexName) {
|
|
10794
|
+
core.guardShutdown();
|
|
10795
|
+
if (core.rebalancingIndexes.has(indexName)) await yieldToEventLoop();
|
|
10796
|
+
}
|
|
10339
10797
|
function scopedParams(params, options) {
|
|
10340
10798
|
if (options?.globalStats === void 0) {
|
|
10341
10799
|
return params;
|
|
@@ -10354,7 +10812,7 @@ function broadcastStatsFor(core, options) {
|
|
|
10354
10812
|
return (name) => invalidation.broadcastStats(name);
|
|
10355
10813
|
}
|
|
10356
10814
|
async function runEngineQuery(core, indexName, params, options) {
|
|
10357
|
-
core
|
|
10815
|
+
await enterRead(core, indexName);
|
|
10358
10816
|
const release = await core.indexState.acquire(indexName);
|
|
10359
10817
|
try {
|
|
10360
10818
|
const entry = core.requireIndex(indexName);
|
|
@@ -10395,7 +10853,7 @@ async function runEngineQuery(core, indexName, params, options) {
|
|
|
10395
10853
|
}
|
|
10396
10854
|
}
|
|
10397
10855
|
async function runEnginePreflight(core, indexName, params, options) {
|
|
10398
|
-
core
|
|
10856
|
+
await enterRead(core, indexName);
|
|
10399
10857
|
const release = await core.indexState.acquire(indexName);
|
|
10400
10858
|
try {
|
|
10401
10859
|
const entry = core.requireIndex(indexName);
|
|
@@ -10428,7 +10886,7 @@ async function runEnginePreflight(core, indexName, params, options) {
|
|
|
10428
10886
|
}
|
|
10429
10887
|
}
|
|
10430
10888
|
async function runEngineSuggest(core, indexName, params, partitionIds) {
|
|
10431
|
-
core
|
|
10889
|
+
await enterRead(core, indexName);
|
|
10432
10890
|
const release = await core.indexState.acquire(indexName);
|
|
10433
10891
|
try {
|
|
10434
10892
|
const result = executeSuggest(
|
|
@@ -10446,7 +10904,7 @@ async function runEngineSuggest(core, indexName, params, partitionIds) {
|
|
|
10446
10904
|
}
|
|
10447
10905
|
}
|
|
10448
10906
|
async function runEngineQueryStats(core, indexName, terms, partitionIds) {
|
|
10449
|
-
core
|
|
10907
|
+
await enterRead(core, indexName);
|
|
10450
10908
|
const release = await core.indexState.acquire(indexName);
|
|
10451
10909
|
try {
|
|
10452
10910
|
const entry = core.requireIndex(indexName);
|
|
@@ -10471,13 +10929,14 @@ async function runEngineQueryStats(core, indexName, terms, partitionIds) {
|
|
|
10471
10929
|
}
|
|
10472
10930
|
}
|
|
10473
10931
|
async function runEngineListDocuments(core, indexName, params, partitionIds) {
|
|
10474
|
-
core
|
|
10932
|
+
await enterRead(core, indexName);
|
|
10475
10933
|
const release = await core.indexState.acquire(indexName);
|
|
10476
10934
|
try {
|
|
10477
10935
|
const entry = core.requireIndex(indexName);
|
|
10478
10936
|
return executeListDocuments(params, {
|
|
10479
10937
|
manager: core.requireManager(indexName),
|
|
10480
10938
|
schema: entry.config.schema,
|
|
10939
|
+
strict: entry.config.strict,
|
|
10481
10940
|
partitionIds
|
|
10482
10941
|
});
|
|
10483
10942
|
} finally {
|
|
@@ -10784,19 +11243,14 @@ function createNarsilFromCore(core, config) {
|
|
|
10784
11243
|
|
|
10785
11244
|
// src/narsil/index.ts
|
|
10786
11245
|
async function createNarsil(config) {
|
|
10787
|
-
|
|
10788
|
-
|
|
10789
|
-
|
|
10790
|
-
|
|
10791
|
-
|
|
10792
|
-
if (core.invalidation) {
|
|
10793
|
-
await core.invalidation.start();
|
|
10794
|
-
}
|
|
10795
|
-
if (config?.lifecycle === void 0) await core.analysisRebuild.reviewStaleIndexes();
|
|
10796
|
-
return createNarsilFromCore(core, config);
|
|
11246
|
+
requireKnownConfig(config);
|
|
11247
|
+
const runnableConfig = await resolveRunnableConfig(config);
|
|
11248
|
+
const core = createEngineCore(runnableConfig);
|
|
11249
|
+
await startEngineCore(core, runnableConfig);
|
|
11250
|
+
return createNarsilFromCore(core, runnableConfig);
|
|
10797
11251
|
}
|
|
10798
11252
|
|
|
10799
11253
|
// src/index.ts
|
|
10800
11254
|
var VERSION = "0.1.0";
|
|
10801
11255
|
|
|
10802
|
-
export { CHECKPOINT_WORKER_HEARTBEAT_MS, VERSION, applyDeleteEntry, applyIndexEntry,
|
|
11256
|
+
export { CHECKPOINT_WORKER_HEARTBEAT_MS, VERSION, applyDeleteEntry, applyIndexEntry, computeEntryChecksum, createAckMessage, createDurableDirectory, createEngineCore, createEngineIndex, createEntryBatchMessage, createEntryMessage, createForwardBatchMessage, createForwardBatchResultMessage, createForwardMessage, createInsyncAddMessage, createInsyncConfirmMessage, createInsyncRemoveMessage, createNarsil, createNarsilFromCore, requireKnownConfig, resolveRunnableConfig, runEngineListDocuments, runEnginePreflight, runEngineQuery, runEngineQueryStats, runEngineSuggest, startEngineCore, validateAckPayload, validateEntryBatchPayload, validateEntryPayload, validateForwardBatchPayload, validateForwardBatchResultPayload, validateInsyncConfirmPayload, validateReplicationEntry, writeCheckpointSegments };
|