@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
package/dist/resources/Table.js
CHANGED
|
@@ -46,6 +46,7 @@ exports.acquireUpdateAttributesLock = acquireUpdateAttributesLock;
|
|
|
46
46
|
exports.tryUpdateAttributesLock = tryUpdateAttributesLock;
|
|
47
47
|
exports.releaseUpdateAttributesLock = releaseUpdateAttributesLock;
|
|
48
48
|
exports.withUpdateAttributesLock = withUpdateAttributesLock;
|
|
49
|
+
exports.withUpdateAttributesLockNonBlocking = withUpdateAttributesLockNonBlocking;
|
|
49
50
|
exports.ignoreAlreadyDropped = ignoreAlreadyDropped;
|
|
50
51
|
exports.freezeRecord = freezeRecord;
|
|
51
52
|
exports.frozenRecordView = frozenRecordView;
|
|
@@ -67,6 +68,7 @@ const recordLock_ts_1 = require("./recordLock.js");
|
|
|
67
68
|
const nodeName_ts_1 = require("../server/nodeName.js");
|
|
68
69
|
const envMngr = __importStar(require("../utility/environment/environmentManager.js"));
|
|
69
70
|
const transactionBroadcast_ts_1 = require("./transactionBroadcast.js");
|
|
71
|
+
const databaseDropPreparation_ts_1 = require("./databaseDropPreparation.js");
|
|
70
72
|
const hdbError_ts_1 = require("../utility/errors/hdbError.js");
|
|
71
73
|
const signalling = __importStar(require("../utility/signalling.js"));
|
|
72
74
|
const itc_js_1 = require("../server/threads/itc.js");
|
|
@@ -83,6 +85,7 @@ const auditStore_ts_1 = require("./auditStore.js");
|
|
|
83
85
|
const derivedIndexRegistry_ts_1 = require("./derivedIndexRegistry.js");
|
|
84
86
|
const recordLockCoordinator_ts_1 = require("./recordLockCoordinator.js");
|
|
85
87
|
const embedHook_ts_1 = require("./models/embedHook.js");
|
|
88
|
+
const decideHook_ts_1 = require("./models/decideHook.js");
|
|
86
89
|
const common_utils_ts_1 = require("../utility/common_utils.js");
|
|
87
90
|
const RecordEncoder_ts_1 = require("./RecordEncoder.js");
|
|
88
91
|
const write_ts_1 = require("./analytics/write.js");
|
|
@@ -98,6 +101,7 @@ const rocksdb_js_1 = require("@harperfast/rocksdb-js");
|
|
|
98
101
|
const LMDBTransaction_1 = require("./LMDBTransaction");
|
|
99
102
|
const contentTypes_1 = require("../server/serverHelpers/contentTypes");
|
|
100
103
|
const jsonSchemaTypes_ts_1 = require("./jsonSchemaTypes.js");
|
|
104
|
+
const fullTextSchema_ts_1 = require("./fullTextSchema.js");
|
|
101
105
|
const { sortBy } = lodash_1.default;
|
|
102
106
|
const { validateAttribute } = lmdbProcessRows_js_1.default;
|
|
103
107
|
const NULL_WITH_TIMESTAMP = new Uint8Array(9);
|
|
@@ -189,8 +193,28 @@ function tryUpdateAttributesLock(rootStore) {
|
|
|
189
193
|
function releaseUpdateAttributesLock(rootStore) {
|
|
190
194
|
rootStore.unlock(updateAttributesLockKey);
|
|
191
195
|
}
|
|
192
|
-
function
|
|
193
|
-
|
|
196
|
+
async function acquireUpdateAttributesLockAsync(rootStore, scopeDescription, timeout = exports.UPDATE_ATTRIBUTES_LOCK_TIMEOUT) {
|
|
197
|
+
if (rootStore.tryLock(updateAttributesLockKey))
|
|
198
|
+
return;
|
|
199
|
+
const startTime = node_perf_hooks_1.performance.now();
|
|
200
|
+
let waitTime = 1;
|
|
201
|
+
while (!rootStore.tryLock(updateAttributesLockKey)) {
|
|
202
|
+
const elapsed = node_perf_hooks_1.performance.now() - startTime;
|
|
203
|
+
if (elapsed >= timeout) {
|
|
204
|
+
throw new hdbError_ts_1.UpdateAttributesLockTimeoutError(`Timed out after ${Math.round(elapsed)}ms waiting for the exclusive '${UPDATE_ATTRIBUTES_LOCK}' lock on ${scopeDescription}; the lock holder did not release it before the deadline, so this schema/attribute update cannot proceed`);
|
|
205
|
+
}
|
|
206
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(waitTime, timeout - elapsed)));
|
|
207
|
+
if (waitTime < 16)
|
|
208
|
+
waitTime *= 2;
|
|
209
|
+
}
|
|
210
|
+
const waited = node_perf_hooks_1.performance.now() - startTime;
|
|
211
|
+
if (waited >= exports.UPDATE_ATTRIBUTES_LOCK_SLOW_WAIT)
|
|
212
|
+
try {
|
|
213
|
+
logger_ts_1.logger.warn?.(`Acquired the exclusive '${UPDATE_ATTRIBUTES_LOCK}' lock on ${scopeDescription} after waiting ${Math.round(waited)}ms`);
|
|
214
|
+
}
|
|
215
|
+
catch { }
|
|
216
|
+
}
|
|
217
|
+
function runWithUpdateAttributesLock(rootStore, scopeDescription, callback) {
|
|
194
218
|
try {
|
|
195
219
|
const result = callback();
|
|
196
220
|
if (typeof result?.then === 'function') {
|
|
@@ -203,6 +227,15 @@ function withUpdateAttributesLock(rootStore, scopeDescription, callback) {
|
|
|
203
227
|
releaseUpdateAttributesLock(rootStore);
|
|
204
228
|
}
|
|
205
229
|
}
|
|
230
|
+
function withUpdateAttributesLock(rootStore, scopeDescription, callback) {
|
|
231
|
+
acquireUpdateAttributesLock(rootStore, scopeDescription);
|
|
232
|
+
return runWithUpdateAttributesLock(rootStore, scopeDescription, callback);
|
|
233
|
+
}
|
|
234
|
+
function withUpdateAttributesLockNonBlocking(rootStore, scopeDescription, callback) {
|
|
235
|
+
if (rootStore.tryLock(updateAttributesLockKey))
|
|
236
|
+
return runWithUpdateAttributesLock(rootStore, scopeDescription, callback);
|
|
237
|
+
return acquireUpdateAttributesLockAsync(rootStore, scopeDescription).then(() => runWithUpdateAttributesLock(rootStore, scopeDescription, callback));
|
|
238
|
+
}
|
|
206
239
|
// Tolerate a redundant column family drop. Drops are broadcast to every worker
|
|
207
240
|
// thread and each holds its own handle to the same underlying family, so a
|
|
208
241
|
// concurrent worker may already have dropped it; the storage engine reports
|
|
@@ -521,7 +554,9 @@ function isPlainOptions(value) {
|
|
|
521
554
|
});
|
|
522
555
|
function makeTable(options) {
|
|
523
556
|
var _a;
|
|
524
|
-
const { primaryKey, indices, tableId, tableName, primaryStore, databasePath, databaseName, auditStore, schemaDefined, dbisDB: dbisDb, sealed, splitSegments, replicate, description, hidden, cacheControl, isBranch, fullTextIndexes = [], } = options;
|
|
557
|
+
const { primaryKey, indices, tableId, tableName, primaryStore, databasePath, databaseName, auditStore, schemaDefined, dbisDB: dbisDb, sealed, splitSegments, replicate, description, hidden, cacheControl, isBranch, fullTextIndexes = [], fullTextIndexGenerations = Object.create(null), fullTextIndexRetirements = [], } = options;
|
|
558
|
+
const tableRootStore = primaryStore.rootStore;
|
|
559
|
+
const tableRootPath = tableRootStore.path;
|
|
525
560
|
let { expirationMS: expirationMs, evictionMS: evictionMs, audit, trackDeletes } = options;
|
|
526
561
|
// Set when the TTL exists only on this thread: either application code configured it at runtime, or
|
|
527
562
|
// an isolated application's schema was declared here. Hydrating persisted metadata does not set it:
|
|
@@ -548,6 +583,9 @@ function makeTable(options) {
|
|
|
548
583
|
let hasSourceGet;
|
|
549
584
|
let primaryKeyAttribute;
|
|
550
585
|
let lastEvictionCompletion = Promise.resolve();
|
|
586
|
+
let recordExpirationCompletion = Promise.resolve();
|
|
587
|
+
const maintenanceCommits = new Set();
|
|
588
|
+
let maintenanceClosed = false;
|
|
551
589
|
let droppingTable = false;
|
|
552
590
|
let createdTimeProperty, updatedTimeProperty, expiresAtProperty;
|
|
553
591
|
for (const attribute of attributes) {
|
|
@@ -761,9 +799,10 @@ function makeTable(options) {
|
|
|
761
799
|
}
|
|
762
800
|
return { txnLogKey: version, nodeId };
|
|
763
801
|
}
|
|
764
|
-
//
|
|
765
|
-
// dropping one would advance the source cursor past a write that never landed.
|
|
802
|
+
// Teardown rejects every producer; source/replay paths only bypass derived-index lag shedding.
|
|
766
803
|
function assertDerivedIndexAdmission(options, transaction) {
|
|
804
|
+
if ((0, databaseDropPreparation_ts_1.databaseDropPrepared)(tableRootPath) || (0, DatabaseTransaction_ts_1.databaseCommitsSuspended)(tableRootStore))
|
|
805
|
+
throw new hdbError_ts_1.DatabaseClosingError(databaseName, !transaction?.root && !transaction?.snapshotFree);
|
|
767
806
|
if (options?.isNotification || transaction?.sourceApply || transaction?.isReplay)
|
|
768
807
|
return;
|
|
769
808
|
const reason = (0, derivedIndexRegistry_ts_1.derivedIndexWriteRejection)(auditStore, tableId);
|
|
@@ -788,6 +827,17 @@ function makeTable(options) {
|
|
|
788
827
|
error.statusCode = 404;
|
|
789
828
|
throw error;
|
|
790
829
|
}
|
|
830
|
+
function currentFullTextDescriptor() {
|
|
831
|
+
const namedKey = `${tableName}/${primaryKey}`;
|
|
832
|
+
const namedDescriptor = dbisDb.getSync(namedKey);
|
|
833
|
+
const key = namedDescriptor?.isPrimaryKey ? namedKey : `${tableName}/`;
|
|
834
|
+
const descriptor = dbisDb.getSync(key);
|
|
835
|
+
if (!descriptor ||
|
|
836
|
+
(descriptor.tableId != null && descriptor.tableId !== tableId) ||
|
|
837
|
+
descriptor.generation !== tableGeneration)
|
|
838
|
+
return;
|
|
839
|
+
return { key, descriptor };
|
|
840
|
+
}
|
|
791
841
|
class TableResource extends Resource_ts_1.Resource {
|
|
792
842
|
#record; // the stored/frozen record from the database and stored in the cache (should not be modified directly)
|
|
793
843
|
#changes; // the changes to the record that have been made (should not be modified directly)
|
|
@@ -840,6 +890,38 @@ function makeTable(options) {
|
|
|
840
890
|
static derivedIndexRuntime;
|
|
841
891
|
static audit = audit;
|
|
842
892
|
static fullTextIndexes = fullTextIndexes;
|
|
893
|
+
static fullTextIndexGenerations = fullTextIndexGenerations;
|
|
894
|
+
static fullTextIndexRetirements = fullTextIndexRetirements;
|
|
895
|
+
static hasCurrentFullTextIndexRetirements(names) {
|
|
896
|
+
if (names.length === 0 || !(primaryStore.rootStore instanceof rocksdb_js_1.RocksDatabase))
|
|
897
|
+
return false;
|
|
898
|
+
return withUpdateAttributesLockNonBlocking(primaryStore.rootStore, `verify full-text retirement for '${databaseName}.${tableName}'`, () => {
|
|
899
|
+
const current = currentFullTextDescriptor();
|
|
900
|
+
if (!current)
|
|
901
|
+
return false;
|
|
902
|
+
const pending = new Set((0, fullTextSchema_ts_1.persistedFullTextIndexNames)(current.descriptor.fullTextIndexRetirements));
|
|
903
|
+
return names.every((name) => pending.has(name));
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
static completeFullTextIndexRetirements(names) {
|
|
907
|
+
if (names.length === 0 || !(primaryStore.rootStore instanceof rocksdb_js_1.RocksDatabase))
|
|
908
|
+
return;
|
|
909
|
+
const completed = new Set(names);
|
|
910
|
+
return withUpdateAttributesLockNonBlocking(primaryStore.rootStore, `complete full-text retirement for '${databaseName}.${tableName}'`, () => {
|
|
911
|
+
const current = currentFullTextDescriptor();
|
|
912
|
+
if (!current)
|
|
913
|
+
return;
|
|
914
|
+
const { key, descriptor } = current;
|
|
915
|
+
const remaining = (0, fullTextSchema_ts_1.persistedFullTextIndexNames)(descriptor.fullTextIndexRetirements).filter((name) => !completed.has(name));
|
|
916
|
+
const updated = { ...descriptor };
|
|
917
|
+
if (remaining.length > 0)
|
|
918
|
+
updated.fullTextIndexRetirements = remaining.map((name) => ({ name }));
|
|
919
|
+
else
|
|
920
|
+
delete updated.fullTextIndexRetirements;
|
|
921
|
+
dbisDb.putSync(key, updated);
|
|
922
|
+
this.fullTextIndexRetirements = remaining;
|
|
923
|
+
});
|
|
924
|
+
}
|
|
843
925
|
static databasePath = databasePath;
|
|
844
926
|
static databaseName = databaseName;
|
|
845
927
|
static attributes = attributes;
|
|
@@ -863,6 +945,9 @@ function makeTable(options) {
|
|
|
863
945
|
static userEmbedders = {};
|
|
864
946
|
static userSetEmbedders = new Set();
|
|
865
947
|
static embedAttributes = attributes.filter((a) => a?.embed);
|
|
948
|
+
static userDeciders = {};
|
|
949
|
+
static userSetDeciders = new Set();
|
|
950
|
+
static decideAttributes = attributes.filter((a) => a?.decide);
|
|
866
951
|
static source;
|
|
867
952
|
static getResidencyById;
|
|
868
953
|
static get expirationMS() {
|
|
@@ -1224,7 +1309,7 @@ function makeTable(options) {
|
|
|
1224
1309
|
catch { }
|
|
1225
1310
|
throw error;
|
|
1226
1311
|
}
|
|
1227
|
-
return seqTransaction.
|
|
1312
|
+
return (0, DatabaseTransaction_ts_1.commitTrackedRocksTransaction)(seqTransaction, dbisDb, primaryStore.rootStore).catch((error) => {
|
|
1228
1313
|
// A rejected commit leaves the handle open too, so release it here as well —
|
|
1229
1314
|
// same reason as the staging failure above, and the same shape as the
|
|
1230
1315
|
// eviction paths' commit failures (see evict/commitItems below).
|
|
@@ -1453,6 +1538,8 @@ function makeTable(options) {
|
|
|
1453
1538
|
* @returns
|
|
1454
1539
|
*/
|
|
1455
1540
|
static getResource(target, request, resourceOptions) {
|
|
1541
|
+
if ((0, databaseDropPreparation_ts_1.databaseDropPrepared)(tableRootPath) || (0, DatabaseTransaction_ts_1.databaseCommitsSuspended)(tableRootStore))
|
|
1542
|
+
throw new hdbError_ts_1.DatabaseClosingError(databaseName, !request?.transaction?.root && !request?.transaction?.snapshotFree);
|
|
1456
1543
|
const resource = super.getResource(target, request, resourceOptions);
|
|
1457
1544
|
if (this.loadAsInstance !== false) {
|
|
1458
1545
|
return resource._loadRecord(target, request, resourceOptions);
|
|
@@ -1842,14 +1929,25 @@ function makeTable(options) {
|
|
|
1842
1929
|
// same-name recreate must not race an owner still applying to the old generation.
|
|
1843
1930
|
const derivedIndexRuntime = _a.derivedIndexRuntime;
|
|
1844
1931
|
const restoreDerivedIndexesAfterFailedDrop = () => {
|
|
1932
|
+
_a.resumeMaintenance();
|
|
1845
1933
|
try {
|
|
1846
|
-
|
|
1934
|
+
const restored = derivedIndexRuntime?.restoreAfterFailedDrop?.();
|
|
1935
|
+
if (_a.derivedIndexRuntime === derivedIndexRuntime)
|
|
1936
|
+
_a.derivedIndexRuntime = restored;
|
|
1847
1937
|
}
|
|
1848
1938
|
catch (restoreError) {
|
|
1849
|
-
_a.derivedIndexRuntime
|
|
1939
|
+
if (_a.derivedIndexRuntime === derivedIndexRuntime)
|
|
1940
|
+
_a.derivedIndexRuntime = undefined;
|
|
1850
1941
|
logger_ts_1.logger.error?.(`Could not restore derived indexes after failed drop of ${databaseName}.${_a.tableName}`, restoreError);
|
|
1851
1942
|
}
|
|
1852
1943
|
};
|
|
1944
|
+
try {
|
|
1945
|
+
await _a.closeMaintenance();
|
|
1946
|
+
}
|
|
1947
|
+
catch (error) {
|
|
1948
|
+
_a.resumeMaintenance();
|
|
1949
|
+
throw error;
|
|
1950
|
+
}
|
|
1853
1951
|
try {
|
|
1854
1952
|
await derivedIndexRuntime?.close(true);
|
|
1855
1953
|
}
|
|
@@ -1857,13 +1955,23 @@ function makeTable(options) {
|
|
|
1857
1955
|
restoreDerivedIndexesAfterFailedDrop();
|
|
1858
1956
|
throw error;
|
|
1859
1957
|
}
|
|
1958
|
+
let releaseFullTextRetirementFence;
|
|
1959
|
+
const releaseFullTextRetirement = () => {
|
|
1960
|
+
const release = releaseFullTextRetirementFence;
|
|
1961
|
+
releaseFullTextRetirementFence = undefined;
|
|
1962
|
+
release?.();
|
|
1963
|
+
};
|
|
1860
1964
|
const abortStaleDrop = () => {
|
|
1965
|
+
releaseFullTextRetirement();
|
|
1861
1966
|
derivedIndexRuntime?.completeDrop?.(false);
|
|
1862
1967
|
_a.derivedIndexRuntime = undefined;
|
|
1863
1968
|
_a.cleanup();
|
|
1864
1969
|
if (databases_ts_1.databases[databaseName]?.[tableName] === _a)
|
|
1865
1970
|
delete databases_ts_1.databases[databaseName][tableName];
|
|
1866
1971
|
};
|
|
1972
|
+
let fullTextDefinitionsForRetirement = [
|
|
1973
|
+
..._a.fullTextIndexes,
|
|
1974
|
+
];
|
|
1867
1975
|
let dropIdentityConfirmed = databaseName !== databasePath;
|
|
1868
1976
|
let primaryCatalogKey = _a.tableName + '/';
|
|
1869
1977
|
let storeGeneration;
|
|
@@ -1892,6 +2000,23 @@ function makeTable(options) {
|
|
|
1892
2000
|
return false;
|
|
1893
2001
|
dropGeneration = primaryMeta.dropGeneration;
|
|
1894
2002
|
storeGeneration = primaryMeta.generation;
|
|
2003
|
+
const durableFullTextDefinitions = rootStore instanceof rocksdb_js_1.RocksDatabase
|
|
2004
|
+
? [
|
|
2005
|
+
...(0, fullTextSchema_ts_1.persistedFullTextIndexNames)(primaryMeta.fullTextIndexes),
|
|
2006
|
+
...(0, fullTextSchema_ts_1.persistedFullTextIndexNames)(primaryMeta.fullTextIndexRetirements),
|
|
2007
|
+
].map((name) => ({ name }))
|
|
2008
|
+
: [];
|
|
2009
|
+
const attachedFullTextDefinitions = derivedIndexRuntime?.fullTextDefinitions?.() ?? [];
|
|
2010
|
+
const definitionsByName = new Map([...durableFullTextDefinitions, ...attachedFullTextDefinitions].map((definition) => [
|
|
2011
|
+
definition.name,
|
|
2012
|
+
definition,
|
|
2013
|
+
]));
|
|
2014
|
+
fullTextDefinitionsForRetirement = [...definitionsByName.values()];
|
|
2015
|
+
if (fullTextDefinitionsForRetirement.length > 0 && !releaseFullTextRetirementFence) {
|
|
2016
|
+
releaseFullTextRetirementFence = (0, derivedIndexRegistry_ts_1.acquireFullTextRetirementFence)(rootStore, _a.tableName);
|
|
2017
|
+
if (!releaseFullTextRetirementFence)
|
|
2018
|
+
throw new hdbError_ts_1.ClientError(`Cannot drop '${databaseName}.${_a.tableName}' while its full-text storage is being retired`, 409);
|
|
2019
|
+
}
|
|
1895
2020
|
if (primaryMeta.dropping)
|
|
1896
2021
|
return true;
|
|
1897
2022
|
primaryMeta.dropping = true;
|
|
@@ -1923,11 +2048,13 @@ function makeTable(options) {
|
|
|
1923
2048
|
}
|
|
1924
2049
|
}
|
|
1925
2050
|
catch (error) {
|
|
2051
|
+
releaseFullTextRetirement();
|
|
1926
2052
|
restoreDerivedIndexesAfterFailedDrop();
|
|
1927
2053
|
throw error;
|
|
1928
2054
|
}
|
|
1929
2055
|
}
|
|
1930
2056
|
if (!dropIdentityConfirmed) {
|
|
2057
|
+
releaseFullTextRetirement();
|
|
1931
2058
|
abortStaleDrop();
|
|
1932
2059
|
return;
|
|
1933
2060
|
}
|
|
@@ -1951,13 +2078,20 @@ function makeTable(options) {
|
|
|
1951
2078
|
try {
|
|
1952
2079
|
if (!dropGeneration)
|
|
1953
2080
|
throw new Error(`Cannot drop ${databaseName}.${tableName}: its catalog tombstone has no drop generation`);
|
|
1954
|
-
await retireRocksStores(storeGeneration, dropGeneration);
|
|
2081
|
+
const retired = await retireRocksStores(storeGeneration, dropGeneration);
|
|
2082
|
+
if (!retired) {
|
|
2083
|
+
derivedIndexRuntime?.completeDrop?.();
|
|
2084
|
+
releaseFullTextRetirement();
|
|
2085
|
+
return;
|
|
2086
|
+
}
|
|
1955
2087
|
}
|
|
1956
2088
|
catch (error) {
|
|
2089
|
+
releaseFullTextRetirement();
|
|
1957
2090
|
derivedIndexRuntime?.completeDrop?.();
|
|
1958
2091
|
throw error;
|
|
1959
2092
|
}
|
|
1960
2093
|
derivedIndexRuntime?.completeDrop?.();
|
|
2094
|
+
releaseFullTextRetirement();
|
|
1961
2095
|
return;
|
|
1962
2096
|
}
|
|
1963
2097
|
try {
|
|
@@ -1968,6 +2102,7 @@ function makeTable(options) {
|
|
|
1968
2102
|
}
|
|
1969
2103
|
}
|
|
1970
2104
|
catch (error) {
|
|
2105
|
+
releaseFullTextRetirement();
|
|
1971
2106
|
derivedIndexRuntime?.completeDrop?.();
|
|
1972
2107
|
throw error;
|
|
1973
2108
|
}
|
|
@@ -2009,6 +2144,7 @@ function makeTable(options) {
|
|
|
2009
2144
|
await dbisDb.committed;
|
|
2010
2145
|
}
|
|
2011
2146
|
catch (error) {
|
|
2147
|
+
releaseFullTextRetirement();
|
|
2012
2148
|
derivedIndexRuntime?.completeDrop?.();
|
|
2013
2149
|
throw error;
|
|
2014
2150
|
}
|
|
@@ -2029,14 +2165,31 @@ function makeTable(options) {
|
|
|
2029
2165
|
node_fs_1.default.unlinkSync(primaryStore.path);
|
|
2030
2166
|
}
|
|
2031
2167
|
catch (error) {
|
|
2168
|
+
releaseFullTextRetirement();
|
|
2032
2169
|
derivedIndexRuntime?.completeDrop?.();
|
|
2033
2170
|
throw error;
|
|
2034
2171
|
}
|
|
2035
2172
|
}
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2173
|
+
try {
|
|
2174
|
+
const message = new itc_js_1.SchemaEventMsg(process.pid, hdbTerms_ts_1.OPERATIONS_ENUM.DROP_TABLE, databaseName, tableName);
|
|
2175
|
+
message.dropTableId = tableId;
|
|
2176
|
+
await signalling.signalSchemaChange(message);
|
|
2177
|
+
await retireFullTextStorage();
|
|
2178
|
+
derivedIndexRuntime?.completeDrop?.();
|
|
2179
|
+
}
|
|
2180
|
+
finally {
|
|
2181
|
+
releaseFullTextRetirement();
|
|
2182
|
+
}
|
|
2183
|
+
async function retireFullTextStorage() {
|
|
2184
|
+
if (fullTextDefinitionsForRetirement.length === 0)
|
|
2185
|
+
return true;
|
|
2186
|
+
if (derivedIndexRuntime?.retireAfterConfirmedDrop)
|
|
2187
|
+
return derivedIndexRuntime.retireAfterConfirmedDrop(fullTextDefinitionsForRetirement);
|
|
2188
|
+
else {
|
|
2189
|
+
const { retireFullTextIndexes } = await import("./derivedIndexes.js");
|
|
2190
|
+
return retireFullTextIndexes(_a, fullTextDefinitionsForRetirement);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2040
2193
|
async function retireRocksStores(generation, dropGeneration) {
|
|
2041
2194
|
const releaseDropMark = (0, databases_ts_1.markDropInProgress)(dropGeneration);
|
|
2042
2195
|
try {
|
|
@@ -2044,6 +2197,10 @@ function makeTable(options) {
|
|
|
2044
2197
|
message.dropGeneration = dropGeneration;
|
|
2045
2198
|
message.dropTableId = tableId;
|
|
2046
2199
|
await signalling.signalSchemaChange(message);
|
|
2200
|
+
// Keep the tombstone's native index names durable until every peer writer
|
|
2201
|
+
// has stopped and the wrapper has retired their storage.
|
|
2202
|
+
if (!(await retireFullTextStorage()))
|
|
2203
|
+
return false;
|
|
2047
2204
|
const removed = withUpdateAttributesLock(rootStore, `table '${databaseName}.${tableName}'`, () => {
|
|
2048
2205
|
const stores = (0, databases_ts_1.storeNamesFor)(dbisDb, tableName, generation);
|
|
2049
2206
|
const retiredStores = (0, databases_ts_1.recordRetiredGeneration)(dbisDb, tableName, dropGeneration, stores, primaryStore.name ?? (0, databases_ts_1.storeNameFor)(primaryCatalogKey, generation));
|
|
@@ -2092,6 +2249,7 @@ function makeTable(options) {
|
|
|
2092
2249
|
await (0, databases_ts_1.sweepDroppedTableBlobs)(primaryStore, label);
|
|
2093
2250
|
if (!settled)
|
|
2094
2251
|
finishDroppedTableBlobSweep(rootStore, primaryStore, label);
|
|
2252
|
+
return true;
|
|
2095
2253
|
}
|
|
2096
2254
|
finally {
|
|
2097
2255
|
releaseDropMark();
|
|
@@ -2724,6 +2882,8 @@ function makeTable(options) {
|
|
|
2724
2882
|
* Evicting a record will remove it from a caching table. This is not considered a canonical data change, and it is assumed that retrieving this record from the source will still yield the same record, this is only removing the local copy of the record.
|
|
2725
2883
|
*/
|
|
2726
2884
|
static evict(id, existingRecord, existingVersion) {
|
|
2885
|
+
if (maintenanceClosed)
|
|
2886
|
+
return Promise.resolve();
|
|
2727
2887
|
let entry;
|
|
2728
2888
|
const lmdbTransaction = txnForContext({ transaction: new DatabaseTransaction_ts_1.DatabaseTransaction() });
|
|
2729
2889
|
let transaction = lmdbTransaction.getReadTxn();
|
|
@@ -2775,15 +2935,15 @@ function makeTable(options) {
|
|
|
2775
2935
|
// a plain resolution object rather than a promise — return the store's write promises instead, so
|
|
2776
2936
|
// the caller gets a real thenable that resolves once the removal is durable.
|
|
2777
2937
|
lmdbTransaction.commit();
|
|
2778
|
-
return Promise.resolve(lmdbCompletion).catch((error) => {
|
|
2938
|
+
return trackMaintenanceCommit(Promise.resolve(lmdbCompletion).catch((error) => {
|
|
2779
2939
|
logger_ts_1.logger.warn?.('Error evicting record', id, error);
|
|
2780
|
-
});
|
|
2940
|
+
}));
|
|
2781
2941
|
}
|
|
2782
2942
|
// RocksDB: eviction writes went directly into the raw transaction via options; commit it directly,
|
|
2783
2943
|
// as DatabaseTransaction.commit() would abort it (no tracked writes). The raw commit bypasses
|
|
2784
2944
|
// DatabaseTransaction's ERR_BUSY retry, so a concurrent-write conflict rejects here — swallow it
|
|
2785
2945
|
// (abandon the eviction) and log anything unexpected, rather than letting it crash the process.
|
|
2786
|
-
return
|
|
2946
|
+
return trackMaintenanceCommit((0, DatabaseTransaction_ts_1.commitTrackedRocksTransaction)(transaction, primaryStore).catch((error) => {
|
|
2787
2947
|
// The commit failed, so the read-snapshot/transaction handle is still open — release it, as the
|
|
2788
2948
|
// batched-eviction path does on its own commit failures. committed===true skips the finally abort.
|
|
2789
2949
|
try {
|
|
@@ -2794,7 +2954,7 @@ function makeTable(options) {
|
|
|
2794
2954
|
logger_ts_1.logger.trace?.('Abandoned eviction of busy record', id);
|
|
2795
2955
|
else
|
|
2796
2956
|
logger_ts_1.logger.warn?.('Error evicting record', id, error);
|
|
2797
|
-
});
|
|
2957
|
+
}));
|
|
2798
2958
|
}
|
|
2799
2959
|
finally {
|
|
2800
2960
|
if (!committed) {
|
|
@@ -4076,23 +4236,26 @@ function makeTable(options) {
|
|
|
4076
4236
|
},
|
|
4077
4237
|
};
|
|
4078
4238
|
this.#savingOperation = write;
|
|
4079
|
-
//
|
|
4080
|
-
//
|
|
4081
|
-
//
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4239
|
+
// The hooks run before `addWrite` so the derived values are on the record at commit (the
|
|
4240
|
+
// txn `before` slot runs after commit). They see the payload before table validation, and a
|
|
4241
|
+
// tracked-instance mutation that sets the source via accessors after update() is not seen.
|
|
4242
|
+
let modelHooksBefore;
|
|
4243
|
+
try {
|
|
4244
|
+
modelHooksBefore =
|
|
4245
|
+
(_a.embedAttributes.length || _a.decideAttributes.length) && !(0, databases_ts_1.isReadOnlyMode)()
|
|
4246
|
+
? (0, embedHook_ts_1.combineWriteHooks)((0, embedHook_ts_1.buildEmbedBefore)(recordUpdate, context, options, _a.embedAttributes, _a.userEmbedders), (0, decideHook_ts_1.buildDecideBefore)(recordUpdate, context, options, _a.decideAttributes, _a.userDeciders))
|
|
4247
|
+
: undefined;
|
|
4248
|
+
}
|
|
4249
|
+
catch (err) {
|
|
4250
|
+
return Promise.reject(err);
|
|
4251
|
+
}
|
|
4089
4252
|
const proceed = () => {
|
|
4090
4253
|
// On a source/replication apply (`isNotification`), the record's already-saved blobs were
|
|
4091
4254
|
// received out-of-band for THIS write, so track them for skip/abort cleanup (harper-pro#406).
|
|
4092
4255
|
write.beforeIntermediate = preCommitBlobsForRecordBefore(write, recordUpdate, undefined, undefined, options?.isNotification);
|
|
4093
4256
|
return transaction.addWrite(write);
|
|
4094
4257
|
};
|
|
4095
|
-
return
|
|
4258
|
+
return modelHooksBefore ? modelHooksBefore(context?.signal).then(proceed) : proceed();
|
|
4096
4259
|
}
|
|
4097
4260
|
async delete(target) {
|
|
4098
4261
|
if (isSearchTarget(target)) {
|
|
@@ -5326,6 +5489,10 @@ function makeTable(options) {
|
|
|
5326
5489
|
// messages during this loop because the snapshot:false cursor will pick them up itself.
|
|
5327
5490
|
pendingRealTimeQueue = null;
|
|
5328
5491
|
dropDuringReplay = true;
|
|
5492
|
+
// subscription.startTime is the resume cursor (exclusive) and RocksDB gives every record of a
|
|
5493
|
+
// transaction the same txnLogKey, so it only moves to a key once all of that key's records are
|
|
5494
|
+
// handled; an early return leaves it before a partly delivered transaction.
|
|
5495
|
+
let handledTxnLogKey;
|
|
5329
5496
|
try {
|
|
5330
5497
|
for (const auditRecord of auditStore.getRange({
|
|
5331
5498
|
start: startTime,
|
|
@@ -5342,20 +5509,25 @@ function makeTable(options) {
|
|
|
5342
5509
|
continue;
|
|
5343
5510
|
if ((0, auditStore_ts_1.isLockControlType)(auditRecord.type))
|
|
5344
5511
|
continue;
|
|
5512
|
+
if (handledTxnLogKey !== undefined && auditRecord.txnLogKey !== handledTxnLogKey) {
|
|
5513
|
+
subscription.startTime = handledTxnLogKey;
|
|
5514
|
+
}
|
|
5345
5515
|
const id = auditRecord.recordId;
|
|
5346
|
-
subscription.startTime = auditRecord.txnLogKey;
|
|
5347
5516
|
if (thisId == null || isDescendantId(thisId, id)) {
|
|
5348
5517
|
const event = eventFromAudit(id, auditRecord, auditRecord.txnLogKey);
|
|
5349
|
-
if (
|
|
5350
|
-
|
|
5351
|
-
if (!send(event))
|
|
5352
|
-
return;
|
|
5353
|
-
if (subscription.queue?.length > EVENT_HIGH_WATER_MARK) {
|
|
5354
|
-
if ((await subscription.waitForDrain()) === false)
|
|
5518
|
+
if (event) {
|
|
5519
|
+
if (!send(event))
|
|
5355
5520
|
return;
|
|
5521
|
+
if (subscription.queue?.length > EVENT_HIGH_WATER_MARK) {
|
|
5522
|
+
if ((await subscription.waitForDrain()) === false)
|
|
5523
|
+
return;
|
|
5524
|
+
}
|
|
5356
5525
|
}
|
|
5357
5526
|
}
|
|
5527
|
+
handledTxnLogKey = auditRecord.txnLogKey;
|
|
5358
5528
|
}
|
|
5529
|
+
if (handledTxnLogKey !== undefined)
|
|
5530
|
+
subscription.startTime = handledTxnLogKey;
|
|
5359
5531
|
}
|
|
5360
5532
|
finally {
|
|
5361
5533
|
// replay is done, we can start sending real-time messages again
|
|
@@ -6318,10 +6490,19 @@ function makeTable(options) {
|
|
|
6318
6490
|
static updatedAttributes() {
|
|
6319
6491
|
// Refresh on every call: schema reload mutates `attributes` in place, so the
|
|
6320
6492
|
// class-construction snapshot would otherwise go stale.
|
|
6493
|
+
// Declarations are refused before they are saved; here a descriptor an earlier build
|
|
6494
|
+
// accepted must still load, so a violation is logged and the table keeps working.
|
|
6495
|
+
try {
|
|
6496
|
+
(0, embedHook_ts_1.assertDerivedFieldOwnership)(this.attributes);
|
|
6497
|
+
}
|
|
6498
|
+
catch (error) {
|
|
6499
|
+
console.error(`Derived attributes of table "${tableName}" conflict: ${error.message}`);
|
|
6500
|
+
}
|
|
6321
6501
|
this.embedAttributes = this.attributes.filter((a) => a?.embed);
|
|
6502
|
+
this.decideAttributes = this.attributes.filter((a) => a?.decide);
|
|
6322
6503
|
expiresAtProperty = this.attributes.find((attribute) => attribute.expiresAt);
|
|
6323
|
-
// Drop registry entries for attributes that are no longer `@embed`, so a dropped
|
|
6324
|
-
// directive doesn't leave a stale
|
|
6504
|
+
// Drop registry entries for attributes that are no longer `@embed` / `@decide`, so a dropped
|
|
6505
|
+
// directive doesn't leave a stale hook or block a default refresh on re-add.
|
|
6325
6506
|
const embedNames = new Set(this.embedAttributes.map((a) => a.name));
|
|
6326
6507
|
for (const name of Object.keys(this.userEmbedders))
|
|
6327
6508
|
if (!embedNames.has(name))
|
|
@@ -6329,6 +6510,13 @@ function makeTable(options) {
|
|
|
6329
6510
|
for (const name of this.userSetEmbedders)
|
|
6330
6511
|
if (!embedNames.has(name))
|
|
6331
6512
|
this.userSetEmbedders.delete(name);
|
|
6513
|
+
const decideNames = new Set(this.decideAttributes.map((a) => a.name));
|
|
6514
|
+
for (const name of Object.keys(this.userDeciders))
|
|
6515
|
+
if (!decideNames.has(name))
|
|
6516
|
+
delete this.userDeciders[name];
|
|
6517
|
+
for (const name of this.userSetDeciders)
|
|
6518
|
+
if (!decideNames.has(name))
|
|
6519
|
+
this.userSetDeciders.delete(name);
|
|
6332
6520
|
propertyResolvers = this.propertyResolvers = {
|
|
6333
6521
|
$id: (object, context, entry) => ({ value: entry.key }),
|
|
6334
6522
|
$updatedtime: (object, context, entry) => entry.version,
|
|
@@ -6368,9 +6556,12 @@ function makeTable(options) {
|
|
|
6368
6556
|
const computed = attribute.computed;
|
|
6369
6557
|
// Register the default embedder unless an author override is set. Sits outside
|
|
6370
6558
|
// the resolver chain below so `@embed` fields still flow through auto-HNSW indexing.
|
|
6371
|
-
if (attribute.embed && !
|
|
6559
|
+
if (attribute.embed && !this.userSetEmbedders.has(attribute.name)) {
|
|
6372
6560
|
this.userEmbedders[attribute.name] = (0, embedHook_ts_1.createDefaultEmbedder)(attribute.embed);
|
|
6373
6561
|
}
|
|
6562
|
+
if (attribute.decide && !this.userSetDeciders.has(attribute.name)) {
|
|
6563
|
+
this.userDeciders[attribute.name] = (0, decideHook_ts_1.createDefaultDecider)(attribute.decide);
|
|
6564
|
+
}
|
|
6374
6565
|
if (relationship) {
|
|
6375
6566
|
if (attribute.indexed) {
|
|
6376
6567
|
console.error(`A relationship property can not be directly indexed, (but you may want to index the foreign key attribute)`);
|
|
@@ -6597,6 +6788,26 @@ function makeTable(options) {
|
|
|
6597
6788
|
this.userEmbedders[attribute_name] = embedder;
|
|
6598
6789
|
this.userSetEmbedders.add(attribute_name);
|
|
6599
6790
|
}
|
|
6791
|
+
/**
|
|
6792
|
+
* Override the default decider for a `@decide` attribute. Return `{ value, probability }`
|
|
6793
|
+
* to store at the attribute and its confidence attribute, or `null` to clear both. The
|
|
6794
|
+
* value must be one the directive allows, and the probability is required when the
|
|
6795
|
+
* directive names a confidence attribute. Like an embedder, the decider receives the write
|
|
6796
|
+
* payload, not the post-merge record, and a `signal` that aborts when a sibling hook fails.
|
|
6797
|
+
*/
|
|
6798
|
+
static setDecideAttribute(attribute_name, decider) {
|
|
6799
|
+
const attribute = (0, search_ts_1.findAttribute)(attributes, attribute_name);
|
|
6800
|
+
if (!attribute) {
|
|
6801
|
+
console.error(`The attribute "${attribute_name}" does not exist in the table "${tableName}"`);
|
|
6802
|
+
return;
|
|
6803
|
+
}
|
|
6804
|
+
if (!attribute.decide) {
|
|
6805
|
+
console.error(`The attribute "${attribute_name}" is not declared with @decide in the table "${tableName}"`);
|
|
6806
|
+
return;
|
|
6807
|
+
}
|
|
6808
|
+
this.userDeciders[attribute_name] = decider;
|
|
6809
|
+
this.userSetDeciders.add(attribute_name);
|
|
6810
|
+
}
|
|
6600
6811
|
static async deleteHistory(endTime = 0, cleanupDeletedRecords = false) {
|
|
6601
6812
|
const maxConcurrentRemovals = isRocksDB ? MAX_CONCURRENT_HISTORY_REMOVALS : MAX_CONCURRENT_LMDB_HISTORY_REMOVALS;
|
|
6602
6813
|
const inFlightRemovals = new Set();
|
|
@@ -6760,31 +6971,105 @@ function makeTable(options) {
|
|
|
6760
6971
|
} while (count < 1000 && nextVersion);
|
|
6761
6972
|
return history.reverse();
|
|
6762
6973
|
}
|
|
6763
|
-
static clear() {
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6974
|
+
static async clear() {
|
|
6975
|
+
const rootStore = primaryStore.rootStore;
|
|
6976
|
+
const assertNoFullTextDeclaration = () => {
|
|
6977
|
+
const namedPrimaryDescriptor = dbisDb.getSync(`${tableName}/${primaryKey}`);
|
|
6978
|
+
const primaryDescriptor = namedPrimaryDescriptor?.isPrimaryKey
|
|
6979
|
+
? namedPrimaryDescriptor
|
|
6980
|
+
: dbisDb.getSync(`${tableName}/`);
|
|
6981
|
+
if (_a.fullTextIndexes.length > 0 ||
|
|
6982
|
+
(rootStore instanceof rocksdb_js_1.RocksDatabase &&
|
|
6983
|
+
(0, fullTextSchema_ts_1.persistedFullTextIndexNames)(primaryDescriptor?.fullTextIndexes).length > 0))
|
|
6984
|
+
throw new hdbError_ts_1.ClientError(`Table.clear() is not supported on full-text table '${databaseName}.${tableName}' until whole-table invalidation is crash-safe`, 501);
|
|
6985
|
+
};
|
|
6986
|
+
let releaseFullTextClearFence;
|
|
6987
|
+
let waitForExistingClear;
|
|
6988
|
+
let admission;
|
|
6989
|
+
if (rootStore instanceof rocksdb_js_1.RocksDatabase) {
|
|
6990
|
+
admission = withUpdateAttributesLockNonBlocking(rootStore, `clear table '${databaseName}.${tableName}'`, () => {
|
|
6991
|
+
assertNoFullTextDeclaration();
|
|
6992
|
+
releaseFullTextClearFence = (0, derivedIndexRegistry_ts_1.acquireFullTextClearFence)(rootStore, tableId);
|
|
6993
|
+
if (!releaseFullTextClearFence)
|
|
6994
|
+
waitForExistingClear = (0, derivedIndexRegistry_ts_1.waitForFullTextClear)(rootStore, tableId);
|
|
6995
|
+
});
|
|
6772
6996
|
}
|
|
6773
|
-
|
|
6997
|
+
else
|
|
6998
|
+
assertNoFullTextDeclaration();
|
|
6999
|
+
const startClear = () => {
|
|
7000
|
+
if (waitForExistingClear)
|
|
7001
|
+
return waitForExistingClear.then(() => _a.clear());
|
|
7002
|
+
// clear the primary store and every secondary index dbi (same pattern used by
|
|
7003
|
+
// runIndexing when rebuilding from scratch), so clear() doesn't leave stale
|
|
7004
|
+
// index entries pointing at records that no longer exist.
|
|
7005
|
+
const promises = [];
|
|
7006
|
+
const settleStartedClears = (synchronousFailure) => Promise.allSettled(promises)
|
|
7007
|
+
.then((results) => {
|
|
7008
|
+
if (synchronousFailure)
|
|
7009
|
+
throw synchronousFailure.error;
|
|
7010
|
+
const values = [];
|
|
7011
|
+
for (const result of results) {
|
|
7012
|
+
if (result.status === 'rejected')
|
|
7013
|
+
throw result.reason;
|
|
7014
|
+
values.push(result.value);
|
|
7015
|
+
}
|
|
7016
|
+
return values;
|
|
7017
|
+
})
|
|
7018
|
+
.finally(releaseFullTextClearFence);
|
|
7019
|
+
try {
|
|
7020
|
+
promises.push(primaryStore.clear());
|
|
7021
|
+
for (const key in indices) {
|
|
7022
|
+
const index = indices[key];
|
|
7023
|
+
index.customIndex?.resetDerivedStorage?.();
|
|
7024
|
+
promises.push(index.clearAsync ? index.clearAsync() : index.clear());
|
|
7025
|
+
}
|
|
7026
|
+
return settleStartedClears();
|
|
7027
|
+
}
|
|
7028
|
+
catch (error) {
|
|
7029
|
+
if (promises.length > 0)
|
|
7030
|
+
return settleStartedClears({ error });
|
|
7031
|
+
releaseFullTextClearFence?.();
|
|
7032
|
+
throw error;
|
|
7033
|
+
}
|
|
7034
|
+
};
|
|
7035
|
+
return admission instanceof Promise ? admission.then(startClear) : startClear();
|
|
6774
7036
|
}
|
|
6775
7037
|
/** Release everything makeTable() registered process-wide; the class must not be used afterwards. */
|
|
6776
7038
|
static cleanup() {
|
|
6777
7039
|
disposed = true;
|
|
6778
7040
|
_a.getResource = unavailableResource;
|
|
6779
|
-
_a.derivedIndexRuntime
|
|
7041
|
+
void _a.derivedIndexRuntime
|
|
6780
7042
|
?.close()
|
|
6781
7043
|
.catch((error) => logger_ts_1.logger.warn?.(`Derived index shutdown failed for ${databaseName}.${tableName}`, error));
|
|
6782
|
-
|
|
6783
|
-
settlePendingCleanup();
|
|
6784
|
-
clearInterval(recordExpirationInterval);
|
|
7044
|
+
stopMaintenance();
|
|
6785
7045
|
deleteCallbackHandle?.remove();
|
|
6786
7046
|
(0, storageReclamation_ts_1.removeStorageReclamationHandler)(primaryStore.path, reclamationHandler);
|
|
6787
7047
|
}
|
|
7048
|
+
static async closeMaintenance(deadline) {
|
|
7049
|
+
const startedAt = Date.now();
|
|
7050
|
+
const defaultTimeout = (0, DatabaseTransaction_ts_1.getDatabaseCommitDrainTimeoutMilliseconds)();
|
|
7051
|
+
const timeoutMilliseconds = deadline === undefined ? defaultTimeout : Math.max(0, deadline - startedAt);
|
|
7052
|
+
const resolvedDeadline = deadline ?? startedAt + timeoutMilliseconds;
|
|
7053
|
+
stopMaintenance();
|
|
7054
|
+
const timeoutError = () => new hdbError_ts_1.DatabaseDrainTimeoutError(databaseName, timeoutMilliseconds);
|
|
7055
|
+
await (0, when_ts_1.settleBeforeDeadline)([lastEvictionCompletion, recordExpirationCompletion], resolvedDeadline, timeoutError);
|
|
7056
|
+
for (;;) {
|
|
7057
|
+
const pending = [...maintenanceCommits];
|
|
7058
|
+
if (pending.length === 0)
|
|
7059
|
+
return;
|
|
7060
|
+
await (0, when_ts_1.settleBeforeDeadline)(pending, resolvedDeadline, timeoutError);
|
|
7061
|
+
}
|
|
7062
|
+
}
|
|
7063
|
+
static resumeMaintenance() {
|
|
7064
|
+
if (disposed || !maintenanceClosed)
|
|
7065
|
+
return;
|
|
7066
|
+
maintenanceClosed = false;
|
|
7067
|
+
lastCleanupInterval = undefined;
|
|
7068
|
+
if (expirationScanScheduled || evictionMs)
|
|
7069
|
+
scheduleCleanup();
|
|
7070
|
+
if (expiresAtProperty && !recordExpirationInterval)
|
|
7071
|
+
runRecordExpirationEviction();
|
|
7072
|
+
}
|
|
6788
7073
|
static _readTxnForContext(context) {
|
|
6789
7074
|
return txnForContext(context).getReadTxn();
|
|
6790
7075
|
}
|
|
@@ -6802,7 +7087,7 @@ function makeTable(options) {
|
|
|
6802
7087
|
}
|
|
6803
7088
|
}, () => {
|
|
6804
7089
|
throw new hdbError_ts_1.ServerError('Service unavailable, exceeded request queue limit for resolving cache record', 503);
|
|
6805
|
-
});
|
|
7090
|
+
}, undefined, `cache resolution for ${tableName}`);
|
|
6806
7091
|
try {
|
|
6807
7092
|
TableResource.updatedAttributes(); // on creation, update accessors as well
|
|
6808
7093
|
if (expirationMs) {
|
|
@@ -7445,7 +7730,8 @@ function makeTable(options) {
|
|
|
7445
7730
|
replacingVersion: existingVersion,
|
|
7446
7731
|
// Once dropTable() has started, no new source-fill write may begin; still resolve the
|
|
7447
7732
|
// caller's read with fresh source data, just don't cache it into a table that's going away.
|
|
7448
|
-
|
|
7733
|
+
// No write can commit on a read-only node, so a fill there is served but never staged.
|
|
7734
|
+
noCacheStore: droppingTable || (0, databases_ts_1.isReadOnlyMode)(),
|
|
7449
7735
|
source: null,
|
|
7450
7736
|
transaction: undefined,
|
|
7451
7737
|
expiresAt: undefined,
|
|
@@ -7769,14 +8055,11 @@ function makeTable(options) {
|
|
|
7769
8055
|
}
|
|
7770
8056
|
},
|
|
7771
8057
|
};
|
|
7772
|
-
// The
|
|
7773
|
-
//
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
const embedBefore = (0, embedHook_ts_1.buildEmbedBefore)(updatedRecord, sourceContext, undefined, TableResource.embedAttributes, TableResource.userEmbedders);
|
|
7778
|
-
if (embedBefore)
|
|
7779
|
-
await embedBefore();
|
|
8058
|
+
// The fill is shared by every reader, so it takes no request signal; a hook failure aborts
|
|
8059
|
+
// only the cache write.
|
|
8060
|
+
const modelHooksBefore = (0, embedHook_ts_1.combineWriteHooks)((0, embedHook_ts_1.buildEmbedBefore)(updatedRecord, sourceContext, undefined, TableResource.embedAttributes, TableResource.userEmbedders), (0, decideHook_ts_1.buildDecideBefore)(updatedRecord, sourceContext, undefined, TableResource.decideAttributes, TableResource.userDeciders));
|
|
8061
|
+
if (modelHooksBefore)
|
|
8062
|
+
await modelHooksBefore();
|
|
7780
8063
|
if (droppingTable) {
|
|
7781
8064
|
// Re-check right before staging the write: dropTable() may have started
|
|
7782
8065
|
// while we were awaiting the embed step above (harper#1381).
|
|
@@ -7811,6 +8094,19 @@ function makeTable(options) {
|
|
|
7811
8094
|
throw new hdbError_ts_1.ClientError('Can not specify replication confirmation without super user permissions', 403);
|
|
7812
8095
|
return true;
|
|
7813
8096
|
}
|
|
8097
|
+
function trackMaintenanceCommit(commit) {
|
|
8098
|
+
const tracked = commit.finally(() => maintenanceCommits.delete(tracked));
|
|
8099
|
+
maintenanceCommits.add(tracked);
|
|
8100
|
+
return tracked;
|
|
8101
|
+
}
|
|
8102
|
+
function stopMaintenance() {
|
|
8103
|
+
maintenanceClosed = true;
|
|
8104
|
+
clearTimeout(cleanupTimer);
|
|
8105
|
+
cleanupTimer = undefined;
|
|
8106
|
+
settlePendingCleanup();
|
|
8107
|
+
clearInterval(recordExpirationInterval);
|
|
8108
|
+
recordExpirationInterval = undefined;
|
|
8109
|
+
}
|
|
7814
8110
|
// RocksDB-only: coalesces eviction/tombstone removals into shared transactions so the cleanup
|
|
7815
8111
|
// scan pays one commit per batch instead of one per record. Descriptors hold only the decoded
|
|
7816
8112
|
// primary key and the version seen during the scan (both stable primitives — the scanned record
|
|
@@ -7875,7 +8171,7 @@ function makeTable(options) {
|
|
|
7875
8171
|
return;
|
|
7876
8172
|
}
|
|
7877
8173
|
try {
|
|
7878
|
-
await
|
|
8174
|
+
await (0, DatabaseTransaction_ts_1.commitTrackedRocksTransaction)(transaction, primaryStore);
|
|
7879
8175
|
return;
|
|
7880
8176
|
}
|
|
7881
8177
|
catch (error) {
|
|
@@ -7926,7 +8222,7 @@ function makeTable(options) {
|
|
|
7926
8222
|
}
|
|
7927
8223
|
function scheduleCleanup(priority) {
|
|
7928
8224
|
// a reclamation run may still hold this class's handler after cleanup(); a promise here would never settle
|
|
7929
|
-
if (disposed)
|
|
8225
|
+
if (disposed || maintenanceClosed)
|
|
7930
8226
|
return;
|
|
7931
8227
|
let runImmediately = false;
|
|
7932
8228
|
if (priority) {
|
|
@@ -7938,7 +8234,10 @@ function makeTable(options) {
|
|
|
7938
8234
|
// Periodically evict expired records and deleted records searching for records who expiresAt timestamp is before now
|
|
7939
8235
|
if (cleanupInterval === lastCleanupInterval && !runImmediately)
|
|
7940
8236
|
return;
|
|
7941
|
-
|
|
8237
|
+
// Left unrecorded by a thread with no worker index yet: under threads: 0 the main thread loads tables
|
|
8238
|
+
// before it becomes worker 0, and its next call for this interval has to arm the scan then.
|
|
8239
|
+
if ((0, manageThreads_js_1.getWorkerIndex)() !== undefined)
|
|
8240
|
+
lastCleanupInterval = cleanupInterval;
|
|
7942
8241
|
if ((0, manageThreads_js_1.ownsStoreMaintenance)(primaryStore.path) || (ttlConfiguredByApplication && (0, manageThreads_js_1.isDedicatedWorker)())) {
|
|
7943
8242
|
// run on the last thread so we aren't overloading lower-numbered threads
|
|
7944
8243
|
if (cleanupTimer)
|
|
@@ -7968,11 +8267,13 @@ function makeTable(options) {
|
|
|
7968
8267
|
? Date.now()
|
|
7969
8268
|
: Math.ceil((Date.now() - startOfYear.getTime()) / nextInterval) * nextInterval + startOfYear.getTime();
|
|
7970
8269
|
const startNextTimer = (nextScheduled) => {
|
|
7971
|
-
if (disposed)
|
|
8270
|
+
if (disposed || maintenanceClosed)
|
|
7972
8271
|
return;
|
|
7973
8272
|
logger_ts_1.logger.trace?.(`Scheduled next cleanup scan at ${new Date(nextScheduled)}`);
|
|
7974
8273
|
// noinspection JSVoidFunctionReturnValueUsed
|
|
7975
8274
|
cleanupTimer = setTimeout(() => (lastEvictionCompletion = lastEvictionCompletion.then(async () => {
|
|
8275
|
+
if (disposed || maintenanceClosed)
|
|
8276
|
+
return;
|
|
7976
8277
|
// schedule the next run for when the next cleanup interval should occur (or now if it is in the past)
|
|
7977
8278
|
startNextTimer(Math.max(nextScheduled + cleanupInterval, Date.now()));
|
|
7978
8279
|
const rootStore = primaryStore.rootStore;
|
|
@@ -8022,6 +8323,8 @@ function makeTable(options) {
|
|
|
8022
8323
|
versions: true,
|
|
8023
8324
|
lazy: true, // only want to access metadata most of the time
|
|
8024
8325
|
})) {
|
|
8326
|
+
if (maintenanceClosed)
|
|
8327
|
+
break;
|
|
8025
8328
|
const { key, value: record, version, expiresAt, metadataFlags } = entry;
|
|
8026
8329
|
// if there is no auditing cleanup and we are tracking deletion, need to do cleanup of
|
|
8027
8330
|
// these deletion entries (LMDB audit cleanup has its own scheduled job for this)
|
|
@@ -8055,6 +8358,7 @@ function makeTable(options) {
|
|
|
8055
8358
|
}
|
|
8056
8359
|
await rest();
|
|
8057
8360
|
}
|
|
8361
|
+
await Promise.all(outstandingCleanupOperations.filter(Boolean));
|
|
8058
8362
|
if (batcher)
|
|
8059
8363
|
await batcher.drain();
|
|
8060
8364
|
logger_ts_1.logger.debug?.(`Finished cleanup scan for ${tableName}, evicted ${count} entries`);
|
|
@@ -8083,45 +8387,59 @@ function makeTable(options) {
|
|
|
8083
8387
|
// Periodically evict expired records, searching for records who expiresAt timestamp is before now
|
|
8084
8388
|
if ((0, manageThreads_js_1.ownsStoreExpiration)(primaryStore.path) || (ttlConfiguredByApplication && (0, manageThreads_js_1.isDedicatedWorker)())) {
|
|
8085
8389
|
// we want to run the pruning of expired records on only one thread so we don't have conflicts in evicting
|
|
8086
|
-
recordExpirationInterval = setInterval(
|
|
8390
|
+
recordExpirationInterval = setInterval(() => {
|
|
8087
8391
|
// go through each database and table and then search for expired entries
|
|
8088
8392
|
// find any entries that are set to expire before now
|
|
8089
8393
|
// updatedAttributes() clears expiresAtProperty when a live redeclaration drops the directive,
|
|
8090
8394
|
// and there is nothing left for this interval to scan by
|
|
8091
|
-
if (disposed || runningRecordExpiration || !expiresAtProperty)
|
|
8395
|
+
if (disposed || maintenanceClosed || runningRecordExpiration || !expiresAtProperty)
|
|
8092
8396
|
return;
|
|
8093
8397
|
runningRecordExpiration = true;
|
|
8094
|
-
|
|
8398
|
+
recordExpirationCompletion = (async () => {
|
|
8095
8399
|
const expiresAtName = expiresAtProperty.name;
|
|
8096
8400
|
const index = indices[expiresAtName];
|
|
8097
8401
|
if (!index)
|
|
8098
8402
|
throw new Error(`expiresAt attribute ${expiresAtProperty} must be indexed`);
|
|
8099
|
-
|
|
8403
|
+
const inFlight = new Set();
|
|
8404
|
+
const trackInFlight = (operation) => {
|
|
8405
|
+
const tracked = operation.finally(() => inFlight.delete(tracked));
|
|
8406
|
+
inFlight.add(tracked);
|
|
8407
|
+
if (inFlight.size >= 50)
|
|
8408
|
+
return Promise.race(inFlight);
|
|
8409
|
+
};
|
|
8410
|
+
expirationScan: for (const key of index.getRange({
|
|
8100
8411
|
start: true,
|
|
8101
8412
|
values: false,
|
|
8102
8413
|
end: Date.now(),
|
|
8103
8414
|
snapshot: false,
|
|
8104
8415
|
})) {
|
|
8105
8416
|
for (const id of index.getValues(key)) {
|
|
8417
|
+
if (maintenanceClosed)
|
|
8418
|
+
break expirationScan;
|
|
8106
8419
|
const recordEntry = primaryStore.getEntry(id);
|
|
8107
8420
|
if (!recordEntry?.value) {
|
|
8108
8421
|
// cleanup the index if the record is gone
|
|
8109
|
-
primaryStore.ifVersion(id, recordEntry?.version, () => index.remove(key, id));
|
|
8422
|
+
const repair = trackMaintenanceCommit(Promise.resolve(primaryStore.ifVersion(id, recordEntry?.version, () => index.remove(key, id))).catch((error) => logger_ts_1.logger.warn?.('Error removing stale expiration index entry', id, error)));
|
|
8423
|
+
const backpressure = trackInFlight(repair);
|
|
8424
|
+
if (backpressure)
|
|
8425
|
+
await backpressure;
|
|
8110
8426
|
}
|
|
8111
8427
|
else if (recordEntry.value[expiresAtName] < Date.now()) {
|
|
8112
8428
|
// make sure the record hasn't changed and won't change while removing
|
|
8113
|
-
TableResource.evict(id, recordEntry.value, recordEntry.version);
|
|
8429
|
+
const eviction = TableResource.evict(id, recordEntry.value, recordEntry.version);
|
|
8430
|
+
if (eviction) {
|
|
8431
|
+
const backpressure = trackInFlight(eviction);
|
|
8432
|
+
if (backpressure)
|
|
8433
|
+
await backpressure;
|
|
8434
|
+
}
|
|
8114
8435
|
}
|
|
8115
8436
|
}
|
|
8116
8437
|
await rest();
|
|
8117
8438
|
}
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
logger_ts_1.logger.error?.('Error in evicting old records', error)
|
|
8121
|
-
|
|
8122
|
-
finally {
|
|
8123
|
-
runningRecordExpiration = false;
|
|
8124
|
-
}
|
|
8439
|
+
await Promise.all(inFlight);
|
|
8440
|
+
})()
|
|
8441
|
+
.catch((error) => logger_ts_1.logger.error?.('Error in evicting old records', error))
|
|
8442
|
+
.finally(() => (runningRecordExpiration = false));
|
|
8125
8443
|
}, RECORD_PRUNING_INTERVAL).unref();
|
|
8126
8444
|
}
|
|
8127
8445
|
}
|