@harperfast/harper 5.3.0-beta.3 → 5.3.0-beta.4
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 +25 -3
- package/bin/run.ts +18 -0
- package/components/Application.ts +52 -78
- package/components/DESIGN.md +76 -3
- package/components/Scope.ts +10 -4
- package/components/anthropic/index.ts +10 -1
- package/components/bedrock/index.ts +10 -1
- package/components/componentLoader.ts +23 -14
- package/components/mcp/tools/operations.ts +3 -1
- package/components/ollama/index.ts +10 -1
- package/components/openai/index.ts +265 -18
- package/components/operations.js +40 -1
- package/config/configUtils.ts +3 -19
- package/dataLayer/DESIGN.md +29 -5
- package/dataLayer/harperBridge/ResourceBridge.ts +39 -3
- package/dataLayer/restoreMarker.ts +250 -14
- package/dataLayer/schema.ts +2 -7
- package/dataLayer/schemaDescribe.ts +10 -0
- package/dist/bin/cliOperations.js +28 -3
- package/dist/bin/cliOperations.js.map +1 -1
- package/dist/bin/run.js +19 -0
- package/dist/bin/run.js.map +1 -1
- package/dist/components/Application.d.ts +8 -2
- package/dist/components/Application.js +43 -69
- package/dist/components/Application.js.map +1 -1
- package/dist/components/Scope.d.ts +1 -1
- package/dist/components/Scope.js +9 -4
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/anthropic/index.d.ts +0 -10
- package/dist/components/anthropic/index.js +9 -1
- package/dist/components/anthropic/index.js.map +1 -1
- package/dist/components/bedrock/index.d.ts +0 -12
- package/dist/components/bedrock/index.js +9 -1
- package/dist/components/bedrock/index.js.map +1 -1
- package/dist/components/componentLoader.js +23 -14
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/mcp/tools/operations.js +1 -1
- package/dist/components/mcp/tools/operations.js.map +1 -1
- package/dist/components/ollama/index.d.ts +0 -13
- package/dist/components/ollama/index.js +9 -1
- package/dist/components/ollama/index.js.map +1 -1
- package/dist/components/openai/index.d.ts +11 -15
- package/dist/components/openai/index.js +224 -12
- package/dist/components/openai/index.js.map +1 -1
- package/dist/components/operations.d.ts +9 -0
- package/dist/components/operations.js +32 -2
- package/dist/components/operations.js.map +1 -1
- package/dist/config/configUtils.d.ts +1 -2
- package/dist/config/configUtils.js +7 -22
- package/dist/config/configUtils.js.map +1 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.js +29 -2
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/dataLayer/restoreMarker.d.ts +25 -3
- package/dist/dataLayer/restoreMarker.js +213 -13
- package/dist/dataLayer/restoreMarker.js.map +1 -1
- package/dist/dataLayer/schema.js +1 -4
- package/dist/dataLayer/schema.js.map +1 -1
- package/dist/dataLayer/schemaDescribe.js +10 -0
- package/dist/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/json/systemSchema.json +28 -0
- package/dist/resources/DatabaseTransaction.d.ts +21 -1
- package/dist/resources/DatabaseTransaction.js +147 -8
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.js +1 -1
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
- package/dist/resources/Table.d.ts +31 -3
- package/dist/resources/Table.js +397 -79
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/auditStore.js +16 -10
- package/dist/resources/auditStore.js.map +1 -1
- package/dist/resources/blob.d.ts +1 -0
- package/dist/resources/blob.js +14 -5
- package/dist/resources/blob.js.map +1 -1
- package/dist/resources/branchDatabase.js +3 -3
- package/dist/resources/branchDatabase.js.map +1 -1
- package/dist/resources/databaseDropPreparation.d.ts +20 -0
- package/dist/resources/databaseDropPreparation.js +106 -0
- package/dist/resources/databaseDropPreparation.js.map +1 -0
- package/dist/resources/databases.d.ts +26 -21
- package/dist/resources/databases.js +1040 -204
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/derivedIndexRegistry.d.ts +23 -0
- package/dist/resources/derivedIndexRegistry.js +92 -0
- package/dist/resources/derivedIndexRegistry.js.map +1 -1
- package/dist/resources/derivedIndexRuntime.d.ts +10 -0
- package/dist/resources/derivedIndexRuntime.js +136 -25
- package/dist/resources/derivedIndexRuntime.js.map +1 -1
- package/dist/resources/derivedIndexes.d.ts +39 -0
- package/dist/resources/derivedIndexes.js +733 -0
- package/dist/resources/derivedIndexes.js.map +1 -0
- package/dist/resources/fullTextSchema.d.ts +8 -0
- package/dist/resources/fullTextSchema.js +42 -0
- package/dist/resources/fullTextSchema.js.map +1 -1
- package/dist/resources/fullTextSchemaLifecycle.d.ts +0 -5
- package/dist/resources/fullTextSchemaLifecycle.js +0 -9
- package/dist/resources/fullTextSchemaLifecycle.js.map +1 -1
- package/dist/resources/graphql.js +101 -9
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/hnswDerivedIndex.d.ts +0 -10
- package/dist/resources/indexes/hnswDerivedIndex.js +0 -230
- package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -1
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.d.ts +3 -0
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js +57 -8
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js.map +1 -1
- package/dist/resources/models/Models.d.ts +26 -3
- package/dist/resources/models/Models.js +256 -13
- package/dist/resources/models/Models.js.map +1 -1
- package/dist/resources/models/TestBackend.d.ts +3 -1
- package/dist/resources/models/TestBackend.js +35 -1
- package/dist/resources/models/TestBackend.js.map +1 -1
- package/dist/resources/models/analyticsTable.d.ts +2 -2
- package/dist/resources/models/analyticsTable.js +4 -2
- package/dist/resources/models/analyticsTable.js.map +1 -1
- package/dist/resources/models/backendHelpers.d.ts +24 -0
- package/dist/resources/models/backendHelpers.js +18 -1
- package/dist/resources/models/backendHelpers.js.map +1 -1
- package/dist/resources/models/backendRegistry.d.ts +10 -21
- package/dist/resources/models/backendRegistry.js +74 -18
- package/dist/resources/models/backendRegistry.js.map +1 -1
- package/dist/resources/models/bootstrap.d.ts +7 -0
- package/dist/resources/models/bootstrap.js +36 -4
- package/dist/resources/models/bootstrap.js.map +1 -1
- package/dist/resources/models/decideHook.d.ts +33 -0
- package/dist/resources/models/decideHook.js +105 -0
- package/dist/resources/models/decideHook.js.map +1 -0
- package/dist/resources/models/decision.d.ts +64 -0
- package/dist/resources/models/decision.js +507 -0
- package/dist/resources/models/decision.js.map +1 -0
- package/dist/resources/models/decisionStore.d.ts +134 -0
- package/dist/resources/models/decisionStore.js +350 -0
- package/dist/resources/models/decisionStore.js.map +1 -0
- package/dist/resources/models/embedHook.d.ts +66 -8
- package/dist/resources/models/embedHook.js +228 -61
- package/dist/resources/models/embedHook.js.map +1 -1
- package/dist/resources/models/generativeDecision.d.ts +58 -0
- package/dist/resources/models/generativeDecision.js +315 -0
- package/dist/resources/models/generativeDecision.js.map +1 -0
- package/dist/resources/models/types.d.ts +232 -4
- package/dist/security/authn/oidc/tokenExchange.js +3 -33
- package/dist/security/authn/oidc/tokenExchange.js.map +1 -1
- package/dist/security/authn/oidc/tokenUseTable.d.ts +9 -0
- package/dist/security/authn/oidc/tokenUseTable.js +28 -0
- package/dist/security/authn/oidc/tokenUseTable.js.map +1 -0
- package/dist/security/certificateVerification/certificateVerificationSource.d.ts +0 -1
- package/dist/security/certificateVerification/certificateVerificationSource.js +3 -3
- package/dist/security/certificateVerification/certificateVerificationSource.js.map +1 -1
- package/dist/security/certificateVerification/crlVerification.js +77 -165
- package/dist/security/certificateVerification/crlVerification.js.map +1 -1
- package/dist/security/certificateVerification/types.d.ts +0 -3
- package/dist/security/certificateVerification/verificationTables.d.ts +19 -0
- package/dist/security/certificateVerification/verificationTables.js +125 -0
- package/dist/security/certificateVerification/verificationTables.js.map +1 -0
- package/dist/security/certificateVerification/verificationUtils.d.ts +1 -1
- package/dist/security/certificateVerification/verificationUtils.js +6 -31
- package/dist/security/certificateVerification/verificationUtils.js.map +1 -1
- package/dist/security/jsLoader.d.ts +5 -0
- package/dist/security/jsLoader.js +8 -0
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/server/Server.d.ts +1 -0
- package/dist/server/Server.js.map +1 -1
- package/dist/server/http.js +2 -2
- package/dist/server/http.js.map +1 -1
- package/dist/server/itc/serverHandlers.js +21 -2
- package/dist/server/itc/serverHandlers.js.map +1 -1
- package/dist/server/jobs/jobOwnership.d.ts +25 -0
- package/dist/server/jobs/jobOwnership.js +127 -0
- package/dist/server/jobs/jobOwnership.js.map +1 -0
- package/dist/server/jobs/jobProcess.js +9 -2
- package/dist/server/jobs/jobProcess.js.map +1 -1
- package/dist/server/jobs/jobs.js +18 -5
- package/dist/server/jobs/jobs.js.map +1 -1
- package/dist/server/loadRootComponents.js +10 -0
- package/dist/server/loadRootComponents.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.js +11 -1
- package/dist/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/server/serverHelpers/serverHandlers.js +12 -2
- package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
- package/dist/server/threads/itc.d.ts +4 -1
- package/dist/server/threads/itc.js +29 -6
- package/dist/server/threads/itc.js.map +1 -1
- package/dist/server/threads/manageThreads.d.ts +12 -4
- package/dist/server/threads/manageThreads.js +241 -119
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/threadServer.d.ts +1 -0
- package/dist/server/threads/threadServer.js +15 -1
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/server/threads/windowsProcessTree.d.ts +91 -0
- package/dist/server/threads/windowsProcessTree.js +361 -0
- package/dist/server/threads/windowsProcessTree.js.map +1 -0
- package/dist/server/throttle.d.ts +1 -1
- package/dist/server/throttle.js +8 -1
- package/dist/server/throttle.js.map +1 -1
- package/dist/upgrade/directives/5-3-0.js +53 -46
- package/dist/upgrade/directives/5-3-0.js.map +1 -1
- package/dist/utility/errors/hdbError.d.ts +10 -1
- package/dist/utility/errors/hdbError.js +55 -11
- package/dist/utility/errors/hdbError.js.map +1 -1
- package/dist/utility/fsync.d.ts +2 -0
- package/dist/utility/fsync.js +20 -0
- package/dist/utility/fsync.js.map +1 -0
- package/dist/utility/globalSchema.d.ts +18 -0
- package/dist/utility/hdbTerms.d.ts +3 -0
- package/dist/utility/hdbTerms.js +3 -0
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/signalling.d.ts +7 -1
- package/dist/utility/signalling.js +27 -3
- package/dist/utility/signalling.js.map +1 -1
- package/dist/utility/when.d.ts +1 -0
- package/dist/utility/when.js +21 -0
- package/dist/utility/when.js.map +1 -1
- package/dist/validation/configValidator.js +30 -3
- package/dist/validation/configValidator.js.map +1 -1
- package/index.ts +13 -0
- package/json/systemSchema.json +28 -0
- package/npm-shrinkwrap.json +151 -55
- package/package.json +14 -13
- package/resources/DESIGN.md +80 -100
- package/resources/DatabaseTransaction.ts +157 -10
- package/resources/PrimaryRocksDatabase.ts +2 -2
- package/resources/Table.ts +494 -101
- package/resources/auditStore.ts +18 -10
- package/resources/blob.ts +13 -6
- package/resources/branchDatabase.ts +3 -3
- package/resources/databaseDropPreparation.ts +120 -0
- package/resources/databases.ts +1155 -205
- package/resources/derivedIndexRegistry.ts +122 -0
- package/resources/derivedIndexRuntime.ts +157 -30
- package/resources/derivedIndexes.ts +856 -0
- package/resources/fullTextSchema.ts +55 -0
- package/resources/fullTextSchemaLifecycle.ts +0 -15
- package/resources/graphql.ts +107 -12
- package/resources/indexes/DESIGN.md +7 -0
- package/resources/indexes/hnswDerivedIndex.ts +0 -260
- package/resources/indexes/nativeFullTextDerivedIndexLifecycle.ts +72 -9
- package/resources/models/DESIGN.md +39 -0
- package/resources/models/Models.ts +361 -18
- package/resources/models/TestBackend.ts +56 -1
- package/resources/models/analyticsTable.ts +6 -4
- package/resources/models/backendHelpers.ts +17 -0
- package/resources/models/backendRegistry.ts +76 -21
- package/resources/models/bootstrap.ts +57 -7
- package/resources/models/decideHook.ts +135 -0
- package/resources/models/decision.ts +505 -0
- package/resources/models/decisionStore.ts +419 -0
- package/resources/models/embedHook.ts +272 -55
- package/resources/models/generativeDecision.ts +459 -0
- package/resources/models/types.ts +231 -4
- package/security/DESIGN.md +22 -2
- package/security/authn/oidc/tokenExchange.ts +6 -34
- package/security/authn/oidc/tokenUseTable.ts +27 -0
- package/security/certificateVerification/certificateVerificationSource.ts +2 -3
- package/security/certificateVerification/crlVerification.ts +96 -174
- package/security/certificateVerification/types.ts +0 -3
- package/security/certificateVerification/verificationTables.ts +124 -0
- package/security/certificateVerification/verificationUtils.ts +8 -32
- package/security/jsLoader.ts +9 -1
- package/server/DESIGN.md +27 -1
- package/server/Server.ts +2 -1
- package/server/http.ts +4 -2
- package/server/itc/serverHandlers.js +29 -1
- package/server/jobs/jobOwnership.ts +89 -0
- package/server/jobs/jobProcess.ts +8 -2
- package/server/jobs/jobs.ts +15 -4
- package/server/loadRootComponents.js +11 -0
- package/server/serverHelpers/contentTypes.ts +13 -1
- package/server/serverHelpers/serverHandlers.js +11 -2
- package/server/threads/itc.js +32 -6
- package/server/threads/manageThreads.js +268 -122
- package/server/threads/threadServer.js +13 -1
- package/server/threads/windowsProcessTree.ts +409 -0
- package/server/throttle.ts +11 -1
- package/studio/web/assets/{Chat-CveGbxe6.js → Chat-Bq60AO-g.js} +1 -1
- package/studio/web/assets/{FloatingChat-IpCFgYWL.js → FloatingChat-D4560T0p.js} +3 -3
- package/studio/web/assets/{apiToken-CS8fFTgL.js → apiToken-BEvbFPMc.js} +1 -1
- package/studio/web/assets/{applications-D7F4Jopl.js → applications-CSUVTH4E.js} +1 -1
- package/studio/web/assets/{cssMode-Daw_oLPq.js → cssMode-DJyc9mHj.js} +1 -1
- package/studio/web/assets/{editor-B3Bs1zWX.js → editor-DDIsQfj6.js} +1 -1
- package/studio/web/assets/{html-CDaQwWiE.js → html-g1BJHxLy.js} +1 -1
- package/studio/web/assets/{htmlMode-Cb6emO4U.js → htmlMode-Vh5IglCQ.js} +1 -1
- package/studio/web/assets/{index-B4g2Fu_2.js → index-DGRAzvO4.js} +5 -5
- package/studio/web/assets/{index.lazy-NwhvF1q1.js → index.lazy-vgSic7et.js} +1 -1
- package/studio/web/assets/{javascript-2_86gB9Q.js → javascript-5VIolqf7.js} +1 -1
- package/studio/web/assets/{jsonMode-DX-i86uQ.js → jsonMode-DG4zQraI.js} +1 -1
- package/studio/web/assets/{languageServices-B85WN-Ej.js → languageServices-t5FBpX-i.js} +1 -1
- package/studio/web/assets/{lspLanguageFeatures-JCWHIsKg.js → lspLanguageFeatures-DgVnNPh-.js} +1 -1
- package/studio/web/assets/{notifications-D9ejzt52.js → notifications-BeWcX04m.js} +1 -1
- package/studio/web/assets/{notifications-B7hjKhai.js → notifications-DUGWrWoO.js} +1 -1
- package/studio/web/assets/{profile-D8B6No3F.js → profile-BvUEDBzW.js} +1 -1
- package/studio/web/assets/{regions-DFazIfTK.js → regions-CB-_2m7M.js} +1 -1
- package/studio/web/assets/{register-CwFLviLF.js → register-1w1Zr3b2.js} +2 -2
- package/studio/web/assets/{setComponentFile-VnJ80SSJ.js → setComponentFile-z4-qJCbA.js} +1 -1
- package/studio/web/assets/{setup-BYQxFy-C.js → setup-CS50uJ4x.js} +2 -2
- package/studio/web/assets/{status-Cn5XjmsL.js → status-CI0wOdOE.js} +1 -1
- package/studio/web/assets/{toggleHighContrast-g9xIh6TX.js → toggleHighContrast-BsAE4jPe.js} +1 -1
- package/studio/web/assets/{tsMode-EC-n0Wqc.js → tsMode-D83Gg3cq.js} +1 -1
- package/studio/web/assets/{typescript-DHmM9JCO.js → typescript-Dfp-GrNq.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-BUdnVSVv.js → useEntityRestURL-BKetlINf.js} +1 -1
- package/studio/web/assets/{workers-BVl9bBB-.js → workers-CCvmDLH3.js} +1 -1
- package/studio/web/assets/{xml-C4_sqOkX.js → xml-06pZp00U.js} +1 -1
- package/studio/web/assets/{yaml-CVkbDqtW.js → yaml-iSnWMVlV.js} +1 -1
- package/studio/web/index.html +1 -1
- package/upgrade/directives/5-3-0.ts +61 -54
- package/utility/DESIGN.md +5 -1
- package/utility/errors/hdbError.ts +56 -8
- package/utility/fsync.ts +16 -0
- package/utility/hdbTerms.ts +3 -0
- package/utility/signalling.ts +33 -4
- package/utility/when.ts +22 -0
- package/validation/configValidator.ts +36 -7
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.suspendDerivedIndexActivation = suspendDerivedIndexActivation;
|
|
4
|
+
exports.permanentlySuspendDerivedIndexActivation = permanentlySuspendDerivedIndexActivation;
|
|
5
|
+
exports.attachDerivedIndexes = attachDerivedIndexes;
|
|
6
|
+
exports.retireFullTextIndexes = retireFullTextIndexes;
|
|
7
|
+
exports.refreshDerivedIndexes = refreshDerivedIndexes;
|
|
8
|
+
exports.assertFullTextActivationSupported = assertFullTextActivationSupported;
|
|
9
|
+
exports.fullTextDerivedIndexId = fullTextDerivedIndexId;
|
|
10
|
+
exports.fullTextDerivedIndexReadinessId = fullTextDerivedIndexReadinessId;
|
|
11
|
+
exports.fullTextDerivedIndexReadiness = fullTextDerivedIndexReadiness;
|
|
12
|
+
exports.setFullTextNativeBindingForTests = setFullTextNativeBindingForTests;
|
|
13
|
+
const hdbError_ts_1 = require("../utility/errors/hdbError.js");
|
|
14
|
+
const logger_ts_1 = require("../utility/logging/logger.js");
|
|
15
|
+
const rocksdb_js_1 = require("@harperfast/rocksdb-js");
|
|
16
|
+
const derivedIndexRegistry_ts_1 = require("./derivedIndexRegistry.js");
|
|
17
|
+
const derivedIndexRuntime_ts_1 = require("./derivedIndexRuntime.js");
|
|
18
|
+
const hnswDerivedIndex_ts_1 = require("./indexes/hnswDerivedIndex.js");
|
|
19
|
+
const nativeFullTextDerivedIndexLifecycle_ts_1 = require("./indexes/nativeFullTextDerivedIndexLifecycle.js");
|
|
20
|
+
const fullTextSchema_ts_1 = require("./fullTextSchema.js");
|
|
21
|
+
const logger = (0, logger_ts_1.loggerWithTag)('HNSW');
|
|
22
|
+
const fullTextLogger = (0, logger_ts_1.loggerWithTag)('fulltext-derived-index');
|
|
23
|
+
const derivedIndexLogger = (0, logger_ts_1.loggerWithTag)('derived-index');
|
|
24
|
+
const DEFAULT_MAX_LAG_MILLISECONDS = 30_000;
|
|
25
|
+
const DEFAULT_FULL_TEXT_RETIREMENT_RETRY_MILLISECONDS = 70_000;
|
|
26
|
+
const FULL_TEXT_LIMITS = Object.freeze({
|
|
27
|
+
indexingThreads: 1,
|
|
28
|
+
searchThreads: 1,
|
|
29
|
+
writerMemoryBytes: 32 * 1024 * 1024,
|
|
30
|
+
maxQueuedCommands: 16,
|
|
31
|
+
maxQueuedBytes: 64 * 1024 * 1024,
|
|
32
|
+
maxBatchBytes: 8 * 1024 * 1024,
|
|
33
|
+
});
|
|
34
|
+
const runtimes = new WeakMap();
|
|
35
|
+
const suspendedActivation = new WeakMap();
|
|
36
|
+
const PERMANENTLY_SUSPENDED = -1;
|
|
37
|
+
const retryUnavailableByStore = new WeakMap();
|
|
38
|
+
let fullTextTestConfiguration;
|
|
39
|
+
/** Prevent new derived-index attachments while a root store is being torn down. */
|
|
40
|
+
function suspendDerivedIndexActivation(rootStore) {
|
|
41
|
+
const current = suspendedActivation.get(rootStore) ?? 0;
|
|
42
|
+
if (current !== PERMANENTLY_SUSPENDED)
|
|
43
|
+
suspendedActivation.set(rootStore, current + 1);
|
|
44
|
+
let released = false;
|
|
45
|
+
return () => {
|
|
46
|
+
if (released)
|
|
47
|
+
return;
|
|
48
|
+
released = true;
|
|
49
|
+
const current = suspendedActivation.get(rootStore);
|
|
50
|
+
if (current === PERMANENTLY_SUSPENDED)
|
|
51
|
+
return;
|
|
52
|
+
const remaining = (current ?? 1) - 1;
|
|
53
|
+
if (remaining > 0)
|
|
54
|
+
suspendedActivation.set(rootStore, remaining);
|
|
55
|
+
else
|
|
56
|
+
suspendedActivation.delete(rootStore);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Prevent abandoned root wrappers from reattaching derived indexes after a failed native close. */
|
|
60
|
+
function permanentlySuspendDerivedIndexActivation(rootStore) {
|
|
61
|
+
suspendedActivation.set(rootStore, PERMANENTLY_SUSPENDED);
|
|
62
|
+
}
|
|
63
|
+
function activationSuspended(Table) {
|
|
64
|
+
const rootStore = Table.primaryStore?.rootStore;
|
|
65
|
+
// Closed roots no longer retain an active suspension count, but stale table classes must not
|
|
66
|
+
// resurrect their derived-index runtime after successful teardown.
|
|
67
|
+
return rootStore != null && (rootStore.status === 'closed' || suspendedActivation.has(rootStore));
|
|
68
|
+
}
|
|
69
|
+
function markUnavailableRetry(registered, auditStore, backendId) {
|
|
70
|
+
registered.retryUnavailable.add(backendId);
|
|
71
|
+
const rootStore = auditStore.rootStore;
|
|
72
|
+
let retries = retryUnavailableByStore.get(rootStore);
|
|
73
|
+
if (!retries)
|
|
74
|
+
retryUnavailableByStore.set(rootStore, (retries = new Set()));
|
|
75
|
+
retries.add(backendId);
|
|
76
|
+
}
|
|
77
|
+
function consumeUnavailableRetry(registered, auditStore, backendId) {
|
|
78
|
+
const local = registered.retryUnavailable.delete(backendId);
|
|
79
|
+
const rootStore = auditStore.rootStore;
|
|
80
|
+
const retries = retryUnavailableByStore.get(rootStore);
|
|
81
|
+
const storeScoped = retries?.delete(backendId) ?? false;
|
|
82
|
+
if (retries?.size === 0)
|
|
83
|
+
retryUnavailableByStore.delete(rootStore);
|
|
84
|
+
return local || storeScoped;
|
|
85
|
+
}
|
|
86
|
+
function runtimeFor(auditStore) {
|
|
87
|
+
let registered = runtimes.get(auditStore);
|
|
88
|
+
if (registered)
|
|
89
|
+
return registered;
|
|
90
|
+
const tables = new Map();
|
|
91
|
+
const runtime = new derivedIndexRuntime_ts_1.DerivedIndexRuntime(auditStore, (tableId, recordId) => {
|
|
92
|
+
const entry = tables.get(tableId)?.current.Table.primaryStore.getEntry(recordId);
|
|
93
|
+
return entry?.value == null ? undefined : { version: entry.version, value: entry.value, size: entry.size };
|
|
94
|
+
}, {
|
|
95
|
+
scanRecords: (tableId) => {
|
|
96
|
+
const Table = tables.get(tableId)?.current.Table;
|
|
97
|
+
if (!Table)
|
|
98
|
+
throw new Error(`Derived-index table ${tableId} is no longer registered`);
|
|
99
|
+
return Table.primaryStore
|
|
100
|
+
.getRange({ versions: true, snapshot: false })
|
|
101
|
+
.map(({ key, value, version, size }) => ({ recordId: key, version, value, size }));
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
registered = {
|
|
105
|
+
runtime,
|
|
106
|
+
tables,
|
|
107
|
+
backends: new Map(),
|
|
108
|
+
tableBackends: new Map(),
|
|
109
|
+
droppingTables: new Set(),
|
|
110
|
+
retryUnavailable: new Set(),
|
|
111
|
+
};
|
|
112
|
+
runtimes.set(auditStore, registered);
|
|
113
|
+
return registered;
|
|
114
|
+
}
|
|
115
|
+
function beginBackendHandoff(registered, id) {
|
|
116
|
+
let predecessor = registered.backends.get(id);
|
|
117
|
+
const inherited = predecessor;
|
|
118
|
+
const settlePredecessor = () => {
|
|
119
|
+
const current = predecessor;
|
|
120
|
+
if (!current)
|
|
121
|
+
return Promise.resolve();
|
|
122
|
+
const settled = current.settle();
|
|
123
|
+
settled.then(() => {
|
|
124
|
+
if (predecessor === current)
|
|
125
|
+
predecessor = undefined;
|
|
126
|
+
}, () => { });
|
|
127
|
+
return settled;
|
|
128
|
+
};
|
|
129
|
+
const predecessorSettled = settlePredecessor();
|
|
130
|
+
predecessorSettled.catch(() => { });
|
|
131
|
+
if (registered.backends.get(id) === inherited)
|
|
132
|
+
registered.backends.delete(id);
|
|
133
|
+
return { settlePredecessor, predecessorSettled };
|
|
134
|
+
}
|
|
135
|
+
function registerBackend(registered, tableId, id, label, settlePredecessor, settleCurrent) {
|
|
136
|
+
let settling;
|
|
137
|
+
const registeredBackend = {
|
|
138
|
+
settle: () => {
|
|
139
|
+
if (settling)
|
|
140
|
+
return settling;
|
|
141
|
+
const attempt = Promise.allSettled([settlePredecessor(), settleCurrent()]).then((results) => {
|
|
142
|
+
if (results.every((result) => result.status === 'fulfilled')) {
|
|
143
|
+
if (registered.backends.get(id) === registeredBackend)
|
|
144
|
+
registered.backends.delete(id);
|
|
145
|
+
const tableBackends = registered.tableBackends.get(tableId);
|
|
146
|
+
tableBackends?.delete(registeredBackend);
|
|
147
|
+
if (tableBackends?.size === 0)
|
|
148
|
+
registered.tableBackends.delete(tableId);
|
|
149
|
+
}
|
|
150
|
+
const failures = results
|
|
151
|
+
.filter((result) => result.status === 'rejected')
|
|
152
|
+
.map((result) => result.reason);
|
|
153
|
+
if (failures.length === 1)
|
|
154
|
+
throw failures[0];
|
|
155
|
+
if (failures.length)
|
|
156
|
+
throw new AggregateError(failures, `Could not settle ${label} backend '${id}'`);
|
|
157
|
+
});
|
|
158
|
+
const retryable = attempt.catch((error) => {
|
|
159
|
+
if (settling === retryable)
|
|
160
|
+
settling = undefined;
|
|
161
|
+
throw error;
|
|
162
|
+
});
|
|
163
|
+
settling = retryable;
|
|
164
|
+
return retryable;
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
let tableBackends = registered.tableBackends.get(tableId);
|
|
168
|
+
if (!tableBackends)
|
|
169
|
+
registered.tableBackends.set(tableId, (tableBackends = new Set()));
|
|
170
|
+
tableBackends.add(registeredBackend);
|
|
171
|
+
registered.backends.set(id, registeredBackend);
|
|
172
|
+
return registeredBackend;
|
|
173
|
+
}
|
|
174
|
+
const warnedAuditIndexes = new Set();
|
|
175
|
+
function attachDerivedIndexes(Table, options = {}) {
|
|
176
|
+
if (activationSuspended(Table))
|
|
177
|
+
return;
|
|
178
|
+
const hnswAttributes = Table.attributes.filter((attribute) => attribute.indexed?.type === 'HNSW' && Table.indices[attribute.name]?.customIndex);
|
|
179
|
+
const attributes = hnswAttributes.filter((attribute) => Table.indices[attribute.name]?.customIndex?.postCommit);
|
|
180
|
+
const fullTextDefinitions = (Table.fullTextIndexes ?? []);
|
|
181
|
+
const fullTextRetirementNames = Table.primaryStore?.rootStore instanceof rocksdb_js_1.RocksDatabase
|
|
182
|
+
? [...new Set((Table.fullTextIndexRetirements ?? []))].sort()
|
|
183
|
+
: [];
|
|
184
|
+
if (Table.audit !== true && (attributes.length > 0 || fullTextDefinitions.length > 0)) {
|
|
185
|
+
throw new hdbError_ts_1.ClientError(`Table '${Table.databaseName}.${Table.tableName}' must enable audit logging before using a post-commit derived index`);
|
|
186
|
+
}
|
|
187
|
+
assertFullTextActivationSupported(Table.primaryStore?.rootStore, Table.databaseName, Table.tableName, fullTextDefinitions);
|
|
188
|
+
if (!Table.auditStore) {
|
|
189
|
+
if (fullTextRetirementNames.length > 0)
|
|
190
|
+
return attachFullTextRetirementRecovery(Table, fullTextRetirementNames);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const auditStore = Table.auditStore;
|
|
194
|
+
const registered = runtimeFor(auditStore);
|
|
195
|
+
const fullTextTest = fullTextTestConfiguration;
|
|
196
|
+
if (registered.droppingTables.has(Table.tableId))
|
|
197
|
+
return;
|
|
198
|
+
for (const attribute of hnswAttributes) {
|
|
199
|
+
const indexStore = Table.indices[attribute.name];
|
|
200
|
+
const id = `hnsw:${indexStore.name}`;
|
|
201
|
+
// A transient JS-backed declaration can precede the first native registration while a
|
|
202
|
+
// schema is being installed. Only an explicit transition away from an already-registered
|
|
203
|
+
// native backend should make a later native registration eligible to rearm an exhausted
|
|
204
|
+
// rebuild budget.
|
|
205
|
+
if (attribute.indexed.nativePlane === false && !indexStore.customIndex.postCommit && registered.backends.has(id))
|
|
206
|
+
markUnavailableRetry(registered, auditStore, id);
|
|
207
|
+
}
|
|
208
|
+
if (attributes.length === 0 && fullTextDefinitions.length === 0 && fullTextRetirementNames.length === 0)
|
|
209
|
+
return;
|
|
210
|
+
if (attributes.length === 0 &&
|
|
211
|
+
fullTextDefinitions.length === 0 &&
|
|
212
|
+
(registered.tableBackends.get(Table.tableId)?.size ?? 0) > 0)
|
|
213
|
+
return;
|
|
214
|
+
const installed = { Table };
|
|
215
|
+
let registeredTable = registered.tables.get(Table.tableId);
|
|
216
|
+
if (registeredTable) {
|
|
217
|
+
registeredTable.owners.add(installed);
|
|
218
|
+
registeredTable.current = installed;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
registeredTable = { current: installed, owners: new Set([installed]) };
|
|
222
|
+
registered.tables.set(Table.tableId, registeredTable);
|
|
223
|
+
}
|
|
224
|
+
const releases = [];
|
|
225
|
+
const hnswRetryBackendIds = [];
|
|
226
|
+
const registeredBackends = new Map();
|
|
227
|
+
const fullTextSetups = new Map();
|
|
228
|
+
let closing = false;
|
|
229
|
+
let registrationReleased = false;
|
|
230
|
+
let closeOperation;
|
|
231
|
+
const fullTextSnapshot = fullTextActivationSnapshot(Table, fullTextDefinitions);
|
|
232
|
+
let fullTextRetirementSnapshot = JSON.stringify(fullTextRetirementNames);
|
|
233
|
+
let fullTextRetirementRecoveryFailed = false;
|
|
234
|
+
const persistedRetirementOperation = resumePersistedFullTextRetirements(Table, fullTextRetirementNames, () => !closing && registered.tables.get(Table.tableId)?.current === installed);
|
|
235
|
+
const persistedRetirementSettlement = persistedRetirementOperation.then((completed) => {
|
|
236
|
+
if (completed)
|
|
237
|
+
fullTextRetirementSnapshot = JSON.stringify(Table.fullTextIndexRetirements ?? []);
|
|
238
|
+
else
|
|
239
|
+
fullTextRetirementRecoveryFailed = true;
|
|
240
|
+
}, (error) => {
|
|
241
|
+
fullTextRetirementRecoveryFailed = true;
|
|
242
|
+
fullTextLogger.warn?.(`Could not resume full-text retirement for ${Table.databaseName}.${Table.tableName}`, error);
|
|
243
|
+
});
|
|
244
|
+
const hnswSnapshot = attributes.map((attribute) => ({
|
|
245
|
+
name: attribute.name,
|
|
246
|
+
index: Table.indices[attribute.name]?.customIndex,
|
|
247
|
+
computedFrom: attribute.computed?.from,
|
|
248
|
+
computedFromExpression: attribute.computedFromExpression,
|
|
249
|
+
userResolver: attribute.computed && !attribute.computedFromExpression ? Table.userResolvers?.[attribute.name] : undefined,
|
|
250
|
+
}));
|
|
251
|
+
const releaseFullTextRegistration = fullTextDefinitions.length > 0 ? (0, derivedIndexRegistry_ts_1.registerDerivedIndexTables)(auditStore, [Table.tableId]) : undefined;
|
|
252
|
+
const releaseRegistration = () => {
|
|
253
|
+
if (registrationReleased)
|
|
254
|
+
return;
|
|
255
|
+
registrationReleased = true;
|
|
256
|
+
releaseFullTextRegistration?.();
|
|
257
|
+
};
|
|
258
|
+
for (const attribute of attributes) {
|
|
259
|
+
const indexStore = Table.indices[attribute.name];
|
|
260
|
+
const index = indexStore.customIndex;
|
|
261
|
+
const id = `hnsw:${indexStore.name}`;
|
|
262
|
+
const warningKey = `${Table.databaseName}.${Table.tableName}.${indexStore.name}`;
|
|
263
|
+
if (!warnedAuditIndexes.has(warningKey)) {
|
|
264
|
+
warnedAuditIndexes.add(warningKey);
|
|
265
|
+
logger.warn?.(`Derived index ${indexStore.name} requires auditing; the audit API retains full record history for the configured retention window`);
|
|
266
|
+
}
|
|
267
|
+
const resolver = Table.propertyResolvers?.[attribute.name];
|
|
268
|
+
const label = `Vector for attribute "${attribute.name}"`;
|
|
269
|
+
index.attachDerivedHost({
|
|
270
|
+
readiness: () => registered.runtime.getReadiness(id),
|
|
271
|
+
coverage: (maxLagMilliseconds) => (0, derivedIndexRuntime_ts_1.readDerivedIndexCoverage)(auditStore, id, () => indexStore.getSync(hnswDerivedIndex_ts_1.DERIVED_INDEX_CURSOR_KEY), maxLagMilliseconds),
|
|
272
|
+
requestRebuild: () => registered.runtime.requestRebuild(id),
|
|
273
|
+
waitForCoverage: (since, timeout, signal) => registered.runtime.waitForCoverage(id, since, timeout, signal),
|
|
274
|
+
});
|
|
275
|
+
const { settlePredecessor } = beginBackendHandoff(registered, id);
|
|
276
|
+
const release = registered.runtime.register({
|
|
277
|
+
backend: new hnswDerivedIndex_ts_1.HnswDerivedIndexBackend(id, index),
|
|
278
|
+
projections: new Map([
|
|
279
|
+
[
|
|
280
|
+
Table.tableId,
|
|
281
|
+
(record) => {
|
|
282
|
+
const vector = resolver ? resolver(record) : record[attribute.name];
|
|
283
|
+
if (vector == null)
|
|
284
|
+
return undefined;
|
|
285
|
+
index.assertDerivedValue(vector, label);
|
|
286
|
+
return vector;
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
]),
|
|
290
|
+
options: { maxLagMilliseconds: attribute.indexed?.maxLagMilliseconds ?? DEFAULT_MAX_LAG_MILLISECONDS },
|
|
291
|
+
});
|
|
292
|
+
const registeredBackend = registerBackend(registered, Table.tableId, id, 'HNSW', settlePredecessor, release);
|
|
293
|
+
registeredBackends.set(id, registeredBackend);
|
|
294
|
+
const retryUnavailable = consumeUnavailableRetry(registered, auditStore, id);
|
|
295
|
+
if (retryUnavailable && registered.runtime.getReadiness(id).state === 'unavailable')
|
|
296
|
+
registered.runtime.requestRebuild(id);
|
|
297
|
+
hnswRetryBackendIds.push(id);
|
|
298
|
+
releases.push(() => registeredBackend.settle());
|
|
299
|
+
}
|
|
300
|
+
for (const definition of fullTextDefinitions)
|
|
301
|
+
startFullTextBackend(definition, options.retryUnavailableReadiness);
|
|
302
|
+
function startFullTextBackend(definition, retryUnavailableReadiness = false) {
|
|
303
|
+
if (fullTextSetups.has(definition.name))
|
|
304
|
+
return;
|
|
305
|
+
const generation = Table.fullTextIndexGenerations?.[definition.name];
|
|
306
|
+
const id = fullTextDerivedIndexId(Table, definition.name);
|
|
307
|
+
const readinessId = fullTextDerivedIndexReadinessId(Table, definition.name, generation);
|
|
308
|
+
if (typeof generation !== 'string' || generation.length === 0) {
|
|
309
|
+
(0, derivedIndexRuntime_ts_1.publishDerivedIndexReadiness)(auditStore, readinessId, 'unavailable', 'backend-failed');
|
|
310
|
+
fullTextLogger.error?.(`Full-text index ${Table.databaseName}.${Table.tableName}.${definition.name} has no durable generation; reapply its schema to activate it`);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const { settlePredecessor, predecessorSettled } = beginBackendHandoff(registered, id);
|
|
314
|
+
const quiescePredecessor = async () => {
|
|
315
|
+
try {
|
|
316
|
+
await predecessorSettled;
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
if (!registered.runtime.requestRebuild(id))
|
|
320
|
+
throw error;
|
|
321
|
+
await settlePredecessor();
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
let cancelled = false;
|
|
325
|
+
let release;
|
|
326
|
+
let setup;
|
|
327
|
+
const registeredBackend = registerBackend(registered, Table.tableId, id, 'full-text', settlePredecessor, () => {
|
|
328
|
+
cancelled = true;
|
|
329
|
+
return setup.then(() => release?.());
|
|
330
|
+
});
|
|
331
|
+
registeredBackends.set(id, registeredBackend);
|
|
332
|
+
releases.push(() => registeredBackend.settle());
|
|
333
|
+
const isCurrent = () => !closing &&
|
|
334
|
+
!cancelled &&
|
|
335
|
+
registered.tables.get(Table.tableId)?.current === installed &&
|
|
336
|
+
Table.fullTextIndexGenerations?.[definition.name] === generation;
|
|
337
|
+
setup = (async () => {
|
|
338
|
+
await quiescePredecessor();
|
|
339
|
+
if (!isCurrent())
|
|
340
|
+
return;
|
|
341
|
+
if (!(await (0, derivedIndexRegistry_ts_1.waitForFullTextRetirement)(Table.primaryStore.rootStore, Table.tableName, {
|
|
342
|
+
shouldContinue: isCurrent,
|
|
343
|
+
})))
|
|
344
|
+
return;
|
|
345
|
+
if (!isCurrent())
|
|
346
|
+
return;
|
|
347
|
+
if (retryUnavailableReadiness)
|
|
348
|
+
(0, derivedIndexRuntime_ts_1.retryDerivedIndexUnavailable)(auditStore, readinessId);
|
|
349
|
+
const storage = (0, fullTextSchema_ts_1.fullTextStorageDefinition)(definition);
|
|
350
|
+
const storeName = `${Table.tableName}/${definition.name}`;
|
|
351
|
+
const warningKey = `${Table.primaryStore.rootStore.path}:${storeName}`;
|
|
352
|
+
if (!warnedAuditIndexes.has(warningKey)) {
|
|
353
|
+
warnedAuditIndexes.add(warningKey);
|
|
354
|
+
fullTextLogger.warn?.(`Derived index ${storeName} requires auditing; the audit API retains full record history for the configured retention window`);
|
|
355
|
+
}
|
|
356
|
+
const backend = await (0, nativeFullTextDerivedIndexLifecycle_ts_1.createNativeFullTextDerivedIndexBackend)({
|
|
357
|
+
id,
|
|
358
|
+
storePath: Table.primaryStore.rootStore.path,
|
|
359
|
+
storeName,
|
|
360
|
+
sourceGeneration: `${Table.tableId}:${generation}`,
|
|
361
|
+
fields: storage.fields,
|
|
362
|
+
analyzer: storage.analyzer,
|
|
363
|
+
stopWords: storage.stopWords,
|
|
364
|
+
positions: storage.positions,
|
|
365
|
+
surfaceTerms: storage.surfaceTerms,
|
|
366
|
+
limits: { ...FULL_TEXT_LIMITS },
|
|
367
|
+
...(fullTextTest
|
|
368
|
+
? {
|
|
369
|
+
binding: fullTextTest.binding,
|
|
370
|
+
closeTimeoutMilliseconds: fullTextTest.closeTimeoutMilliseconds,
|
|
371
|
+
shutdownTimeoutMilliseconds: fullTextTest.shutdownTimeoutMilliseconds,
|
|
372
|
+
}
|
|
373
|
+
: {}),
|
|
374
|
+
});
|
|
375
|
+
if (!isCurrent())
|
|
376
|
+
return;
|
|
377
|
+
release = registered.runtime.register({
|
|
378
|
+
backend,
|
|
379
|
+
readinessId,
|
|
380
|
+
isCurrent,
|
|
381
|
+
projections: new Map([[Table.tableId, fullTextProjection(definition)]]),
|
|
382
|
+
options: {
|
|
383
|
+
maxLagMilliseconds: DEFAULT_MAX_LAG_MILLISECONDS,
|
|
384
|
+
...fullTextTest?.runnerOptions,
|
|
385
|
+
},
|
|
386
|
+
});
|
|
387
|
+
})().catch((error) => {
|
|
388
|
+
if (!isCurrent())
|
|
389
|
+
return;
|
|
390
|
+
// Setup runs on every worker, but readiness is shared. A local setup failure must not
|
|
391
|
+
// replace a state already published by the elected runner on another worker.
|
|
392
|
+
(0, derivedIndexRuntime_ts_1.publishDerivedIndexUnavailableIfUnknown)(auditStore, readinessId);
|
|
393
|
+
fullTextLogger.error?.(`Could not activate full-text index ${Table.databaseName}.${Table.tableName}.${definition.name}`, error);
|
|
394
|
+
});
|
|
395
|
+
fullTextSetups.set(definition.name, setup);
|
|
396
|
+
void setup.finally(() => {
|
|
397
|
+
if (fullTextSetups.get(definition.name) === setup)
|
|
398
|
+
fullTextSetups.delete(definition.name);
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
return {
|
|
402
|
+
fullTextDefinitions() {
|
|
403
|
+
return fullTextDefinitions;
|
|
404
|
+
},
|
|
405
|
+
async close(dropping = false) {
|
|
406
|
+
if (dropping)
|
|
407
|
+
registered.droppingTables.add(Table.tableId);
|
|
408
|
+
if (closeOperation)
|
|
409
|
+
return closeOperation;
|
|
410
|
+
closing = true;
|
|
411
|
+
const settlements = dropping
|
|
412
|
+
? [...(registered.tableBackends.get(Table.tableId) ?? [])].map((backend) => backend.settle())
|
|
413
|
+
: releases.map((release) => release());
|
|
414
|
+
const retained = new Set((Table.fullTextIndexes ?? []).map((definition) => definition.name));
|
|
415
|
+
const removedDefinitions = dropping
|
|
416
|
+
? []
|
|
417
|
+
: fullTextDefinitions.filter((definition) => !retained.has(definition.name));
|
|
418
|
+
let releaseRetirementFence;
|
|
419
|
+
let waitForPeerRetirement;
|
|
420
|
+
if (removedDefinitions.length > 0) {
|
|
421
|
+
releaseRetirementFence = (0, derivedIndexRegistry_ts_1.acquireFullTextRetirementFence)(Table.primaryStore.rootStore, Table.tableName);
|
|
422
|
+
if (!releaseRetirementFence)
|
|
423
|
+
waitForPeerRetirement = (0, derivedIndexRegistry_ts_1.waitForFullTextRetirement)(Table.primaryStore.rootStore, Table.tableName, {
|
|
424
|
+
shouldContinue: () => Table.primaryStore.rootStore.status === 'open',
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
const tableRegistration = registered.tables.get(Table.tableId);
|
|
428
|
+
if (tableRegistration) {
|
|
429
|
+
tableRegistration.owners.delete(installed);
|
|
430
|
+
if (tableRegistration.owners.size === 0) {
|
|
431
|
+
registered.tables.delete(Table.tableId);
|
|
432
|
+
}
|
|
433
|
+
else if (tableRegistration.current === installed) {
|
|
434
|
+
tableRegistration.current = tableRegistration.owners.values().next().value;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
const operation = (async () => {
|
|
438
|
+
try {
|
|
439
|
+
if (dropping) {
|
|
440
|
+
await Promise.all(settlements);
|
|
441
|
+
await persistedRetirementSettlement;
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
const results = await Promise.allSettled(settlements);
|
|
445
|
+
const failures = [];
|
|
446
|
+
for (const result of results) {
|
|
447
|
+
if (result.status === 'rejected') {
|
|
448
|
+
failures.push(result.reason);
|
|
449
|
+
derivedIndexLogger.error(`Could not settle a superseded derived-index backend for table ${Table.tableId}`, result.reason);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (failures.length === 1)
|
|
453
|
+
throw failures[0];
|
|
454
|
+
if (failures.length)
|
|
455
|
+
throw new AggregateError(failures, `Could not settle derived indexes for table ${Table.tableId}`);
|
|
456
|
+
if (releaseRetirementFence) {
|
|
457
|
+
const retired = await retireFullTextIndexes(Table, removedDefinitions);
|
|
458
|
+
if (retired)
|
|
459
|
+
await Table.completeFullTextIndexRetirements?.(removedDefinitions.map(({ name }) => name));
|
|
460
|
+
}
|
|
461
|
+
else if (waitForPeerRetirement)
|
|
462
|
+
await waitForPeerRetirement;
|
|
463
|
+
await persistedRetirementSettlement;
|
|
464
|
+
releaseRegistration();
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
finally {
|
|
468
|
+
releaseRetirementFence?.();
|
|
469
|
+
}
|
|
470
|
+
})();
|
|
471
|
+
const retryable = operation.catch((error) => {
|
|
472
|
+
if (closeOperation === retryable)
|
|
473
|
+
closeOperation = undefined;
|
|
474
|
+
throw error;
|
|
475
|
+
});
|
|
476
|
+
closeOperation = retryable;
|
|
477
|
+
return closeOperation;
|
|
478
|
+
},
|
|
479
|
+
matchesCurrent() {
|
|
480
|
+
if (closing || fullTextRetirementRecoveryFailed || !matchesCurrentHnsw())
|
|
481
|
+
return false;
|
|
482
|
+
const currentDefinitions = (Table.fullTextIndexes ?? []);
|
|
483
|
+
return (fullTextActivationSnapshot(Table, currentDefinitions) === fullTextSnapshot &&
|
|
484
|
+
JSON.stringify(Table.fullTextIndexRetirements ?? []) === fullTextRetirementSnapshot);
|
|
485
|
+
},
|
|
486
|
+
retryUnavailableFullText() {
|
|
487
|
+
if (closing)
|
|
488
|
+
return;
|
|
489
|
+
for (const definition of fullTextDefinitions) {
|
|
490
|
+
const generation = Table.fullTextIndexGenerations?.[definition.name];
|
|
491
|
+
if ((0, derivedIndexRuntime_ts_1.readDerivedIndexReadiness)(auditStore, fullTextDerivedIndexReadinessId(Table, definition.name, generation))
|
|
492
|
+
.state === 'unavailable')
|
|
493
|
+
startFullTextBackend(definition, true);
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
restoreAfterFailedDrop() {
|
|
497
|
+
registered.droppingTables.delete(Table.tableId);
|
|
498
|
+
const currentTable = registered.tables.get(Table.tableId)?.current.Table;
|
|
499
|
+
if (currentTable && currentTable !== Table) {
|
|
500
|
+
releaseRegistration();
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
for (const registration of registered.tables.values()) {
|
|
504
|
+
const current = registration.current.Table;
|
|
505
|
+
if (current !== Table && current.databasePath === Table.databasePath && current.tableName === Table.tableName) {
|
|
506
|
+
releaseRegistration();
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
for (const [id, registeredBackend] of registeredBackends) {
|
|
511
|
+
const current = registered.backends.get(id);
|
|
512
|
+
if (current && current !== registeredBackend) {
|
|
513
|
+
releaseRegistration();
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
const restored = attachDerivedIndexes(Table, { retryUnavailableReadiness: true });
|
|
518
|
+
if (restored)
|
|
519
|
+
releaseRegistration();
|
|
520
|
+
return restored;
|
|
521
|
+
},
|
|
522
|
+
retireAfterConfirmedDrop(definitions = fullTextDefinitions) {
|
|
523
|
+
return retireFullTextIndexes(Table, definitions);
|
|
524
|
+
},
|
|
525
|
+
completeDrop(dropped = true) {
|
|
526
|
+
releaseRegistration();
|
|
527
|
+
registered.droppingTables.delete(Table.tableId);
|
|
528
|
+
if (dropped)
|
|
529
|
+
for (const id of hnswRetryBackendIds)
|
|
530
|
+
markUnavailableRetry(registered, auditStore, id);
|
|
531
|
+
},
|
|
532
|
+
};
|
|
533
|
+
function matchesCurrentHnsw() {
|
|
534
|
+
const current = Table.attributes.filter((attribute) => attribute.indexed?.type === 'HNSW' && Table.indices[attribute.name]?.customIndex?.postCommit);
|
|
535
|
+
if (current.length !== hnswSnapshot.length)
|
|
536
|
+
return false;
|
|
537
|
+
return hnswSnapshot.every(({ name, index, computedFrom, computedFromExpression, userResolver }) => {
|
|
538
|
+
const indexStore = Table.indices[name];
|
|
539
|
+
const attribute = current.find((candidate) => candidate.name === name);
|
|
540
|
+
return (indexStore?.customIndex === index &&
|
|
541
|
+
Object.is(attribute?.computed?.from, computedFrom) &&
|
|
542
|
+
attribute?.computedFromExpression === computedFromExpression &&
|
|
543
|
+
(attribute?.computed && !attribute.computedFromExpression ? Table.userResolvers?.[name] : undefined) ===
|
|
544
|
+
userResolver);
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
/** Retire durable native indexes even when their in-memory attachment could not be restored. */
|
|
549
|
+
async function retireFullTextIndexes(Table, definitions, shouldContinue = () => true) {
|
|
550
|
+
const fullTextTest = fullTextTestConfiguration;
|
|
551
|
+
const retryMilliseconds = fullTextTest?.shutdownTimeoutMilliseconds ?? DEFAULT_FULL_TEXT_RETIREMENT_RETRY_MILLISECONDS;
|
|
552
|
+
const deadline = Date.now() + retryMilliseconds;
|
|
553
|
+
let retired = true;
|
|
554
|
+
for (const definition of definitions) {
|
|
555
|
+
if (!shouldContinue())
|
|
556
|
+
return false;
|
|
557
|
+
let retryDelayMilliseconds = 10;
|
|
558
|
+
let retrying = false;
|
|
559
|
+
for (;;) {
|
|
560
|
+
try {
|
|
561
|
+
await (0, nativeFullTextDerivedIndexLifecycle_ts_1.retireNativeFullTextDerivedIndexStorage)({
|
|
562
|
+
storePath: Table.primaryStore.rootStore.path,
|
|
563
|
+
storeName: `${Table.tableName}/${definition.name}`,
|
|
564
|
+
indexId: fullTextDerivedIndexId(Table, definition.name),
|
|
565
|
+
...(fullTextTest ? { binding: fullTextTest.binding } : {}),
|
|
566
|
+
});
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
catch (error) {
|
|
570
|
+
if (!shouldContinue())
|
|
571
|
+
return false;
|
|
572
|
+
const code = error && typeof error === 'object' && 'code' in error && typeof error.code === 'string'
|
|
573
|
+
? error.code
|
|
574
|
+
: undefined;
|
|
575
|
+
const remaining = deadline - Date.now();
|
|
576
|
+
if (code === 'E_LOCK_BUSY' && remaining > 0) {
|
|
577
|
+
if (!retrying) {
|
|
578
|
+
retrying = true;
|
|
579
|
+
fullTextLogger.warn?.(`Waiting up to ${retryMilliseconds}ms to retire full-text index ${Table.databaseName}.${Table.tableName}.${definition.name}`);
|
|
580
|
+
}
|
|
581
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(retryDelayMilliseconds, remaining)));
|
|
582
|
+
if (!shouldContinue())
|
|
583
|
+
return false;
|
|
584
|
+
retryDelayMilliseconds = Math.min(retryDelayMilliseconds * 2, 250);
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
fullTextLogger.warn?.(`Could not retire full-text index ${Table.databaseName}.${Table.tableName}.${definition.name}`, error);
|
|
588
|
+
retired = false;
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return retired;
|
|
594
|
+
}
|
|
595
|
+
async function resumePersistedFullTextRetirements(Table, names, shouldContinue) {
|
|
596
|
+
if (names.length === 0)
|
|
597
|
+
return true;
|
|
598
|
+
const rootStore = Table.primaryStore.rootStore;
|
|
599
|
+
const releaseRetirementFence = await (0, derivedIndexRegistry_ts_1.waitForFullTextRetirementLease)(rootStore, Table.tableName, {
|
|
600
|
+
shouldContinue,
|
|
601
|
+
timeoutMilliseconds: DEFAULT_FULL_TEXT_RETIREMENT_RETRY_MILLISECONDS,
|
|
602
|
+
});
|
|
603
|
+
if (!releaseRetirementFence)
|
|
604
|
+
return false;
|
|
605
|
+
try {
|
|
606
|
+
if (!shouldContinue())
|
|
607
|
+
return false;
|
|
608
|
+
if (!(await Table.hasCurrentFullTextIndexRetirements(names)))
|
|
609
|
+
return true;
|
|
610
|
+
const retired = await retireFullTextIndexes(Table, names.map((name) => ({ name })), shouldContinue);
|
|
611
|
+
if (!retired || !shouldContinue())
|
|
612
|
+
return false;
|
|
613
|
+
await Table.completeFullTextIndexRetirements?.(names);
|
|
614
|
+
return true;
|
|
615
|
+
}
|
|
616
|
+
finally {
|
|
617
|
+
releaseRetirementFence();
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
function attachFullTextRetirementRecovery(Table, names) {
|
|
621
|
+
let closing = false;
|
|
622
|
+
let settled = false;
|
|
623
|
+
const operation = resumePersistedFullTextRetirements(Table, names, () => !closing);
|
|
624
|
+
const settlement = operation.then(() => {
|
|
625
|
+
settled = true;
|
|
626
|
+
}, (error) => {
|
|
627
|
+
settled = true;
|
|
628
|
+
fullTextLogger.warn?.(`Could not resume full-text retirement for ${Table.databaseName}.${Table.tableName}`, error);
|
|
629
|
+
});
|
|
630
|
+
return {
|
|
631
|
+
async close() {
|
|
632
|
+
closing = true;
|
|
633
|
+
await settlement;
|
|
634
|
+
},
|
|
635
|
+
fullTextDefinitions() {
|
|
636
|
+
return [];
|
|
637
|
+
},
|
|
638
|
+
matchesCurrent() {
|
|
639
|
+
return !closing && !settled && JSON.stringify(Table.fullTextIndexRetirements ?? []) === JSON.stringify(names);
|
|
640
|
+
},
|
|
641
|
+
retryUnavailableFullText() { },
|
|
642
|
+
restoreAfterFailedDrop() {
|
|
643
|
+
return undefined;
|
|
644
|
+
},
|
|
645
|
+
retireAfterConfirmedDrop(definitions = names.map((name) => ({ name }))) {
|
|
646
|
+
return retireFullTextIndexes(Table, definitions);
|
|
647
|
+
},
|
|
648
|
+
completeDrop() { },
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
/** Keep a healthy, generation-identical full-text attachment across routine catalog reloads. */
|
|
652
|
+
function refreshDerivedIndexes(Table) {
|
|
653
|
+
const current = Table.derivedIndexRuntime;
|
|
654
|
+
if (activationSuspended(Table)) {
|
|
655
|
+
const closing = current?.close();
|
|
656
|
+
if (closing)
|
|
657
|
+
void closing.then(() => {
|
|
658
|
+
if (Table.derivedIndexRuntime === current)
|
|
659
|
+
Table.derivedIndexRuntime = undefined;
|
|
660
|
+
}, (error) => {
|
|
661
|
+
derivedIndexLogger.error(`Could not settle suspended derived indexes for table ${Table.tableId}`, error);
|
|
662
|
+
});
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (current?.matchesCurrent?.()) {
|
|
666
|
+
current.retryUnavailableFullText?.();
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
let next;
|
|
670
|
+
const closing = current?.close();
|
|
671
|
+
if (closing)
|
|
672
|
+
void closing.then(() => {
|
|
673
|
+
if (!next && Table.derivedIndexRuntime === current)
|
|
674
|
+
Table.derivedIndexRuntime = undefined;
|
|
675
|
+
}, (error) => {
|
|
676
|
+
derivedIndexLogger.error(`Could not settle replaced derived indexes for table ${Table.tableId}`, error);
|
|
677
|
+
});
|
|
678
|
+
next = attachDerivedIndexes(Table, {
|
|
679
|
+
retryUnavailableReadiness: Boolean(current),
|
|
680
|
+
});
|
|
681
|
+
Table.derivedIndexRuntime = next ?? current;
|
|
682
|
+
}
|
|
683
|
+
function assertFullTextActivationSupported(rootStore, databaseName, tableName, definitions) {
|
|
684
|
+
if (definitions.length > 0 && !(rootStore instanceof rocksdb_js_1.RocksDatabase))
|
|
685
|
+
throw new hdbError_ts_1.ClientError(`Table '${databaseName}.${tableName}' cannot activate @fullText with the LMDB storage engine`, 400);
|
|
686
|
+
}
|
|
687
|
+
function fullTextProjection(definition) {
|
|
688
|
+
return (record) => {
|
|
689
|
+
const projection = Object.create(null);
|
|
690
|
+
for (const { name } of definition.fields) {
|
|
691
|
+
const value = record[name];
|
|
692
|
+
if (typeof value === 'string') {
|
|
693
|
+
projection[name] = value;
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
if (Array.isArray(value)) {
|
|
697
|
+
const copied = [];
|
|
698
|
+
for (const entry of value) {
|
|
699
|
+
if (typeof entry === 'string')
|
|
700
|
+
copied.push(entry);
|
|
701
|
+
else if (entry != null)
|
|
702
|
+
throw new hdbError_ts_1.ClientError(`Full-text source '${name}' contains a non-text array value`, 400);
|
|
703
|
+
}
|
|
704
|
+
projection[name] = copied;
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
if (value != null)
|
|
708
|
+
throw new hdbError_ts_1.ClientError(`Full-text source '${name}' contains a non-text value`, 400);
|
|
709
|
+
}
|
|
710
|
+
return projection;
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
function fullTextActivationSnapshot(Table, definitions) {
|
|
714
|
+
return JSON.stringify(definitions.map((definition) => ({
|
|
715
|
+
storage: (0, fullTextSchema_ts_1.fullTextStorageDefinition)(definition),
|
|
716
|
+
generation: Table.fullTextIndexGenerations?.[definition.name],
|
|
717
|
+
})));
|
|
718
|
+
}
|
|
719
|
+
function fullTextDerivedIndexId(Table, indexName) {
|
|
720
|
+
return `fulltext:${Table.tableName}/${indexName}`;
|
|
721
|
+
}
|
|
722
|
+
function fullTextDerivedIndexReadinessId(Table, indexName, generation) {
|
|
723
|
+
return `${fullTextDerivedIndexId(Table, indexName)}:${Table.tableId}:${generation ?? 'missing'}`;
|
|
724
|
+
}
|
|
725
|
+
function fullTextDerivedIndexReadiness(Table, indexName) {
|
|
726
|
+
if (!Table.fullTextIndexes?.some((definition) => definition.name === indexName))
|
|
727
|
+
throw new hdbError_ts_1.ClientError(`'${indexName}' is not a full-text index`, 400);
|
|
728
|
+
return (0, derivedIndexRuntime_ts_1.readDerivedIndexReadiness)(Table.auditStore, fullTextDerivedIndexReadinessId(Table, indexName, Table.fullTextIndexGenerations?.[indexName]));
|
|
729
|
+
}
|
|
730
|
+
function setFullTextNativeBindingForTests(configuration) {
|
|
731
|
+
fullTextTestConfiguration = configuration;
|
|
732
|
+
}
|
|
733
|
+
//# sourceMappingURL=derivedIndexes.js.map
|