@harperfast/harper 5.3.0-alpha.1 → 5.3.0-beta.2
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/bin/cliOperations.ts +12 -0
- package/bin/restart.ts +66 -6
- package/components/Application.ts +1134 -112
- package/components/DESIGN.md +421 -0
- package/components/OptionsWatcher.ts +368 -102
- package/components/Scope.ts +20 -1
- package/components/componentLoader.ts +43 -4
- package/components/deploymentOperations.ts +4 -1
- package/components/deploymentRecorder.ts +9 -2
- package/components/mcp/DESIGN.md +109 -0
- package/components/mcp/audit.ts +21 -17
- package/components/operations.js +284 -52
- package/components/operationsValidation.js +49 -2
- package/components/packageComponent.ts +25 -1
- package/components/requestRestart.ts +11 -0
- package/config/DESIGN.md +306 -0
- package/config/RootConfigWatcher.ts +191 -37
- package/config/configReadRetry.ts +62 -0
- package/config/configUtils.ts +78 -26
- package/config/parseConfigFile.ts +34 -0
- package/config/readConfigFileSync.ts +44 -0
- package/config/watcherArming.ts +59 -0
- package/config-root.schema.json +4 -0
- package/dataLayer/DESIGN.md +179 -0
- package/dataLayer/harperBridge/ResourceBridge.ts +28 -2
- package/dataLayer/restoreMarker.ts +92 -25
- package/dist/bin/cliOperations.js +13 -0
- package/dist/bin/cliOperations.js.map +1 -1
- package/dist/bin/restart.js +42 -6
- package/dist/bin/restart.js.map +1 -1
- package/dist/components/Application.d.ts +104 -9
- package/dist/components/Application.js +954 -102
- package/dist/components/Application.js.map +1 -1
- package/dist/components/OptionsWatcher.d.ts +4 -1
- package/dist/components/OptionsWatcher.js +378 -104
- package/dist/components/OptionsWatcher.js.map +1 -1
- package/dist/components/Scope.js +15 -1
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/componentLoader.js +35 -3
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/deploymentOperations.js +4 -1
- package/dist/components/deploymentOperations.js.map +1 -1
- package/dist/components/deploymentRecorder.d.ts +4 -2
- package/dist/components/deploymentRecorder.js +1 -0
- package/dist/components/deploymentRecorder.js.map +1 -1
- package/dist/components/mcp/audit.d.ts +2 -1
- package/dist/components/mcp/audit.js +21 -17
- package/dist/components/mcp/audit.js.map +1 -1
- package/dist/components/operations.d.ts +28 -0
- package/dist/components/operations.js +263 -54
- package/dist/components/operations.js.map +1 -1
- package/dist/components/operationsValidation.js +48 -2
- package/dist/components/operationsValidation.js.map +1 -1
- package/dist/components/packageComponent.js +24 -0
- package/dist/components/packageComponent.js.map +1 -1
- package/dist/components/requestRestart.d.ts +1 -0
- package/dist/components/requestRestart.js +7 -0
- package/dist/components/requestRestart.js.map +1 -1
- package/dist/config/RootConfigWatcher.d.ts +2 -0
- package/dist/config/RootConfigWatcher.js +189 -35
- package/dist/config/RootConfigWatcher.js.map +1 -1
- package/dist/config/configReadRetry.d.ts +8 -0
- package/dist/config/configReadRetry.js +62 -0
- package/dist/config/configReadRetry.js.map +1 -0
- package/dist/config/configUtils.d.ts +10 -9
- package/dist/config/configUtils.js +63 -27
- package/dist/config/configUtils.js.map +1 -1
- package/dist/config/parseConfigFile.d.ts +4 -0
- package/dist/config/parseConfigFile.js +35 -0
- package/dist/config/parseConfigFile.js.map +1 -0
- package/dist/config/readConfigFileSync.d.ts +1 -0
- package/dist/config/readConfigFileSync.js +47 -0
- package/dist/config/readConfigFileSync.js.map +1 -0
- package/dist/config/watcherArming.d.ts +15 -0
- package/dist/config/watcherArming.js +59 -0
- package/dist/config/watcherArming.js.map +1 -0
- package/dist/dataLayer/harperBridge/ResourceBridge.js +21 -2
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/dataLayer/restoreMarker.d.ts +21 -8
- package/dist/dataLayer/restoreMarker.js +94 -27
- package/dist/dataLayer/restoreMarker.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/json/systemSchema.json +3 -0
- package/dist/resources/DatabaseTransaction.d.ts +25 -0
- package/dist/resources/DatabaseTransaction.js +224 -18
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/LMDBTransaction.d.ts +2 -1
- package/dist/resources/LMDBTransaction.js +22 -3
- package/dist/resources/LMDBTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.js +22 -6
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
- package/dist/resources/RecordEncoder.d.ts +1 -1
- package/dist/resources/RecordEncoder.js +46 -10
- package/dist/resources/RecordEncoder.js.map +1 -1
- package/dist/resources/Resource.js +97 -13
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +8 -0
- package/dist/resources/RocksIndexStore.js +2 -1
- package/dist/resources/RocksIndexStore.js.map +1 -1
- package/dist/resources/RocksTransactionLogStore.d.ts +10 -0
- package/dist/resources/RocksTransactionLogStore.js +104 -33
- package/dist/resources/RocksTransactionLogStore.js.map +1 -1
- package/dist/resources/Table.d.ts +58 -7
- package/dist/resources/Table.js +1143 -348
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/write.d.ts +3 -0
- package/dist/resources/analytics/write.js +49 -16
- package/dist/resources/analytics/write.js.map +1 -1
- package/dist/resources/auditStore.d.ts +170 -0
- package/dist/resources/auditStore.js +457 -11
- package/dist/resources/auditStore.js.map +1 -1
- package/dist/resources/crdt.d.ts +10 -0
- package/dist/resources/crdt.js +22 -0
- package/dist/resources/crdt.js.map +1 -1
- package/dist/resources/dataLoader.js +3 -4
- package/dist/resources/dataLoader.js.map +1 -1
- package/dist/resources/databases.d.ts +16 -13
- package/dist/resources/databases.js +625 -177
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/derivedIndexRegistry.d.ts +5 -0
- package/dist/resources/derivedIndexRegistry.js +68 -0
- package/dist/resources/derivedIndexRegistry.js.map +1 -0
- package/dist/resources/derivedIndexRuntime.d.ts +215 -0
- package/dist/resources/derivedIndexRuntime.js +2027 -0
- package/dist/resources/derivedIndexRuntime.js.map +1 -0
- package/dist/resources/graphql.js +3 -2
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +102 -9
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +867 -39
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/indexes/fullTextDerivedIndex.d.ts +81 -0
- package/dist/resources/indexes/fullTextDerivedIndex.js +1004 -0
- package/dist/resources/indexes/fullTextDerivedIndex.js.map +1 -0
- package/dist/resources/indexes/fullTextNativeBinding.d.ts +78 -0
- package/dist/resources/indexes/fullTextNativeBinding.js +85 -0
- package/dist/resources/indexes/fullTextNativeBinding.js.map +1 -0
- package/dist/resources/indexes/hnswDerivedIndex.d.ts +67 -0
- package/dist/resources/indexes/hnswDerivedIndex.js +464 -0
- package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -0
- package/dist/resources/indexes/hnswPlaneBinding.d.ts +65 -0
- package/dist/resources/indexes/hnswPlaneBinding.js +91 -0
- package/dist/resources/indexes/hnswPlaneBinding.js.map +1 -0
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.d.ts +24 -0
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js +149 -0
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js.map +1 -0
- package/dist/resources/nodeIdMapping.d.ts +5 -0
- package/dist/resources/nodeIdMapping.js +49 -0
- package/dist/resources/nodeIdMapping.js.map +1 -1
- package/dist/resources/recordLock.d.ts +47 -4
- package/dist/resources/recordLock.js +138 -7
- package/dist/resources/recordLock.js.map +1 -1
- package/dist/resources/recordLockCoordinator.d.ts +557 -0
- package/dist/resources/recordLockCoordinator.js +2597 -0
- package/dist/resources/recordLockCoordinator.js.map +1 -0
- package/dist/resources/replayLogs.js +5 -0
- package/dist/resources/replayLogs.js.map +1 -1
- package/dist/resources/replicatedApplyFailure.d.ts +16 -0
- package/dist/resources/replicatedApplyFailure.js +63 -0
- package/dist/resources/replicatedApplyFailure.js.map +1 -0
- package/dist/resources/scheduler/scheduler.js +3 -3
- package/dist/resources/scheduler/scheduler.js.map +1 -1
- package/dist/resources/search.d.ts +10 -4
- package/dist/resources/search.js +160 -40
- package/dist/resources/search.js.map +1 -1
- package/dist/resources/tracked.d.ts +5 -1
- package/dist/resources/tracked.js +74 -23
- package/dist/resources/tracked.js.map +1 -1
- package/dist/resources/transactionBroadcast.js +4 -6
- package/dist/resources/transactionBroadcast.js.map +1 -1
- package/dist/security/auth.js +59 -23
- package/dist/security/auth.js.map +1 -1
- package/dist/security/deferredAuthentication.d.ts +11 -0
- package/dist/security/deferredAuthentication.js +25 -3
- package/dist/security/deferredAuthentication.js.map +1 -1
- package/dist/security/jsLoader.js +9 -4
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/server/REST.js +39 -5
- package/dist/server/REST.js.map +1 -1
- package/dist/server/http.d.ts +5 -1
- package/dist/server/http.js +34 -2
- package/dist/server/http.js.map +1 -1
- package/dist/server/mqtt.js +5 -1
- package/dist/server/mqtt.js.map +1 -1
- package/dist/server/serverHelpers/Headers.d.ts +2 -0
- package/dist/server/serverHelpers/Headers.js +6 -0
- package/dist/server/serverHelpers/Headers.js.map +1 -1
- package/dist/server/serverHelpers/NodeAdapterResponse.d.ts +48 -0
- package/dist/server/serverHelpers/NodeAdapterResponse.js +220 -0
- package/dist/server/serverHelpers/NodeAdapterResponse.js.map +1 -0
- package/dist/server/serverHelpers/Request.d.ts +5 -10
- package/dist/server/serverHelpers/Request.js +38 -136
- package/dist/server/serverHelpers/Request.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.d.ts +2 -0
- package/dist/server/serverHelpers/contentTypes.js +189 -15
- package/dist/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/server/serverHelpers/serverUtilities.d.ts +3 -3
- package/dist/server/serverHelpers/serverUtilities.js +110 -20
- package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
- package/dist/server/serverHelpers/uwsServer.js +4 -1
- package/dist/server/serverHelpers/uwsServer.js.map +1 -1
- package/dist/server/serverHelpers/webSocketCloseReason.d.ts +2 -0
- package/dist/server/serverHelpers/webSocketCloseReason.js +29 -0
- package/dist/server/serverHelpers/webSocketCloseReason.js.map +1 -0
- package/dist/server/storageReclamation.js +1 -1
- package/dist/server/storageReclamation.js.map +1 -1
- package/dist/server/threads/isolatedApplications.d.ts +47 -0
- package/dist/server/threads/isolatedApplications.js +171 -0
- package/dist/server/threads/isolatedApplications.js.map +1 -0
- package/dist/server/threads/logRotationTransport.d.ts +1 -0
- package/dist/server/threads/logRotationTransport.js +33 -0
- package/dist/server/threads/logRotationTransport.js.map +1 -0
- package/dist/server/threads/manageThreads.d.ts +64 -6
- package/dist/server/threads/manageThreads.js +261 -12
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/socketRouter.d.ts +1 -0
- package/dist/server/threads/socketRouter.js +196 -13
- package/dist/server/threads/socketRouter.js.map +1 -1
- package/dist/server/threads/threadServer.js +30 -7
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/utility/errors/hdbError.d.ts +24 -0
- package/dist/utility/errors/hdbError.js +58 -1
- package/dist/utility/errors/hdbError.js.map +1 -1
- package/dist/utility/hdbTerms.d.ts +2 -0
- package/dist/utility/hdbTerms.js +2 -0
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/logging/harper_logger.js +218 -38
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/dist/utility/logging/logGenerationCoordinator.d.ts +35 -0
- package/dist/utility/logging/logGenerationCoordinator.js +184 -0
- package/dist/utility/logging/logGenerationCoordinator.js.map +1 -0
- package/dist/utility/logging/logRotation.d.ts +46 -0
- package/dist/utility/logging/logRotation.js +365 -0
- package/dist/utility/logging/logRotation.js.map +1 -0
- package/dist/utility/logging/logRotator.d.ts +1 -1
- package/dist/utility/logging/logRotator.js +172 -92
- package/dist/utility/logging/logRotator.js.map +1 -1
- package/dist/utility/npmUtilities.js +6 -4
- package/dist/utility/npmUtilities.js.map +1 -1
- package/dist/utility/watcherFallback.d.ts +0 -45
- package/dist/utility/watcherFallback.js +1 -125
- package/dist/utility/watcherFallback.js.map +1 -1
- package/dist/validation/configValidator.js +6 -3
- package/dist/validation/configValidator.js.map +1 -1
- package/index.ts +9 -0
- package/json/systemSchema.json +3 -0
- package/npm-shrinkwrap.json +177 -87
- package/package.json +13 -5
- package/resources/DESIGN.md +689 -19
- package/resources/DatabaseTransaction.ts +230 -17
- package/resources/LMDBTransaction.ts +21 -3
- package/resources/PrimaryRocksDatabase.ts +20 -7
- package/resources/RecordEncoder.ts +52 -10
- package/resources/Resource.ts +97 -13
- package/resources/ResourceInterface.ts +8 -0
- package/resources/RocksIndexStore.ts +2 -1
- package/resources/RocksTransactionLogStore.ts +111 -31
- package/resources/Table.ts +1273 -396
- package/resources/analytics/DESIGN.md +38 -0
- package/resources/analytics/write.ts +50 -17
- package/resources/auditStore.ts +460 -11
- package/resources/crdt.ts +22 -0
- package/resources/dataLoader.ts +3 -4
- package/resources/databases.ts +611 -147
- package/resources/derivedIndexRegistry.ts +56 -0
- package/resources/derivedIndexRuntime.ts +2292 -0
- package/resources/graphql.ts +3 -2
- package/resources/indexes/DESIGN.md +833 -0
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +924 -46
- package/resources/indexes/fullTextDerivedIndex.ts +1165 -0
- package/resources/indexes/fullTextNativeBinding.ts +146 -0
- package/resources/indexes/hnswDerivedIndex.ts +531 -0
- package/resources/indexes/hnswPlaneBinding.ts +174 -0
- package/resources/indexes/nativeFullTextDerivedIndexLifecycle.ts +181 -0
- package/resources/nodeIdMapping.ts +50 -0
- package/resources/record-locks.md +1407 -0
- package/resources/recordLock.ts +173 -7
- package/resources/recordLockCoordinator.ts +3082 -0
- package/resources/replayLogs.ts +5 -0
- package/resources/replicatedApplyFailure.ts +77 -0
- package/resources/scheduler/DESIGN.md +40 -0
- package/resources/scheduler/scheduler.ts +4 -4
- package/resources/search.ts +169 -49
- package/resources/tracked.ts +73 -22
- package/resources/transactionBroadcast.ts +4 -4
- package/security/DESIGN.md +175 -0
- package/security/auth.ts +53 -24
- package/security/deferredAuthentication.ts +24 -2
- package/security/jsLoader.ts +9 -4
- package/server/DESIGN.md +275 -0
- package/server/REST.ts +42 -6
- package/server/http.ts +34 -2
- package/server/mqtt.ts +6 -4
- package/server/serverHelpers/Headers.ts +5 -1
- package/server/serverHelpers/NodeAdapterResponse.ts +221 -0
- package/server/serverHelpers/Request.ts +33 -131
- package/server/serverHelpers/contentTypes.ts +188 -15
- package/server/serverHelpers/serverUtilities.ts +157 -27
- package/server/serverHelpers/uwsServer.ts +4 -1
- package/server/serverHelpers/webSocketCloseReason.ts +25 -0
- package/server/storageReclamation.ts +2 -2
- package/server/threads/isolatedApplications.ts +157 -0
- package/server/threads/logRotationTransport.ts +40 -0
- package/server/threads/manageThreads.js +254 -12
- package/server/threads/socketRouter.ts +217 -11
- package/server/threads/threadServer.js +30 -7
- package/studio/web/assets/{Chat-BnCBegQz.js → Chat-DADFFGe_.js} +1 -1
- package/studio/web/assets/{FloatingChat-CoDW1ySS.js → FloatingChat-D_mI-rZ7.js} +3 -3
- package/studio/web/assets/{apiToken-Bwk5BLXW.js → apiToken-c2NiSDHa.js} +1 -1
- package/studio/web/assets/{applications-DHxGi7JH.js → applications-DktUqh7G.js} +1 -1
- package/studio/web/assets/{cssMode-s0cWI-_M.js → cssMode-Cs_75Xhw.js} +1 -1
- package/studio/web/assets/{editor-DNcRHK54.js → editor-19b-Y1IN.js} +1 -1
- package/studio/web/assets/{html-Bdssedlg.js → html-DiYEQMpB.js} +1 -1
- package/studio/web/assets/{htmlMode-CoDlJ3fw.js → htmlMode-CmR0y7P_.js} +1 -1
- package/studio/web/assets/{index-D6sxmFLR.js → index-Dm0rfkJ7.js} +5 -5
- package/studio/web/assets/{index.lazy-tmU5BS8s.js → index.lazy-7vqt2CC3.js} +1 -1
- package/studio/web/assets/{javascript-B8meVSTH.js → javascript-BWtCFuOt.js} +1 -1
- package/studio/web/assets/{jsonMode-DpIPd35T.js → jsonMode-Buzzbv9y.js} +1 -1
- package/studio/web/assets/{languageServices-C_5FMJzQ.js → languageServices-SqsFWfTM.js} +1 -1
- package/studio/web/assets/{lspLanguageFeatures-BIzNBzPK.js → lspLanguageFeatures-EMV5cmjo.js} +1 -1
- package/studio/web/assets/{notifications-CvZivSbh.js → notifications-CAB-LZWT.js} +1 -1
- package/studio/web/assets/{notifications-CQf18QKb.js → notifications-DRzmSRxM.js} +1 -1
- package/studio/web/assets/{profile-DdOwtntb.js → profile-BNKAl79n.js} +1 -1
- package/studio/web/assets/{regions-n69fwagr.js → regions-CUow_Zw2.js} +1 -1
- package/studio/web/assets/{register-PfWTCXWB.js → register-Dkt3WUMp.js} +2 -2
- package/studio/web/assets/{setComponentFile-Bg6O7X0S.js → setComponentFile-BZRfMD0N.js} +1 -1
- package/studio/web/assets/{setup-CUx_aUDl.js → setup-D_yiEPO2.js} +2 -2
- package/studio/web/assets/{status-D7BVKqX9.js → status-DhHh1Ge-.js} +1 -1
- package/studio/web/assets/{toggleHighContrast-DBSyXzMr.js → toggleHighContrast-D7L1PDtV.js} +1 -1
- package/studio/web/assets/{tsMode-BByKCjBS.js → tsMode-CCwLk1YS.js} +1 -1
- package/studio/web/assets/{typescript-DDLnLpw9.js → typescript-BP1j1mjn.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-31CHGaHk.js → useEntityRestURL-D7bnYxLw.js} +1 -1
- package/studio/web/assets/{workers-pR3jRY9D.js → workers-tOuCNT17.js} +1 -1
- package/studio/web/assets/{xml-2iRnMhQO.js → xml-BSG_3mQT.js} +1 -1
- package/studio/web/assets/{yaml-Bf92gJpd.js → yaml-DxiLprBB.js} +1 -1
- package/studio/web/index.html +1 -1
- package/utility/DESIGN.md +55 -0
- package/utility/errors/hdbError.ts +54 -0
- package/utility/hdbTerms.ts +2 -0
- package/utility/logging/harper_logger.ts +210 -30
- package/utility/logging/logGenerationCoordinator.ts +196 -0
- package/utility/logging/logRotation.ts +367 -0
- package/utility/logging/logRotator.ts +196 -91
- package/utility/npmUtilities.ts +6 -4
- package/utility/watcherFallback.ts +0 -122
- package/validation/configValidator.ts +6 -3
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { closeSync, fsyncSync, openSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { loggerWithTag } from '../../utility/logging/logger.ts';
|
|
4
|
+
|
|
5
|
+
const logger = loggerWithTag('HNSW');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parallel arrays, ascending by distance: hit i is (ids[i], distances[i]) with its host key at
|
|
9
|
+
* keys.subarray(keyEnds[i - 1] ?? 0, keyEnds[i]); an empty key means the node vanished mid-query.
|
|
10
|
+
*/
|
|
11
|
+
export interface PlaneSearchHits {
|
|
12
|
+
ids: Uint32Array;
|
|
13
|
+
distances: Float32Array;
|
|
14
|
+
keys: Buffer;
|
|
15
|
+
keyEnds: Uint32Array;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** A predicate batch: candidate ids and their host keys, laid out as in PlaneSearchHits. */
|
|
19
|
+
export type PlanePredicate = (ids: number[], keys: Buffer, keyEnds: Uint32Array) => Uint8Array;
|
|
20
|
+
|
|
21
|
+
/** NAPI surface of the native file-primary HNSW index (`@harperfast/hnsw`). */
|
|
22
|
+
export interface HnswPlane {
|
|
23
|
+
readonly dims: number;
|
|
24
|
+
readonly layer0Cap: number;
|
|
25
|
+
readonly keyCap: number;
|
|
26
|
+
insert(vector: Float32Array, key?: Buffer): number;
|
|
27
|
+
remove(id: number): void;
|
|
28
|
+
writeNodeRaw(
|
|
29
|
+
id: number,
|
|
30
|
+
level: number,
|
|
31
|
+
vector: Buffer,
|
|
32
|
+
scale: number,
|
|
33
|
+
invMag: number,
|
|
34
|
+
neighbors: Uint32Array,
|
|
35
|
+
upper: Uint32Array[] | null
|
|
36
|
+
): void;
|
|
37
|
+
clearNode(id: number): void;
|
|
38
|
+
setEntryPoint(id: number, level: number): void;
|
|
39
|
+
getEntryPoint(): number[];
|
|
40
|
+
search(
|
|
41
|
+
vector: Float32Array,
|
|
42
|
+
k: number,
|
|
43
|
+
ef: number,
|
|
44
|
+
filter?: Uint8Array | null,
|
|
45
|
+
filterExpansion?: number | null
|
|
46
|
+
): Promise<PlaneSearchHits>;
|
|
47
|
+
searchWithPredicate(
|
|
48
|
+
vector: Float32Array,
|
|
49
|
+
k: number,
|
|
50
|
+
ef: number,
|
|
51
|
+
predicate: PlanePredicate,
|
|
52
|
+
filterExpansion?: number | null,
|
|
53
|
+
visitBudget?: number | null
|
|
54
|
+
): Promise<PlaneSearchHits>;
|
|
55
|
+
searchSync(vector: Float32Array, k: number, ef: number): PlaneSearchHits;
|
|
56
|
+
writeNodeRawIfAbsent(
|
|
57
|
+
id: number,
|
|
58
|
+
level: number,
|
|
59
|
+
vector: Buffer,
|
|
60
|
+
scale: number,
|
|
61
|
+
invMag: number,
|
|
62
|
+
neighbors: Uint32Array,
|
|
63
|
+
upper: Uint32Array[] | null
|
|
64
|
+
): boolean;
|
|
65
|
+
openedClean(): boolean;
|
|
66
|
+
idHighWater(): number;
|
|
67
|
+
getWatermark(): number;
|
|
68
|
+
setWatermark(txn: number): void;
|
|
69
|
+
flush(watermark?: number): void;
|
|
70
|
+
flushAsync(watermark?: number): Promise<void>;
|
|
71
|
+
invalidateFile(): PlaneInvalidationOutcome;
|
|
72
|
+
invalidated(): boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface HnswPlaneConstructor {
|
|
76
|
+
readonly prototype: HnswPlane;
|
|
77
|
+
create(path: string, dims: number, layer0Cap: number, maxNodes: number, keyCap?: number): HnswPlane;
|
|
78
|
+
open(path: string): HnswPlane;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface PlaneInvalidationOutcome {
|
|
82
|
+
inBand: boolean;
|
|
83
|
+
sidecar: boolean;
|
|
84
|
+
inBandError?: string;
|
|
85
|
+
sidecarError?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interface HnswPlanePackage {
|
|
89
|
+
Plane: HnswPlaneConstructor;
|
|
90
|
+
invalidatePlane(path: string): PlaneInvalidationOutcome;
|
|
91
|
+
stalePathFor(path: string): string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Entry-point id meaning "none" (u32::MAX in the plane header). */
|
|
95
|
+
export const PLANE_NO_ID = 0xffffffff;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Where an index's plane file lives: next to its store, named by the dbiKey
|
|
99
|
+
* (`table/attribute`, flattened to a single file name). Exposed separately from the index
|
|
100
|
+
* instance so crash-recovery drop paths can remove the file without opening the index.
|
|
101
|
+
*/
|
|
102
|
+
export function planeFilePathFor(storePath: string, storeName: string): string {
|
|
103
|
+
// encodeURIComponent is injective and never emits a path separator: table `a` attribute
|
|
104
|
+
// `b.c` and table `a.b` attribute `c` must not share a plane file (dot-flattening let two
|
|
105
|
+
// indexes serve each other's node ids as their own primary keys)
|
|
106
|
+
return join(storePath, `${encodeURIComponent(storeName)}.hnsw`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Tombstone marking a plane file that could not be deleted (e.g. Windows EBUSY while still
|
|
111
|
+
* mapped). Its presence means the plane file is STALE: never open it — delete both when
|
|
112
|
+
* possible and rebuild.
|
|
113
|
+
*/
|
|
114
|
+
export function planeStalePathFor(planePath: string): string {
|
|
115
|
+
// the package owns the convention; the literal covers the calls that precede its load
|
|
116
|
+
return binding?.stalePathFor(planePath) ?? `${planePath}.stale`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let binding: HnswPlanePackage | null | undefined;
|
|
120
|
+
let bindingLoadError: Error | undefined;
|
|
121
|
+
let bindingWarningLogged = false;
|
|
122
|
+
|
|
123
|
+
function getHnswPackage(warn = true): HnswPlanePackage | null {
|
|
124
|
+
if (binding === undefined) {
|
|
125
|
+
try {
|
|
126
|
+
binding = require('@harperfast/hnsw') as HnswPlanePackage;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
binding = null;
|
|
129
|
+
bindingLoadError = error as Error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (binding === null && warn && !bindingWarningLogged) {
|
|
133
|
+
bindingWarningLogged = true;
|
|
134
|
+
logger.warn?.(
|
|
135
|
+
`The @harperfast/hnsw native module is not available (${bindingLoadError?.message}); ` +
|
|
136
|
+
'indexes with nativePlane enabled will remain unavailable until the module can load'
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
return binding;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** The native plane constructor, or null when the compiled artifact is unavailable (warns once by default). */
|
|
143
|
+
export function getPlaneBinding(warn = true): HnswPlaneConstructor | null {
|
|
144
|
+
return getHnswPackage(warn)?.Plane ?? null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Make a derived plane unadoptable before it is replaced or removed. */
|
|
148
|
+
export function invalidatePlaneFile(filePath: string, attached?: HnswPlane | null): PlaneInvalidationOutcome {
|
|
149
|
+
if (attached) return attached.invalidateFile();
|
|
150
|
+
const hnswPackage = getHnswPackage();
|
|
151
|
+
if (hnswPackage) return hnswPackage.invalidatePlane(filePath);
|
|
152
|
+
// A plane can outlive the package that made it (uninstall, or a prebuild that stopped
|
|
153
|
+
// loading), and a reinstall would then adopt it. Only the sidecar is reachable without the
|
|
154
|
+
// package, and it has to survive a power loss to be worth writing, so fsync it and the
|
|
155
|
+
// directory entry that names it — the same durability the package's own sidecar has.
|
|
156
|
+
const fd = openSync(planeStalePathFor(filePath), 'w');
|
|
157
|
+
try {
|
|
158
|
+
fsyncSync(fd);
|
|
159
|
+
} finally {
|
|
160
|
+
closeSync(fd);
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const dirFd = openSync(dirname(filePath), 'r');
|
|
164
|
+
try {
|
|
165
|
+
fsyncSync(dirFd);
|
|
166
|
+
} finally {
|
|
167
|
+
closeSync(dirFd);
|
|
168
|
+
}
|
|
169
|
+
} catch {
|
|
170
|
+
// Windows cannot open a directory as a file; it also does not need this — the metadata
|
|
171
|
+
// journal already orders the create ahead of anything that could read the sidecar
|
|
172
|
+
}
|
|
173
|
+
return { inBand: false, sidecar: true };
|
|
174
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
3
|
+
import { loggerWithTag } from '../../utility/logging/logger.ts';
|
|
4
|
+
import {
|
|
5
|
+
FullTextDerivedIndexBackend,
|
|
6
|
+
HARPER_FULLTEXT_MAX_CURSOR_PAYLOAD_BYTES,
|
|
7
|
+
type FullTextDerivedIndexBackendOptions,
|
|
8
|
+
type FullTextDerivedIndexEngine,
|
|
9
|
+
type FullTextDerivedIndexInspection,
|
|
10
|
+
} from './fullTextDerivedIndex.ts';
|
|
11
|
+
import {
|
|
12
|
+
getValidatedFullTextRuntimeInfo,
|
|
13
|
+
loadFullTextNativeBinding,
|
|
14
|
+
type NativeFullTextIndexConfiguration,
|
|
15
|
+
type NativeFullTextModule,
|
|
16
|
+
validateFullTextNativeBinding,
|
|
17
|
+
} from './fullTextNativeBinding.ts';
|
|
18
|
+
|
|
19
|
+
const logger = loggerWithTag('fulltext-derived-index');
|
|
20
|
+
|
|
21
|
+
export type NativeFullTextDerivedIndexLifecycleOptions = NativeFullTextIndexConfiguration & {
|
|
22
|
+
storePath: string;
|
|
23
|
+
storeName: string;
|
|
24
|
+
indexId: string;
|
|
25
|
+
sourceGeneration: string;
|
|
26
|
+
binding?: NativeFullTextModule | (() => Promise<NativeFullTextModule>);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type NativeFullTextDerivedIndexBackendOptions = Omit<FullTextDerivedIndexBackendOptions, 'lifecycle'> &
|
|
30
|
+
Omit<NativeFullTextDerivedIndexLifecycleOptions, 'indexId'>;
|
|
31
|
+
|
|
32
|
+
export class NativeFullTextDerivedIndexLifecycle {
|
|
33
|
+
readonly #options: NativeFullTextDerivedIndexLifecycleOptions;
|
|
34
|
+
readonly #path: string;
|
|
35
|
+
readonly #generation: string;
|
|
36
|
+
#binding?: NativeFullTextModule;
|
|
37
|
+
#maxCommitPayloadBytes?: number;
|
|
38
|
+
#reclaimOperation = Promise.resolve();
|
|
39
|
+
|
|
40
|
+
constructor(options: NativeFullTextDerivedIndexLifecycleOptions) {
|
|
41
|
+
if (!isAbsolute(options.storePath)) throw new TypeError('Full-text storePath must be absolute');
|
|
42
|
+
if (!options.storeName) throw new TypeError('Full-text storeName is required');
|
|
43
|
+
if (!options.indexId) throw new TypeError('Full-text indexId is required');
|
|
44
|
+
if (!options.sourceGeneration) throw new TypeError('Full-text sourceGeneration is required');
|
|
45
|
+
this.#options = {
|
|
46
|
+
...options,
|
|
47
|
+
fields: options.fields.map((field) => ({ ...field })),
|
|
48
|
+
limits: { ...options.limits },
|
|
49
|
+
};
|
|
50
|
+
// Hashing bounds filenames for arbitrary store names; the index id remains in native metadata and logs.
|
|
51
|
+
this.#path = join(resolve(options.storePath), `${digest(options.storeName)}.fulltext`);
|
|
52
|
+
this.#generation = digest(options.sourceGeneration);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get path(): string {
|
|
56
|
+
return this.#path;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async initialize(options: { reclaimRetired?: boolean } = {}): Promise<void> {
|
|
60
|
+
const binding = await this.#getBinding();
|
|
61
|
+
binding.validateNativeFullTextIndexOptions({
|
|
62
|
+
...this.#nativeOptions(),
|
|
63
|
+
limits: this.#options.limits,
|
|
64
|
+
});
|
|
65
|
+
this.#maxCommitPayloadBytes = getValidatedFullTextRuntimeInfo(binding).limits.maxCommitPayloadBytes;
|
|
66
|
+
if (options.reclaimRetired !== false) this.#queueReclaimRetired();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
startRetiredStorageReclamation(): void {
|
|
70
|
+
this.#requireBinding();
|
|
71
|
+
// Fulltext publishes unique retired paths and removes them with force, so workers may sweep concurrently.
|
|
72
|
+
this.#queueReclaimRetired();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
get maxCommitPayloadBytes(): number {
|
|
76
|
+
if (this.#maxCommitPayloadBytes === undefined) throw new Error('Full-text lifecycle has not been initialized');
|
|
77
|
+
return this.#maxCommitPayloadBytes;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
inspect(): FullTextDerivedIndexInspection {
|
|
81
|
+
return this.#requireBinding().inspectNativeFullTextIndex(this.#nativeOptions());
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async open(): Promise<FullTextDerivedIndexEngine> {
|
|
85
|
+
return this.#requireBinding().openNativeFullTextIndex({
|
|
86
|
+
...this.#nativeOptions(),
|
|
87
|
+
limits: this.#options.limits,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async reset(): Promise<void> {
|
|
92
|
+
const result = await this.#requireBinding().resetNativeFullTextIndex({
|
|
93
|
+
path: this.#path,
|
|
94
|
+
indexId: this.#options.indexId,
|
|
95
|
+
});
|
|
96
|
+
this.#queueReclaimRetired(result.state === 'reset' ? result.retiredPath : undefined);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
#queueReclaimRetired(retiredPath?: string): void {
|
|
100
|
+
const reclaim = () => this.#reclaimRetired(retiredPath);
|
|
101
|
+
this.#reclaimOperation = this.#reclaimOperation.then(reclaim, reclaim).catch((error) => {
|
|
102
|
+
logWarning(`Could not complete reclamation for full-text index '${this.#options.indexId}'`, error);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async #reclaimRetired(retiredPath?: string): Promise<void> {
|
|
107
|
+
try {
|
|
108
|
+
const result = await this.#requireBinding().reclaimRetiredNativeFullTextIndexes({
|
|
109
|
+
path: this.#path,
|
|
110
|
+
retiredPath,
|
|
111
|
+
});
|
|
112
|
+
if (result.failed > 0)
|
|
113
|
+
logWarning(`Could not remove ${result.failed} retired paths for full-text index '${this.#options.indexId}'`);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
logWarning(`Could not reclaim retired paths for full-text index '${this.#options.indexId}'`, error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#nativeOptions(): Omit<NativeFullTextIndexConfiguration, 'limits'> & {
|
|
120
|
+
path: string;
|
|
121
|
+
indexId: string;
|
|
122
|
+
generation: string;
|
|
123
|
+
} {
|
|
124
|
+
return {
|
|
125
|
+
path: this.#path,
|
|
126
|
+
indexId: this.#options.indexId,
|
|
127
|
+
generation: this.#generation,
|
|
128
|
+
fields: this.#options.fields,
|
|
129
|
+
analyzer: this.#options.analyzer,
|
|
130
|
+
stopWords: this.#options.stopWords,
|
|
131
|
+
positions: this.#options.positions,
|
|
132
|
+
surfaceTerms: this.#options.surfaceTerms,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async #getBinding(): Promise<NativeFullTextModule> {
|
|
137
|
+
if (this.#binding) return this.#binding;
|
|
138
|
+
const configured = this.#options.binding;
|
|
139
|
+
this.#binding = configured
|
|
140
|
+
? await validateFullTextNativeBinding(typeof configured === 'function' ? await configured() : configured)
|
|
141
|
+
: await loadFullTextNativeBinding();
|
|
142
|
+
return this.#binding;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#requireBinding(): NativeFullTextModule {
|
|
146
|
+
if (!this.#binding) throw new Error('Full-text native binding has not been initialized');
|
|
147
|
+
return this.#binding;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export async function createNativeFullTextDerivedIndexBackend(
|
|
152
|
+
options: NativeFullTextDerivedIndexBackendOptions
|
|
153
|
+
): Promise<FullTextDerivedIndexBackend> {
|
|
154
|
+
const maxCursorPayloadBytes = options.maxCursorPayloadBytes ?? HARPER_FULLTEXT_MAX_CURSOR_PAYLOAD_BYTES;
|
|
155
|
+
if (maxCursorPayloadBytes > HARPER_FULLTEXT_MAX_CURSOR_PAYLOAD_BYTES)
|
|
156
|
+
throw new RangeError(`Full-text maxCursorPayloadBytes must not exceed ${HARPER_FULLTEXT_MAX_CURSOR_PAYLOAD_BYTES}`);
|
|
157
|
+
const lifecycle = new NativeFullTextDerivedIndexLifecycle({ ...options, indexId: options.id });
|
|
158
|
+
await lifecycle.initialize({ reclaimRetired: false });
|
|
159
|
+
if (maxCursorPayloadBytes > lifecycle.maxCommitPayloadBytes)
|
|
160
|
+
throw new RangeError('Full-text maxCursorPayloadBytes exceeds the native commit payload capacity');
|
|
161
|
+
const backend = new FullTextDerivedIndexBackend({
|
|
162
|
+
...options,
|
|
163
|
+
id: options.id,
|
|
164
|
+
lifecycle,
|
|
165
|
+
maxCursorPayloadBytes,
|
|
166
|
+
});
|
|
167
|
+
lifecycle.startRetiredStorageReclamation();
|
|
168
|
+
return backend;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function digest(value: string): string {
|
|
172
|
+
return createHash('sha256').update(value).digest('hex');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function logWarning(message: string, error?: unknown): void {
|
|
176
|
+
const code =
|
|
177
|
+
error && typeof error === 'object' && 'code' in error && typeof error.code === 'string' ? error.code : undefined;
|
|
178
|
+
try {
|
|
179
|
+
logger.warn?.(`${message}${code ? ` (${code})` : ''}`);
|
|
180
|
+
} catch {}
|
|
181
|
+
}
|
|
@@ -50,6 +50,7 @@ function getIdMappingRecord(auditStore) {
|
|
|
50
50
|
// now we can take over the local node id
|
|
51
51
|
nameToId[node_name] = 0;
|
|
52
52
|
auditStore.putSync(REMOTE_NODE_IDS, pack(idMappingRecord));
|
|
53
|
+
invalidateNodeNames(auditStore);
|
|
53
54
|
}
|
|
54
55
|
return idMappingRecord;
|
|
55
56
|
}
|
|
@@ -84,6 +85,7 @@ export function remoteToLocalNodeId(remoteMapping: any, auditStore: any) {
|
|
|
84
85
|
}
|
|
85
86
|
if (hasChanges) {
|
|
86
87
|
auditStore.putSync(REMOTE_NODE_IDS, pack(idMappingRecord));
|
|
88
|
+
invalidateNodeNames(auditStore);
|
|
87
89
|
}
|
|
88
90
|
return remoteToLocalId;
|
|
89
91
|
}
|
|
@@ -103,6 +105,7 @@ export function getIdOfRemoteNode(remoteNodeName, auditStore) {
|
|
|
103
105
|
id = lastId + 1;
|
|
104
106
|
nameToId[remoteNodeName] = id;
|
|
105
107
|
auditStore.putSync(REMOTE_NODE_IDS, pack(idMappingRecord));
|
|
108
|
+
invalidateNodeNames(auditStore);
|
|
106
109
|
}
|
|
107
110
|
logger.trace?.('The remote node name map', remoteNodeName, nameToId, id);
|
|
108
111
|
return id;
|
|
@@ -123,3 +126,50 @@ export function lastTimeInAuditStore(auditStore: Database) {
|
|
|
123
126
|
export function getThisNodeId(auditStore: any) {
|
|
124
127
|
return exportIdMapping(auditStore)?.[server.hostname];
|
|
125
128
|
}
|
|
129
|
+
|
|
130
|
+
// Inverted id -> name map. exportIdMapping() re-reads and unpacks the mapping record on every call,
|
|
131
|
+
// which is far too much per replicated entry on the apply thread; ids are stable once minted, so the
|
|
132
|
+
// inversion is cached and dropped wherever nameToId is written. Keyed per audit store because short
|
|
133
|
+
// ids are minted per database in first-seen order — id 1 names a different node in each one.
|
|
134
|
+
// Refresh window for a MISS. A hit is always trusted, but a miss cannot be: ids are minted by
|
|
135
|
+
// whichever worker first talks to a peer, and invalidation only reaches that worker's own copy — so
|
|
136
|
+
// a newly admitted node is absent from every other worker's map until it is rebuilt. Rebuilding on
|
|
137
|
+
// every miss would put a store read and unpack back on the apply thread for each unmapped entry, so
|
|
138
|
+
// misses re-read at most this often — short enough that a joining node loses at most a stray entry,
|
|
139
|
+
// long enough that a burst of unmapped ids cannot drive the store. `onMiss` records whether the
|
|
140
|
+
// cached map was built by a `rebuildOnMiss` caller, which is what bounds that caller to one read per
|
|
141
|
+
// window too rather than exempting it from the interval entirely.
|
|
142
|
+
const NODE_NAME_REFRESH_MS = 50;
|
|
143
|
+
const idToNodeName = new WeakMap<object, { names: Map<number, string>; refreshedAt: number; onMiss: boolean }>();
|
|
144
|
+
function invalidateNodeNames(auditStore: any) {
|
|
145
|
+
idToNodeName.delete(auditStore);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The node name a local short id refers to. Callers that must attribute a replicated entry to its
|
|
150
|
+
* origin node need the globally stable name, not the id, which is assigned per node.
|
|
151
|
+
*/
|
|
152
|
+
export function getNodeNameForId(
|
|
153
|
+
auditStore: any,
|
|
154
|
+
nodeId: number | undefined,
|
|
155
|
+
rebuildOnMiss = false
|
|
156
|
+
): string | undefined {
|
|
157
|
+
if (typeof nodeId !== 'number' || !auditStore) return undefined;
|
|
158
|
+
const cached = idToNodeName.get(auditStore);
|
|
159
|
+
const hit = cached?.names.get(nodeId);
|
|
160
|
+
if (hit !== undefined) return hit;
|
|
161
|
+
const now = Date.now();
|
|
162
|
+
// `rebuildOnMiss` is for entry classes whose loss costs more than the read: a dropped record lock
|
|
163
|
+
// release leaves the home holding a grant until its own deadline, so a just-minted id is worth one
|
|
164
|
+
// store read rather than up to `NODE_NAME_REFRESH_MS` of dropped entries. ONE read, though, not one
|
|
165
|
+
// per entry — an id this map will never resolve (a node whose mapping was purged, an origin
|
|
166
|
+
// relayed from a database that never minted one) is exactly the burst the interval exists to keep
|
|
167
|
+
// off the apply thread, and a rebuild inside the window is not stale: a write to the mapping drops
|
|
168
|
+
// this entry outright (`invalidateNodeNames`).
|
|
169
|
+
if (cached && now - cached.refreshedAt < NODE_NAME_REFRESH_MS && (!rebuildOnMiss || cached.onMiss)) return undefined;
|
|
170
|
+
const nameToId = exportIdMapping(auditStore);
|
|
171
|
+
const names = new Map<number, string>();
|
|
172
|
+
for (const name in nameToId) names.set(nameToId[name], name);
|
|
173
|
+
idToNodeName.set(auditStore, { names, refreshedAt: now, onMiss: rebuildOnMiss });
|
|
174
|
+
return names.get(nodeId);
|
|
175
|
+
}
|