@harperfast/harper 5.2.10 → 5.3.0-alpha.1
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/agent/mcpTools.ts +1 -1
- package/agent/session.ts +25 -14
- package/bin/cliOperations.ts +46 -9
- package/bin/copyDb.ts +282 -59
- package/bin/deploySetup.ts +16 -5
- package/bin/harper.ts +1 -1
- package/bin/help.ts +4 -1
- package/bin/lite.ts +4 -1
- package/bin/restart.ts +59 -3
- package/bin/run.ts +6 -11
- package/bin/upgrade.js +7 -3
- package/bin/workloadIdentity.ts +119 -0
- package/components/Application.ts +2414 -242
- package/components/ApplicationScope.ts +8 -0
- package/components/EntryHandler.ts +59 -39
- package/components/OptionsWatcher.ts +150 -74
- package/components/RuntimeModuleTracker.ts +38 -7
- package/components/Scope.ts +37 -15
- package/components/awaitRestart.ts +84 -0
- package/components/componentLoader.ts +340 -30
- package/components/componentPreparationLock.ts +16 -5
- package/components/mcp/adapters/harperHttp.ts +4 -0
- package/components/mcp/listChanged.ts +4 -0
- package/components/mcp/toolRegistry.ts +2 -0
- package/components/mcp/tools/operations.ts +9 -0
- package/components/mcp/tools/schemas/operationDescriptions.ts +2 -2
- package/components/operations.js +270 -78
- package/components/operationsValidation.js +49 -1
- package/components/status/ComponentStatusRegistry.ts +59 -0
- package/config/RootConfigWatcher.ts +80 -34
- package/config/configUtils.ts +291 -34
- package/config/harperConfigEnvVars.ts +170 -27
- package/config-root.schema.json +29 -0
- package/dataLayer/blobBackup.ts +160 -50
- package/dataLayer/delete.ts +6 -1
- package/dataLayer/harperBridge/ResourceBridge.ts +52 -8
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
- package/dataLayer/hdbInfoController.ts +34 -1
- package/dataLayer/insert.ts +44 -1
- package/dataLayer/rocksdbBackup.ts +53 -10
- package/dataLayer/schema.ts +11 -1
- package/dataLayer/schemaDescribe.ts +8 -1
- package/dist/agent/mcpTools.js +1 -1
- package/dist/agent/mcpTools.js.map +1 -1
- package/dist/agent/session.d.ts +22 -0
- package/dist/agent/session.js +26 -15
- package/dist/agent/session.js.map +1 -1
- package/dist/bin/cliOperations.js +48 -9
- package/dist/bin/cliOperations.js.map +1 -1
- package/dist/bin/copyDb.d.ts +12 -1
- package/dist/bin/copyDb.js +248 -60
- package/dist/bin/copyDb.js.map +1 -1
- package/dist/bin/deploySetup.d.ts +2 -0
- package/dist/bin/deploySetup.js +11 -3
- package/dist/bin/deploySetup.js.map +1 -1
- package/dist/bin/harper.js +1 -1
- package/dist/bin/harper.js.map +1 -1
- package/dist/bin/help.js +4 -1
- package/dist/bin/help.js.map +1 -1
- package/dist/bin/lite.js +4 -1
- package/dist/bin/lite.js.map +1 -1
- package/dist/bin/restart.js +54 -5
- package/dist/bin/restart.js.map +1 -1
- package/dist/bin/run.js +4 -10
- package/dist/bin/run.js.map +1 -1
- package/dist/bin/upgrade.js +4 -3
- package/dist/bin/upgrade.js.map +1 -1
- package/dist/bin/workloadIdentity.d.ts +18 -0
- package/dist/bin/workloadIdentity.js +100 -0
- package/dist/bin/workloadIdentity.js.map +1 -0
- package/dist/components/Application.d.ts +139 -16
- package/dist/components/Application.js +2215 -267
- package/dist/components/Application.js.map +1 -1
- package/dist/components/ApplicationScope.d.ts +8 -0
- package/dist/components/ApplicationScope.js +7 -0
- package/dist/components/ApplicationScope.js.map +1 -1
- package/dist/components/EntryHandler.js +26 -10
- package/dist/components/EntryHandler.js.map +1 -1
- package/dist/components/OptionsWatcher.d.ts +1 -0
- package/dist/components/OptionsWatcher.js +141 -74
- package/dist/components/OptionsWatcher.js.map +1 -1
- package/dist/components/RuntimeModuleTracker.js +40 -6
- package/dist/components/RuntimeModuleTracker.js.map +1 -1
- package/dist/components/Scope.js +38 -13
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/awaitRestart.d.ts +33 -0
- package/dist/components/awaitRestart.js +61 -0
- package/dist/components/awaitRestart.js.map +1 -0
- package/dist/components/componentLoader.d.ts +38 -1
- package/dist/components/componentLoader.js +279 -22
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/componentPreparationLock.d.ts +5 -0
- package/dist/components/componentPreparationLock.js +14 -6
- package/dist/components/componentPreparationLock.js.map +1 -1
- package/dist/components/mcp/adapters/harperHttp.js +4 -0
- package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
- package/dist/components/mcp/listChanged.js +5 -0
- package/dist/components/mcp/listChanged.js.map +1 -1
- package/dist/components/mcp/toolRegistry.d.ts +1 -0
- package/dist/components/mcp/toolRegistry.js.map +1 -1
- package/dist/components/mcp/tools/operations.d.ts +5 -0
- package/dist/components/mcp/tools/operations.js +9 -0
- package/dist/components/mcp/tools/operations.js.map +1 -1
- package/dist/components/mcp/tools/schemas/operationDescriptions.js +2 -2
- package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
- package/dist/components/operations.js +231 -77
- package/dist/components/operations.js.map +1 -1
- package/dist/components/operationsValidation.js +49 -1
- package/dist/components/operationsValidation.js.map +1 -1
- package/dist/components/status/ComponentStatusRegistry.d.ts +0 -4
- package/dist/components/status/ComponentStatusRegistry.js +63 -0
- package/dist/components/status/ComponentStatusRegistry.js.map +1 -1
- package/dist/config/RootConfigWatcher.d.ts +7 -1
- package/dist/config/RootConfigWatcher.js +64 -27
- package/dist/config/RootConfigWatcher.js.map +1 -1
- package/dist/config/configUtils.d.ts +9 -1
- package/dist/config/configUtils.js +254 -33
- package/dist/config/configUtils.js.map +1 -1
- package/dist/config/harperConfigEnvVars.d.ts +16 -0
- package/dist/config/harperConfigEnvVars.js +162 -25
- package/dist/config/harperConfigEnvVars.js.map +1 -1
- package/dist/dataLayer/blobBackup.d.ts +49 -20
- package/dist/dataLayer/blobBackup.js +139 -50
- package/dist/dataLayer/blobBackup.js.map +1 -1
- package/dist/dataLayer/delete.js +1 -1
- package/dist/dataLayer/delete.js.map +1 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +14 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.js +51 -10
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.d.ts +3 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js.map +1 -1
- package/dist/dataLayer/hdbInfoController.d.ts +10 -0
- package/dist/dataLayer/hdbInfoController.js +30 -1
- package/dist/dataLayer/hdbInfoController.js.map +1 -1
- package/dist/dataLayer/insert.d.ts +9 -1
- package/dist/dataLayer/insert.js +30 -0
- package/dist/dataLayer/insert.js.map +1 -1
- package/dist/dataLayer/rocksdbBackup.d.ts +2 -2
- package/dist/dataLayer/rocksdbBackup.js +45 -8
- package/dist/dataLayer/rocksdbBackup.js.map +1 -1
- package/dist/dataLayer/schema.js +8 -0
- package/dist/dataLayer/schema.js.map +1 -1
- package/dist/dataLayer/schemaDescribe.js +8 -1
- 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 +52 -0
- package/dist/resources/DatabaseTransaction.d.ts +55 -3
- package/dist/resources/DatabaseTransaction.js +460 -134
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/LMDBTransaction.js +22 -4
- package/dist/resources/LMDBTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.d.ts +1 -0
- package/dist/resources/PrimaryRocksDatabase.js +30 -2
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
- package/dist/resources/RecordEncoder.d.ts +20 -0
- package/dist/resources/RecordEncoder.js +94 -9
- package/dist/resources/RecordEncoder.js.map +1 -1
- package/dist/resources/RequestTarget.d.ts +2 -0
- package/dist/resources/RequestTarget.js.map +1 -1
- package/dist/resources/Resource.js +20 -11
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +20 -1
- package/dist/resources/ResourceInterface.js.map +1 -1
- package/dist/resources/RocksIndexStore.d.ts +6 -1
- package/dist/resources/RocksIndexStore.js +24 -10
- package/dist/resources/RocksIndexStore.js.map +1 -1
- package/dist/resources/RocksTransactionLogStore.d.ts +14 -1
- package/dist/resources/RocksTransactionLogStore.js +57 -17
- package/dist/resources/RocksTransactionLogStore.js.map +1 -1
- package/dist/resources/Table.d.ts +112 -8
- package/dist/resources/Table.js +1071 -197
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/auditStore.d.ts +11 -2
- package/dist/resources/auditStore.js +183 -18
- package/dist/resources/auditStore.js.map +1 -1
- package/dist/resources/blob.d.ts +129 -9
- package/dist/resources/blob.js +992 -114
- package/dist/resources/blob.js.map +1 -1
- package/dist/resources/branchDatabase.d.ts +48 -0
- package/dist/resources/branchDatabase.js +892 -0
- package/dist/resources/branchDatabase.js.map +1 -0
- package/dist/resources/crdt.js +50 -12
- package/dist/resources/crdt.js.map +1 -1
- package/dist/resources/databases.d.ts +150 -9
- package/dist/resources/databases.js +1247 -525
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/defineTable.d.ts +10 -2
- package/dist/resources/defineTable.js +9 -1
- package/dist/resources/defineTable.js.map +1 -1
- package/dist/resources/graphql.d.ts +1 -1
- package/dist/resources/graphql.js +50 -15
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +1 -0
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +6 -2
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/longLivedTransactions.d.ts +71 -0
- package/dist/resources/longLivedTransactions.js +358 -0
- package/dist/resources/longLivedTransactions.js.map +1 -0
- package/dist/resources/models/backendRegistry.d.ts +26 -0
- package/dist/resources/models/backendRegistry.js +60 -2
- package/dist/resources/models/backendRegistry.js.map +1 -1
- package/dist/resources/models/bootstrap.d.ts +33 -1
- package/dist/resources/models/bootstrap.js +416 -31
- package/dist/resources/models/bootstrap.js.map +1 -1
- package/dist/resources/recordLock.d.ts +80 -0
- package/dist/resources/recordLock.js +184 -0
- package/dist/resources/recordLock.js.map +1 -0
- package/dist/resources/replayLogs.d.ts +14 -1
- package/dist/resources/replayLogs.js +152 -24
- package/dist/resources/replayLogs.js.map +1 -1
- package/dist/resources/replayLogsGuards.d.ts +94 -7
- package/dist/resources/replayLogsGuards.js +111 -7
- package/dist/resources/replayLogsGuards.js.map +1 -1
- package/dist/resources/search.js +158 -32
- package/dist/resources/search.js.map +1 -1
- package/dist/resources/transactionBroadcast.d.ts +1 -1
- package/dist/resources/transactionBroadcast.js +2 -2
- package/dist/resources/transactionBroadcast.js.map +1 -1
- package/dist/security/auth.js +61 -30
- package/dist/security/auth.js.map +1 -1
- package/dist/security/authn/oidc/claims.d.ts +22 -0
- package/dist/security/authn/oidc/claims.js +71 -0
- package/dist/security/authn/oidc/claims.js.map +1 -0
- package/dist/security/authn/oidc/identityToken.d.ts +27 -0
- package/dist/security/authn/oidc/identityToken.js +111 -0
- package/dist/security/authn/oidc/identityToken.js.map +1 -0
- package/dist/security/authn/oidc/jwks.d.ts +25 -0
- package/dist/security/authn/oidc/jwks.js +261 -0
- package/dist/security/authn/oidc/jwks.js.map +1 -0
- package/dist/security/authn/oidc/providers/generic.d.ts +13 -0
- package/dist/security/authn/oidc/providers/generic.js +34 -0
- package/dist/security/authn/oidc/providers/generic.js.map +1 -0
- package/dist/security/authn/oidc/providers/githubActions.d.ts +11 -0
- package/dist/security/authn/oidc/providers/githubActions.js +129 -0
- package/dist/security/authn/oidc/providers/githubActions.js.map +1 -0
- package/dist/security/authn/oidc/providers/index.d.ts +37 -0
- package/dist/security/authn/oidc/providers/index.js +24 -0
- package/dist/security/authn/oidc/providers/index.js.map +1 -0
- package/dist/security/authn/oidc/tokenExchange.d.ts +12 -0
- package/dist/security/authn/oidc/tokenExchange.js +306 -0
- package/dist/security/authn/oidc/tokenExchange.js.map +1 -0
- package/dist/security/authn/oidc/trustPolicyOperations.d.ts +49 -0
- package/dist/security/authn/oidc/trustPolicyOperations.js +358 -0
- package/dist/security/authn/oidc/trustPolicyOperations.js.map +1 -0
- package/dist/security/authn/oidc/types.d.ts +38 -0
- package/dist/security/authn/oidc/types.js +6 -0
- package/dist/security/authn/oidc/types.js.map +1 -0
- package/dist/security/certificateVerification/index.js +40 -11
- package/dist/security/certificateVerification/index.js.map +1 -1
- package/dist/security/certificateVerification/trustedIssuers.d.ts +24 -0
- package/dist/security/certificateVerification/trustedIssuers.js +79 -0
- package/dist/security/certificateVerification/trustedIssuers.js.map +1 -0
- package/dist/security/certificateVerification/types.d.ts +1 -0
- package/dist/security/credentialProvenance.d.ts +35 -0
- package/dist/security/credentialProvenance.js +51 -0
- package/dist/security/credentialProvenance.js.map +1 -0
- package/dist/security/credentialRejection.d.ts +4 -0
- package/dist/security/credentialRejection.js +24 -0
- package/dist/security/credentialRejection.js.map +1 -0
- package/dist/security/deferredAuthentication.d.ts +36 -0
- package/dist/security/deferredAuthentication.js +70 -0
- package/dist/security/deferredAuthentication.js.map +1 -0
- package/dist/security/impersonation.d.ts +21 -0
- package/dist/security/impersonation.js +108 -9
- package/dist/security/impersonation.js.map +1 -1
- package/dist/security/jsLoader.d.ts +6 -0
- package/dist/security/jsLoader.js +75 -15
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/security/keys.js +301 -71
- package/dist/security/keys.js.map +1 -1
- package/dist/security/operationScope.d.ts +21 -0
- package/dist/security/operationScope.js +36 -0
- package/dist/security/operationScope.js.map +1 -0
- package/dist/security/permissionsTranslator.js +21 -0
- package/dist/security/permissionsTranslator.js.map +1 -1
- package/dist/security/tokenAuthentication.d.ts +19 -1
- package/dist/security/tokenAuthentication.js +191 -10
- package/dist/security/tokenAuthentication.js.map +1 -1
- package/dist/security/user.js +4 -3
- package/dist/security/user.js.map +1 -1
- package/dist/server/DurableSubscriptionsSession.d.ts +2 -2
- package/dist/server/DurableSubscriptionsSession.js +67 -10
- package/dist/server/DurableSubscriptionsSession.js.map +1 -1
- package/dist/server/REST.js +73 -0
- package/dist/server/REST.js.map +1 -1
- package/dist/server/graphqlQuerying.js +4 -0
- package/dist/server/graphqlQuerying.js.map +1 -1
- package/dist/server/http.d.ts +11 -0
- package/dist/server/http.js +88 -15
- package/dist/server/http.js.map +1 -1
- package/dist/server/itc/serverHandlers.js +8 -1
- package/dist/server/itc/serverHandlers.js.map +1 -1
- package/dist/server/jobs/jobProcess.js +6 -2
- package/dist/server/jobs/jobProcess.js.map +1 -1
- package/dist/server/jobs/jobs.js +4 -1
- package/dist/server/jobs/jobs.js.map +1 -1
- package/dist/server/liveSubscriptionAuth.d.ts +26 -4
- package/dist/server/liveSubscriptionAuth.js +105 -39
- package/dist/server/liveSubscriptionAuth.js.map +1 -1
- package/dist/server/loadRootComponents.js +49 -10
- package/dist/server/loadRootComponents.js.map +1 -1
- package/dist/server/mqtt.d.ts +2 -0
- package/dist/server/mqtt.js +165 -30
- package/dist/server/mqtt.js.map +1 -1
- package/dist/server/nodeName.d.ts +2 -0
- package/dist/server/nodeName.js +107 -23
- package/dist/server/nodeName.js.map +1 -1
- package/dist/server/serverHelpers/Headers.d.ts +25 -0
- package/dist/server/serverHelpers/Headers.js +139 -1
- package/dist/server/serverHelpers/Headers.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.d.ts +9 -0
- package/dist/server/serverHelpers/contentTypes.js +32 -25
- package/dist/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/server/serverHelpers/deployValidationState.d.ts +3 -0
- package/dist/server/serverHelpers/deployValidationState.js +9 -19
- package/dist/server/serverHelpers/deployValidationState.js.map +1 -1
- package/dist/server/serverHelpers/operationAuthorizationState.d.ts +12 -0
- package/dist/server/serverHelpers/operationAuthorizationState.js +24 -2
- package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -1
- package/dist/server/serverHelpers/registeredOperations.d.ts +5 -4
- package/dist/server/serverHelpers/registeredOperations.js +74 -21
- package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
- package/dist/server/serverHelpers/requestSanitization.d.ts +11 -0
- package/dist/server/serverHelpers/requestSanitization.js +20 -0
- package/dist/server/serverHelpers/requestSanitization.js.map +1 -0
- package/dist/server/serverHelpers/serverHandlers.js +6 -3
- package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
- package/dist/server/serverHelpers/serverUtilities.d.ts +18 -0
- package/dist/server/serverHelpers/serverUtilities.js +90 -20
- package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
- package/dist/server/serverHelpers/sharedMessageEncoding.d.ts +67 -0
- package/dist/server/serverHelpers/sharedMessageEncoding.js +280 -0
- package/dist/server/serverHelpers/sharedMessageEncoding.js.map +1 -0
- package/dist/server/serverHelpers/uwsServer.js +19 -1
- package/dist/server/serverHelpers/uwsServer.js.map +1 -1
- package/dist/server/static.js +24 -28
- package/dist/server/static.js.map +1 -1
- package/dist/server/storageReclamation.d.ts +5 -0
- package/dist/server/storageReclamation.js +16 -0
- package/dist/server/storageReclamation.js.map +1 -1
- package/dist/server/threads/itc.d.ts +7 -2
- package/dist/server/threads/itc.js +5 -1
- package/dist/server/threads/itc.js.map +1 -1
- package/dist/server/threads/manageThreads.d.ts +26 -4
- package/dist/server/threads/manageThreads.js +517 -72
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/socketRouter.js +89 -26
- package/dist/server/threads/socketRouter.js.map +1 -1
- package/dist/server/threads/threadHeapMemory.d.ts +2 -0
- package/dist/server/threads/threadHeapMemory.js +31 -0
- package/dist/server/threads/threadHeapMemory.js.map +1 -0
- package/dist/server/threads/threadServer.js +46 -15
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/sqlEngine/config.d.ts +1 -3
- package/dist/sqlEngine/config.js +19 -16
- package/dist/sqlEngine/config.js.map +1 -1
- package/dist/sqlTranslator/index.d.ts +1 -1
- package/dist/sqlTranslator/index.js +30 -7
- package/dist/sqlTranslator/index.js.map +1 -1
- package/dist/upgrade/directives/5-3-0.d.ts +7 -0
- package/dist/upgrade/directives/5-3-0.js +148 -0
- package/dist/upgrade/directives/5-3-0.js.map +1 -0
- package/dist/upgrade/directives/directivesController.js +2 -1
- package/dist/upgrade/directives/directivesController.js.map +1 -1
- package/dist/utility/OperationFunctionCaller.js +2 -1
- package/dist/utility/OperationFunctionCaller.js.map +1 -1
- package/dist/utility/common_utils.d.ts +16 -0
- package/dist/utility/common_utils.js +32 -6
- package/dist/utility/common_utils.js.map +1 -1
- package/dist/utility/componentNames.d.ts +8 -0
- package/dist/utility/componentNames.js +12 -1
- package/dist/utility/componentNames.js.map +1 -1
- package/dist/utility/environment/environmentManager.js +3 -6
- package/dist/utility/environment/environmentManager.js.map +1 -1
- package/dist/utility/environment/systemInformation.d.ts +1 -0
- package/dist/utility/environment/systemInformation.js +1 -0
- package/dist/utility/environment/systemInformation.js.map +1 -1
- package/dist/utility/errors/commonErrors.d.ts +2 -0
- package/dist/utility/errors/commonErrors.js +2 -0
- package/dist/utility/errors/commonErrors.js.map +1 -1
- package/dist/utility/errors/hdbError.d.ts +17 -0
- package/dist/utility/errors/hdbError.js +30 -1
- package/dist/utility/errors/hdbError.js.map +1 -1
- package/dist/utility/globalSchema.d.ts +18 -0
- package/dist/utility/hdbTerms.d.ts +16 -0
- package/dist/utility/hdbTerms.js +18 -2
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/logging/harper_logger.d.ts +2 -0
- package/dist/utility/logging/harper_logger.js +91 -14
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/dist/utility/logging/logRotator.js +76 -49
- package/dist/utility/logging/logRotator.js.map +1 -1
- package/dist/utility/nodeIdentity.d.ts +9 -0
- package/dist/utility/nodeIdentity.js +58 -0
- package/dist/utility/nodeIdentity.js.map +1 -0
- package/dist/utility/npmUtilities.js +9 -7
- package/dist/utility/npmUtilities.js.map +1 -1
- package/dist/utility/operationPermissions.d.ts +3 -1
- package/dist/utility/operationPermissions.js +16 -1
- package/dist/utility/operationPermissions.js.map +1 -1
- package/dist/utility/operation_authorization.d.ts +10 -7
- package/dist/utility/operation_authorization.js +212 -41
- package/dist/utility/operation_authorization.js.map +1 -1
- package/dist/utility/watchPath.d.ts +29 -0
- package/dist/utility/watchPath.js +68 -0
- package/dist/utility/watchPath.js.map +1 -0
- package/dist/utility/watcherFallback.d.ts +86 -0
- package/dist/utility/watcherFallback.js +278 -1
- package/dist/utility/watcherFallback.js.map +1 -1
- package/dist/validation/configValidator.d.ts +12 -0
- package/dist/validation/configValidator.js +199 -72
- package/dist/validation/configValidator.js.map +1 -1
- package/dist/validation/installValidator.js +12 -0
- package/dist/validation/installValidator.js.map +1 -1
- package/dist/validation/validationWrapper.d.ts +11 -0
- package/dist/validation/validationWrapper.js +16 -3
- package/dist/validation/validationWrapper.js.map +1 -1
- package/index.ts +2 -0
- package/json/systemSchema.json +52 -0
- package/npm-shrinkwrap.json +235 -233
- package/package.json +8 -7
- package/resources/DESIGN.md +114 -52
- package/resources/DatabaseTransaction.ts +522 -130
- package/resources/LMDBTransaction.ts +26 -4
- package/resources/PrimaryRocksDatabase.ts +29 -3
- package/resources/RecordEncoder.ts +103 -8
- package/resources/RequestTarget.ts +2 -0
- package/resources/Resource.ts +17 -9
- package/resources/ResourceInterface.ts +23 -0
- package/resources/RocksIndexStore.ts +30 -10
- package/resources/RocksTransactionLogStore.ts +78 -21
- package/resources/Table.ts +1107 -164
- package/resources/auditStore.ts +187 -21
- package/resources/blob.ts +1029 -110
- package/resources/branchDatabase.ts +941 -0
- package/resources/crdt.ts +76 -12
- package/resources/databases.ts +1354 -522
- package/resources/defineTable.ts +18 -2
- package/resources/graphql.ts +70 -16
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +6 -2
- package/resources/longLivedTransactions.ts +360 -0
- package/resources/models/backendRegistry.ts +84 -2
- package/resources/models/bootstrap.ts +473 -28
- package/resources/recordLock.ts +253 -0
- package/resources/replayLogs.ts +147 -26
- package/resources/replayLogsGuards.ts +171 -8
- package/resources/search.ts +154 -32
- package/resources/transactionBroadcast.ts +3 -3
- package/security/auth.ts +68 -29
- package/security/authn/oidc/claims.ts +72 -0
- package/security/authn/oidc/identityToken.ts +129 -0
- package/security/authn/oidc/jwks.ts +260 -0
- package/security/authn/oidc/providers/generic.ts +40 -0
- package/security/authn/oidc/providers/githubActions.ts +137 -0
- package/security/authn/oidc/providers/index.ts +52 -0
- package/security/authn/oidc/tokenExchange.ts +300 -0
- package/security/authn/oidc/trustPolicyOperations.ts +343 -0
- package/security/authn/oidc/types.ts +41 -0
- package/security/certificateVerification/index.ts +54 -13
- package/security/certificateVerification/trustedIssuers.ts +76 -0
- package/security/certificateVerification/types.ts +1 -0
- package/security/credentialProvenance.ts +47 -0
- package/security/credentialRejection.ts +22 -0
- package/security/deferredAuthentication.ts +71 -0
- package/security/impersonation.ts +117 -12
- package/security/jsLoader.ts +81 -18
- package/security/keys.ts +298 -72
- package/security/operationScope.ts +33 -0
- package/security/permissionsTranslator.js +23 -0
- package/security/tokenAuthentication.ts +233 -12
- package/security/user.ts +4 -3
- package/server/DESIGN.md +183 -16
- package/server/DurableSubscriptionsSession.ts +71 -11
- package/server/REST.ts +79 -1
- package/server/graphqlQuerying.ts +4 -0
- package/server/http.ts +99 -18
- package/server/itc/serverHandlers.js +8 -1
- package/server/jobs/jobProcess.ts +8 -2
- package/server/jobs/jobs.ts +4 -1
- package/server/liveSubscriptionAuth.ts +129 -46
- package/server/loadRootComponents.js +50 -8
- package/server/mqtt.ts +179 -38
- package/server/nodeName.ts +103 -21
- package/server/serverHelpers/Headers.ts +131 -0
- package/server/serverHelpers/contentTypes.ts +29 -21
- package/server/serverHelpers/deployValidationState.ts +24 -13
- package/server/serverHelpers/operationAuthorizationState.ts +34 -3
- package/server/serverHelpers/registeredOperations.ts +79 -22
- package/server/serverHelpers/requestSanitization.ts +15 -0
- package/server/serverHelpers/serverHandlers.js +6 -3
- package/server/serverHelpers/serverUtilities.ts +104 -31
- package/server/serverHelpers/sharedMessageEncoding.ts +307 -0
- package/server/serverHelpers/uwsServer.ts +17 -2
- package/server/static.ts +23 -29
- package/server/storageReclamation.ts +13 -0
- package/server/threads/itc.js +11 -1
- package/server/threads/manageThreads.js +526 -63
- package/server/threads/socketRouter.ts +81 -26
- package/server/threads/threadHeapMemory.ts +26 -0
- package/server/threads/threadServer.js +43 -15
- package/sqlTranslator/index.ts +31 -8
- package/studio/web/assets/{Chat-Br06zdMA.js → Chat-BnCBegQz.js} +1 -1
- package/studio/web/assets/{FloatingChat-BWImX5fA.js → FloatingChat-CoDW1ySS.js} +3 -3
- package/studio/web/assets/{apiToken-DN0nmDsq.js → apiToken-Bwk5BLXW.js} +1 -1
- package/studio/web/assets/{applications-kSxVoyeU.js → applications-DHxGi7JH.js} +1 -1
- package/studio/web/assets/{cssMode-C1JeufH5.js → cssMode-s0cWI-_M.js} +1 -1
- package/studio/web/assets/{editor-qoo9CrGO.js → editor-DNcRHK54.js} +1 -1
- package/studio/web/assets/{html-Dt4IIy04.js → html-Bdssedlg.js} +1 -1
- package/studio/web/assets/{htmlMode-DXgKKr4C.js → htmlMode-CoDlJ3fw.js} +1 -1
- package/studio/web/assets/{index-6onkYFOG.js → index-D6sxmFLR.js} +5 -5
- package/studio/web/assets/{index.lazy-BrCFnpNJ.js → index.lazy-tmU5BS8s.js} +1 -1
- package/studio/web/assets/{javascript-DNCQGUBc.js → javascript-B8meVSTH.js} +1 -1
- package/studio/web/assets/{jsonMode-CR6HWruP.js → jsonMode-DpIPd35T.js} +1 -1
- package/studio/web/assets/{languageServices-BM4fI4rS.js → languageServices-C_5FMJzQ.js} +1 -1
- package/studio/web/assets/{lspLanguageFeatures-DSa1ttcD.js → lspLanguageFeatures-BIzNBzPK.js} +1 -1
- package/studio/web/assets/{notifications-BHXLnh6x.js → notifications-CQf18QKb.js} +1 -1
- package/studio/web/assets/{notifications-CUmtIA6z.js → notifications-CvZivSbh.js} +1 -1
- package/studio/web/assets/{profile-8BeFSF3j.js → profile-DdOwtntb.js} +1 -1
- package/studio/web/assets/{regions-C8qR0HhD.js → regions-n69fwagr.js} +1 -1
- package/studio/web/assets/{register-B4n5i0SD.js → register-PfWTCXWB.js} +2 -2
- package/studio/web/assets/{setComponentFile-g0_B0lgX.js → setComponentFile-Bg6O7X0S.js} +1 -1
- package/studio/web/assets/{setup-B0CTj_Q5.js → setup-CUx_aUDl.js} +2 -2
- package/studio/web/assets/{status-C6Yib7-K.js → status-D7BVKqX9.js} +1 -1
- package/studio/web/assets/{toggleHighContrast-Dgta7bVi.js → toggleHighContrast-DBSyXzMr.js} +1 -1
- package/studio/web/assets/{tsMode-CH_jHvU-.js → tsMode-BByKCjBS.js} +1 -1
- package/studio/web/assets/{typescript-Co9LCXd5.js → typescript-DDLnLpw9.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-wKC8NsC_.js → useEntityRestURL-31CHGaHk.js} +1 -1
- package/studio/web/assets/{workers-CWeLxCXA.js → workers-pR3jRY9D.js} +1 -1
- package/studio/web/assets/{xml-BadC-0Rk.js → xml-2iRnMhQO.js} +1 -1
- package/studio/web/assets/{yaml-BiUfxPbC.js → yaml-Bf92gJpd.js} +1 -1
- package/studio/web/index.html +1 -1
- package/upgrade/directives/5-3-0.ts +132 -0
- package/upgrade/directives/directivesController.ts +2 -1
- package/utility/OperationFunctionCaller.ts +2 -1
- package/utility/common_utils.ts +30 -5
- package/utility/componentNames.ts +12 -0
- package/utility/environment/environmentManager.ts +3 -7
- package/utility/environment/systemInformation.ts +7 -0
- package/utility/errors/commonErrors.ts +4 -0
- package/utility/errors/hdbError.ts +29 -0
- package/utility/hdbTerms.ts +17 -0
- package/utility/logging/harper_logger.ts +87 -14
- package/utility/logging/logRotator.ts +72 -45
- package/utility/nodeIdentity.ts +45 -0
- package/utility/npmUtilities.ts +10 -8
- package/utility/operationPermissions.ts +18 -1
- package/utility/operation_authorization.ts +231 -42
- package/utility/watchPath.ts +63 -0
- package/utility/watcherFallback.ts +270 -0
- package/validation/configValidator.ts +209 -72
- package/validation/installValidator.ts +15 -0
- package/validation/validationWrapper.ts +18 -4
|
@@ -33,24 +33,27 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.RELEASED_TRANSACTION = exports.ImmediateTransaction = exports.DatabaseTransaction = exports.TRANSACTION_STATE = void 0;
|
|
36
|
+
exports.RELEASED_TRANSACTION = exports.ImmediateTransaction = exports.DatabaseTransaction = exports.COMMIT_PHASE_GRACE = exports.TRANSACTION_STATE = void 0;
|
|
37
37
|
exports.trackOutstandingCommit = trackOutstandingCommit;
|
|
38
38
|
exports.getOutstandingCommits = getOutstandingCommits;
|
|
39
39
|
exports.setCommitLatencyRecorder = setCommitLatencyRecorder;
|
|
40
40
|
exports.getTransactionQueueDepths = getTransactionQueueDepths;
|
|
41
41
|
exports.replicationConfirmation = replicationConfirmation;
|
|
42
42
|
exports.transactionOpenTooLongError = transactionOpenTooLongError;
|
|
43
|
-
exports.
|
|
43
|
+
exports.getAppliedWriteVersion = getAppliedWriteVersion;
|
|
44
44
|
exports.priorStagedWrite = priorStagedWrite;
|
|
45
45
|
exports.writeKeyId = writeKeyId;
|
|
46
|
+
exports.shouldSpareCommitPhase = shouldSpareCommitPhase;
|
|
46
47
|
exports.isReleasedTransaction = isReleasedTransaction;
|
|
47
48
|
exports.isJoinableScope = isJoinableScope;
|
|
48
49
|
exports.resetReplayedWritesWarning = resetReplayedWritesWarning;
|
|
50
|
+
exports.setMaxOutstandingTxnDuration = setMaxOutstandingTxnDuration;
|
|
49
51
|
exports.isWriteSupervised = isWriteSupervised;
|
|
50
52
|
exports.setTxnExpiration = setTxnExpiration;
|
|
51
53
|
const blob_ts_1 = require("./blob.js");
|
|
52
54
|
const commonUtility_ts_1 = require("../utility/lmdb/commonUtility.js");
|
|
53
55
|
const hdbError_ts_1 = require("../utility/errors/hdbError.js");
|
|
56
|
+
const recordLock_ts_1 = require("./recordLock.js");
|
|
54
57
|
const harperLogger = __importStar(require("../utility/logging/harper_logger.js"));
|
|
55
58
|
const envMngr = __importStar(require("../utility/environment/environmentManager.js"));
|
|
56
59
|
const hdbTerms_ts_1 = require("../utility/hdbTerms.js");
|
|
@@ -60,8 +63,8 @@ const promises_1 = require("node:timers/promises");
|
|
|
60
63
|
const rocksdb_js_1 = require("@harperfast/rocksdb-js");
|
|
61
64
|
const RETRY_NOW_VALUE = rocksdb_js_1.constants.RETRY_NOW_VALUE;
|
|
62
65
|
const ordered_binary_1 = require("ordered-binary");
|
|
66
|
+
const longLivedTransactions_ts_1 = require("./longLivedTransactions.js");
|
|
63
67
|
const trackedTxns = new Set();
|
|
64
|
-
const readTransactionOwners = new WeakMap();
|
|
65
68
|
// Read options for a rotated generation's native transactions; shared because they never vary.
|
|
66
69
|
const SNAPSHOT_FREE = Object.freeze({ disableSnapshot: true });
|
|
67
70
|
// Logical transactions the monitor supervises for their WRITES, kept apart from trackedTxns because the
|
|
@@ -69,7 +72,7 @@ const SNAPSHOT_FREE = Object.freeze({ disableSnapshot: true });
|
|
|
69
72
|
// is what the read-queue-depth metric counts, while this holds one entry per logical transaction — the
|
|
70
73
|
// chain root — so a chain child can never become its own timeout root (issue #2231).
|
|
71
74
|
const supervisedWriteRoots = new Set();
|
|
72
|
-
|
|
75
|
+
let MAX_OUTSTANDING_TXN_DURATION = (0, common_utils_ts_1.convertToMS)(envMngr.get(hdbTerms_ts_1.CONFIG_PARAMS.STORAGE_MAXTRANSACTIONQUEUETIME)) || 45000; // Allow write transactions to be queued for up to 45 seconds before we start rejecting them
|
|
73
76
|
const DEBUG_LONG_TXNS = envMngr.get(hdbTerms_ts_1.CONFIG_PARAMS.STORAGE_DEBUGLONGTRANSACTIONS);
|
|
74
77
|
exports.TRANSACTION_STATE = {
|
|
75
78
|
CLOSED: 0, // the transaction has been committed or aborted and can no longer be used for writes (if read txn is active, it can be used for reads)
|
|
@@ -81,6 +84,12 @@ exports.TRANSACTION_STATE = {
|
|
|
81
84
|
LINGERING: 2,
|
|
82
85
|
};
|
|
83
86
|
const MAX_RETRIES = 40;
|
|
87
|
+
// Over-limit monitor ticks a transaction parked in its commit phase is spared before it is aborted
|
|
88
|
+
// like any other over-time transaction. Sparing re-arms `timeout`, so each of these costs two
|
|
89
|
+
// monitor intervals — roughly 10 minutes at the 30s default. Generous, so a legitimately large blob
|
|
90
|
+
// write finishes, but bounded, so a pre-commit source that stalls instead of finishing can't pin its
|
|
91
|
+
// read snapshot forever (issue #2062).
|
|
92
|
+
exports.COMMIT_PHASE_GRACE = 10;
|
|
84
93
|
// Cap the per-retry backoff so replication-applied transactions, which retry conflicts without a
|
|
85
94
|
// cap (see the commit rejection handler), don't grow the delay unbounded.
|
|
86
95
|
const MAX_RETRY_DELAY_MS = 1000;
|
|
@@ -91,7 +100,27 @@ let outstandingCommitCount = 0;
|
|
|
91
100
|
// the whole thread, regardless of how many distinct commits individually cross the threshold — see
|
|
92
101
|
// the comment at the log site for why a per-commit-only dedup isn't enough under sustained overload.
|
|
93
102
|
const OVERLOAD_LOG_MIN_INTERVAL_MS = 1000;
|
|
94
|
-
|
|
103
|
+
// One cooldown per reporting site, not one shared: `shed` fires on every bystander write during a
|
|
104
|
+
// wedge and would starve `abandon`, which names a different transaction and is the only server-side
|
|
105
|
+
// record of why a request was failed. Mutates only when it grants, so a caller with its own
|
|
106
|
+
// suppression as well (checkOverloaded's per-node `logged`) must test that first.
|
|
107
|
+
const lastStuckCommitLogAt = { shed: -Infinity, abandon: -Infinity };
|
|
108
|
+
function allowStuckCommitLog(site, now) {
|
|
109
|
+
if (now - lastStuckCommitLogAt[site] <= OVERLOAD_LOG_MIN_INTERVAL_MS)
|
|
110
|
+
return false;
|
|
111
|
+
lastStuckCommitLogAt[site] = now;
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
// Which database/table, which native transaction, and which request a stuck commit belongs to —
|
|
115
|
+
// without it a wedge gives no indication of what to investigate (harper#2001).
|
|
116
|
+
function describeCommitIdentity(store, startedFrom, nativeTransaction) {
|
|
117
|
+
const nativeTransactionId = nativeTransaction?.id;
|
|
118
|
+
return (`from table: ${store?.rootStore?.databaseName ?? '?'}.${store?.name ?? '?'}` +
|
|
119
|
+
(nativeTransactionId !== undefined ? ` (transaction ${nativeTransactionId})` : '') +
|
|
120
|
+
(startedFrom?.resourceName
|
|
121
|
+
? `, started from ${startedFrom.resourceName}${startedFrom.method ? '.' + startedFrom.method : ''}`
|
|
122
|
+
: ''));
|
|
123
|
+
}
|
|
95
124
|
// Track a submitted commit until it settles. Every attempt is tracked unconditionally: a
|
|
96
125
|
// coordinated retry round and a chained second-store commit are both issued from inside the
|
|
97
126
|
// preceding commit's own resolve handler, which runs before any reaction that could release a
|
|
@@ -169,6 +198,21 @@ function getOutstandingCommits() {
|
|
|
169
198
|
// Once per process: committing under open read iterators forces a write replay, so the warning is
|
|
170
199
|
// about the caller's pattern, not the individual commit.
|
|
171
200
|
let replayedWritesWarned = false;
|
|
201
|
+
/**
|
|
202
|
+
* Abort a detached native handle. RocksTransaction.abort() throws on one that was already
|
|
203
|
+
* committed or aborted, and every caller is a cleanup path whose own callers have no handler — a
|
|
204
|
+
* throw there would abandon the rest of the cleanup.
|
|
205
|
+
*/
|
|
206
|
+
function abortNativeTransaction(transaction, context) {
|
|
207
|
+
if (transaction == null)
|
|
208
|
+
return;
|
|
209
|
+
try {
|
|
210
|
+
transaction.abort();
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
harperLogger.debug?.(context, error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
172
216
|
// The analytics module registers a recorder here at load (dependency inversion, mirroring
|
|
173
217
|
// `replicationConfirmation` below) so the storage layer doesn't statically import the analytics/server
|
|
174
218
|
// modules. Unset until analytics loads, and when analytics is disabled the recorder call is cheap.
|
|
@@ -253,78 +297,8 @@ function transactionOpenTooLongError() {
|
|
|
253
297
|
// 422 signals the request itself must change (split the work), which is the actionable response.
|
|
254
298
|
return new hdbError_ts_1.ServerError('Transaction was aborted after exceeding the maximum open-transaction time; split long-running work into smaller transactions', 422);
|
|
255
299
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
super('Read scan snapshot expired; retry the read without replaying previously committed writes', 503);
|
|
259
|
-
this.name = 'ReadSnapshotExpiredError';
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
function trackReadRange(transaction, createRange) {
|
|
263
|
-
const owner = readTransactionOwners.get(transaction);
|
|
264
|
-
if (!owner)
|
|
265
|
-
return createRange();
|
|
266
|
-
function checkActive() {
|
|
267
|
-
if (owner.timedOut)
|
|
268
|
-
throw transactionOpenTooLongError();
|
|
269
|
-
if (owner.transaction !== transaction) {
|
|
270
|
-
throw new ReadSnapshotExpiredError();
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
checkActive();
|
|
274
|
-
const range = createRange();
|
|
275
|
-
const iterate = range.iterate;
|
|
276
|
-
range.iterate = function (options) {
|
|
277
|
-
const iterator = iterate.call(this, options);
|
|
278
|
-
let done = false;
|
|
279
|
-
// Closing the underlying iterator is the one step here that can throw for a reason the caller
|
|
280
|
-
// must not see: `next()` only reaches it once the snapshot is already gone, which is the
|
|
281
|
-
// likeliest moment for the native layer to object, and an error from cleanup would replace the
|
|
282
|
-
// named 503 with exactly the raw iterator error this wrapper exists to stop surfacing. The
|
|
283
|
-
// closure reference rather than `this` so a destructured `next` still cleans up.
|
|
284
|
-
const wrapper = {
|
|
285
|
-
[Symbol.iterator]() {
|
|
286
|
-
return this;
|
|
287
|
-
},
|
|
288
|
-
next() {
|
|
289
|
-
if (done)
|
|
290
|
-
return { done: true, value: undefined };
|
|
291
|
-
try {
|
|
292
|
-
checkActive();
|
|
293
|
-
owner.rangeReadActive = true;
|
|
294
|
-
const result = iterator.next();
|
|
295
|
-
done = result.done === true;
|
|
296
|
-
return result;
|
|
297
|
-
}
|
|
298
|
-
catch (error) {
|
|
299
|
-
closeQuietly();
|
|
300
|
-
throw error;
|
|
301
|
-
}
|
|
302
|
-
},
|
|
303
|
-
return(value) {
|
|
304
|
-
if (!done) {
|
|
305
|
-
done = true;
|
|
306
|
-
iterator.return?.(value);
|
|
307
|
-
}
|
|
308
|
-
return { done: true, value };
|
|
309
|
-
},
|
|
310
|
-
throw(error) {
|
|
311
|
-
// Not delegated to `iterator.throw`: it closes and rethrows the same error anyway, and
|
|
312
|
-
// delegating after the close below would run it against an iterator already closed.
|
|
313
|
-
closeQuietly();
|
|
314
|
-
throw error;
|
|
315
|
-
},
|
|
316
|
-
};
|
|
317
|
-
function closeQuietly() {
|
|
318
|
-
try {
|
|
319
|
-
wrapper.return();
|
|
320
|
-
}
|
|
321
|
-
catch {
|
|
322
|
-
// the error being propagated is the actionable one
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
return wrapper;
|
|
326
|
-
};
|
|
327
|
-
return range;
|
|
300
|
+
function getAppliedWriteVersion(recordVersion, txnLogKey) {
|
|
301
|
+
return recordVersion == null ? txnLogKey : Math.min(recordVersion, txnLogKey);
|
|
328
302
|
}
|
|
329
303
|
/**
|
|
330
304
|
* The state a preceding write in this transaction left for `operation`'s key, or undefined if this
|
|
@@ -375,7 +349,11 @@ class DatabaseTransaction {
|
|
|
375
349
|
readTxnRefCount;
|
|
376
350
|
readTxnsUsed;
|
|
377
351
|
timeout;
|
|
352
|
+
writeTick = -1;
|
|
378
353
|
timeoutBudget = 0;
|
|
354
|
+
// Initialized rather than `declare`d so the class shape stays monomorphic on the write path. One clock
|
|
355
|
+
// read per handle is what lets the monitor age a handle without calling into the registry every tick.
|
|
356
|
+
handleOpenedAt = 0;
|
|
379
357
|
// save() only stages here; ImmediateTransaction overrides it to commit, which addWrite must not defer
|
|
380
358
|
saveCommits = false;
|
|
381
359
|
// True where save() puts the write into this transaction's native handle, which is what lets a scope
|
|
@@ -394,8 +372,24 @@ class DatabaseTransaction {
|
|
|
394
372
|
// asks to stop reading a pinned snapshot, so re-pinning one for the rest of the scope would take
|
|
395
373
|
// back what it asked for.
|
|
396
374
|
snapshotFree = false;
|
|
397
|
-
|
|
398
|
-
|
|
375
|
+
// Set while commit() is parked in its pre-commit await (`before`/`beforeIntermediate` completions —
|
|
376
|
+
// in practice a blob's durable file write). The write set is sealed and the caller is awaiting the
|
|
377
|
+
// commit, so this is core's own I/O rather than an application holding a transaction open, which is
|
|
378
|
+
// what the open-transaction limit polices: the monitor spares it for COMMIT_PHASE_GRACE ticks
|
|
379
|
+
// instead of poisoning it (issue #2062).
|
|
380
|
+
committing = false;
|
|
381
|
+
commitPhaseTicks = 0;
|
|
382
|
+
setCommitPhase(committing) {
|
|
383
|
+
// A commit phase covers the sealed write set across the whole multi-store chain.
|
|
384
|
+
for (let txn = this; txn; txn = txn.next) {
|
|
385
|
+
txn.committing = committing;
|
|
386
|
+
txn.commitChainHead = committing ? this : undefined;
|
|
387
|
+
if (committing)
|
|
388
|
+
txn.commitPhaseTicks = 0;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
getReadTxn(disableSnapshot) {
|
|
392
|
+
this.readTxnRefCount = (this.readTxnRefCount || 0) + 1;
|
|
399
393
|
// The limit is an IDLE limit. Writes always re-arm it (see addWrite), but reads only do so
|
|
400
394
|
// while no uncommitted writes are held: staged writes hold write intents that other writers'
|
|
401
395
|
// coordinated-retry commits park on, so a handler that wrote once and then only reads — an
|
|
@@ -408,10 +402,6 @@ class DatabaseTransaction {
|
|
|
408
402
|
if ((this.writes.length === 0 && !this.next) || this.open !== exports.TRANSACTION_STATE.OPEN || !this.hasPendingWrites()) {
|
|
409
403
|
this.timeout = Math.max(txnExpiration, this.timeoutBudget);
|
|
410
404
|
}
|
|
411
|
-
}
|
|
412
|
-
getReadTxn(disableSnapshot) {
|
|
413
|
-
this.readTxnRefCount = (this.readTxnRefCount || 0) + 1;
|
|
414
|
-
this.renewReadTimeout();
|
|
415
405
|
if (this.transaction) {
|
|
416
406
|
if (this.transaction.openTimer)
|
|
417
407
|
this.transaction.openTimer = 0;
|
|
@@ -444,10 +434,9 @@ class DatabaseTransaction {
|
|
|
444
434
|
// Monitor state is not ownership state: it stays with `trackedTxns.add` in getReadTxn().
|
|
445
435
|
attachOwnedTransaction(transaction) {
|
|
446
436
|
this.transaction = transaction;
|
|
447
|
-
readTransactionOwners.set(transaction, this);
|
|
448
|
-
this.rangeReadActive = false;
|
|
449
437
|
this.readTxnsUsed = 1;
|
|
450
438
|
this.baseReadRefConsumed = false;
|
|
439
|
+
this.handleOpenedAt = performance.now();
|
|
451
440
|
}
|
|
452
441
|
/**
|
|
453
442
|
* Drop this link's supervision claim, and the root's with it once no link in the chain still holds
|
|
@@ -486,9 +475,9 @@ class DatabaseTransaction {
|
|
|
486
475
|
trackedTxns.delete(this);
|
|
487
476
|
this.endWriteSupervision();
|
|
488
477
|
this.transaction = null;
|
|
489
|
-
this.rangeReadActive = false;
|
|
490
478
|
this.readTxnsUsed = 0;
|
|
491
479
|
this.readTxnRefCount = 0;
|
|
480
|
+
this.handleOpenedAt = 0;
|
|
492
481
|
return transaction;
|
|
493
482
|
}
|
|
494
483
|
useReadTxn(disableSnapshot) {
|
|
@@ -535,12 +524,7 @@ class DatabaseTransaction {
|
|
|
535
524
|
*/
|
|
536
525
|
releaseReadTxn() {
|
|
537
526
|
const transaction = this.detachOwnedTransaction();
|
|
538
|
-
|
|
539
|
-
transaction?.abort();
|
|
540
|
-
}
|
|
541
|
-
catch (error) {
|
|
542
|
-
harperLogger.debug?.('releasing timed-out read transaction', error);
|
|
543
|
-
}
|
|
527
|
+
abortNativeTransaction(transaction, 'releasing timed-out read transaction');
|
|
544
528
|
this.completeDeferredContextRelease();
|
|
545
529
|
}
|
|
546
530
|
/**
|
|
@@ -620,6 +604,77 @@ class DatabaseTransaction {
|
|
|
620
604
|
}
|
|
621
605
|
}
|
|
622
606
|
}
|
|
607
|
+
registerRecordLock(handle) {
|
|
608
|
+
if (!this.recordLocks)
|
|
609
|
+
this.recordLocks = new Map();
|
|
610
|
+
let storeMap = this.recordLocks.get(handle.store);
|
|
611
|
+
if (!storeMap)
|
|
612
|
+
this.recordLocks.set(handle.store, (storeMap = new Map()));
|
|
613
|
+
storeMap.set(handle.keyId, handle);
|
|
614
|
+
}
|
|
615
|
+
recordLockFor(store, keyId) {
|
|
616
|
+
const storeMap = this.recordLocks?.get(store);
|
|
617
|
+
if (!storeMap)
|
|
618
|
+
return undefined;
|
|
619
|
+
const h = storeMap.get(keyId);
|
|
620
|
+
if (!h)
|
|
621
|
+
return undefined;
|
|
622
|
+
if (!h.released)
|
|
623
|
+
return h;
|
|
624
|
+
// Prune released handles so they don't accumulate; an expired handle checking re-entrancy
|
|
625
|
+
// would otherwise be seen as the holder and incorrectly granted access.
|
|
626
|
+
storeMap.delete(keyId);
|
|
627
|
+
return undefined;
|
|
628
|
+
}
|
|
629
|
+
unregisterRecordLock(handle) {
|
|
630
|
+
const storeMap = this.recordLocks?.get(handle.store);
|
|
631
|
+
if (storeMap?.get(handle.keyId) === handle)
|
|
632
|
+
storeMap.delete(handle.keyId);
|
|
633
|
+
}
|
|
634
|
+
registerPendingLock(store, keyId, pending) {
|
|
635
|
+
if (!this.pendingLocks)
|
|
636
|
+
this.pendingLocks = new Map();
|
|
637
|
+
let storeMap = this.pendingLocks.get(store);
|
|
638
|
+
if (!storeMap)
|
|
639
|
+
this.pendingLocks.set(store, (storeMap = new Map()));
|
|
640
|
+
storeMap.set(keyId, pending);
|
|
641
|
+
}
|
|
642
|
+
pendingLockFor(store, keyId) {
|
|
643
|
+
return this.pendingLocks?.get(store)?.get(keyId);
|
|
644
|
+
}
|
|
645
|
+
unregisterPendingLock(store, keyId) {
|
|
646
|
+
this.pendingLocks?.get(store)?.delete(keyId);
|
|
647
|
+
}
|
|
648
|
+
/** Release every transaction-scoped lock() handle this link owns. */
|
|
649
|
+
releaseRecordLocks() {
|
|
650
|
+
const recordLocks = this.recordLocks;
|
|
651
|
+
if (!recordLocks)
|
|
652
|
+
return;
|
|
653
|
+
for (const [store, storeMap] of recordLocks) {
|
|
654
|
+
for (const [keyId, handle] of storeMap) {
|
|
655
|
+
if (handle.hold)
|
|
656
|
+
continue; // hold handles outlive the transaction; released by unlock()
|
|
657
|
+
handle.release();
|
|
658
|
+
storeMap.delete(keyId);
|
|
659
|
+
}
|
|
660
|
+
if (storeMap.size === 0)
|
|
661
|
+
recordLocks.delete(store);
|
|
662
|
+
}
|
|
663
|
+
if (recordLocks.size === 0)
|
|
664
|
+
this.recordLocks = undefined;
|
|
665
|
+
}
|
|
666
|
+
noteCommittedLockVersions() {
|
|
667
|
+
const recordLocks = this.recordLocks;
|
|
668
|
+
if (!recordLocks)
|
|
669
|
+
return;
|
|
670
|
+
for (const write of this.writes) {
|
|
671
|
+
if (write?.appliedRecordVersion == null)
|
|
672
|
+
continue;
|
|
673
|
+
const handle = write.lockHandle ?? recordLocks.get(write.store)?.get(writeKeyId(write.key));
|
|
674
|
+
if (handle && !handle.released)
|
|
675
|
+
handle.noteHolderVersion(write.appliedRecordVersion);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
623
678
|
/**
|
|
624
679
|
* Discard the staged write set (committed or aborted); the per-key chain must go with it so a
|
|
625
680
|
* reused transaction never bases a write on a previous batch's staged state.
|
|
@@ -679,25 +734,20 @@ class DatabaseTransaction {
|
|
|
679
734
|
// original per-request log was fixed to avoid, just shifted from per-request to per-commit.
|
|
680
735
|
// A commit skipped by the cooldown is NOT marked `logged`, so it still gets a log later if
|
|
681
736
|
// it's still the oldest once the cooldown clears, rather than going silent forever.
|
|
682
|
-
if (!oldestOutstandingCommit.logged && now
|
|
737
|
+
if (!oldestOutstandingCommit.logged && allowStuckCommitLog('shed', now)) {
|
|
683
738
|
// Log once per stuck commit (not once per rejected request, harper#2001): a wedged
|
|
684
739
|
// thread otherwise logs nothing at all server-side while rejecting every write with a
|
|
685
740
|
// 503, which was the single biggest obstacle to root-causing a recurrence. The flag lives
|
|
686
741
|
// on the node itself, so if THIS commit settles while still over the limit and a
|
|
687
742
|
// different one is now oldest, that one logs too instead of staying silent forever.
|
|
688
743
|
oldestOutstandingCommit.logged = true;
|
|
689
|
-
lastOverloadLogAt = now;
|
|
690
|
-
const nativeTransactionId = oldestOutstandingCommit.nativeTransaction?.id;
|
|
691
|
-
const store = oldestOutstandingCommit.store;
|
|
692
|
-
const startedFrom = oldestOutstandingCommit.startedFrom;
|
|
693
744
|
harperLogger.error(`Rejecting writes on this thread: a commit has been outstanding for ` +
|
|
694
745
|
`${Math.round(now - oldestOutstandingCommit.start)}ms (exceeds the ` +
|
|
695
|
-
`${MAX_OUTSTANDING_TXN_DURATION}ms limit),
|
|
696
|
-
(
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
`. Further record updates and publishes from new application requests on this thread ` +
|
|
746
|
+
`${MAX_OUTSTANDING_TXN_DURATION}ms limit), ` +
|
|
747
|
+
describeCommitIdentity(oldestOutstandingCommit.store, oldestOutstandingCommit.startedFrom, oldestOutstandingCommit.nativeTransaction) +
|
|
748
|
+
`.` +
|
|
749
|
+
(0, longLivedTransactions_ts_1.describeHolderCandidates)(oldestOutstandingCommit.store?.rootStore?.path, oldestOutstandingCommit.nativeTransaction?.id) +
|
|
750
|
+
` Further record updates and publishes from new application requests on this thread ` +
|
|
701
751
|
`will be rejected with 503 until the commit settles or the process is restarted (deletes, ` +
|
|
702
752
|
`and writes applied from a canonical source, e.g. replication or a caching source, bypass this check).`);
|
|
703
753
|
}
|
|
@@ -758,6 +808,7 @@ class DatabaseTransaction {
|
|
|
758
808
|
// Independent write-recency signal for chainStillActive (see the field comment) — reads never
|
|
759
809
|
// touch this, only writes do.
|
|
760
810
|
this.writeTimeout = this.timeout;
|
|
811
|
+
this.writeTick = monitorTick;
|
|
761
812
|
this.linkWrite(operation);
|
|
762
813
|
this.writes.push(operation);
|
|
763
814
|
operation.stagedIn = this;
|
|
@@ -787,7 +838,34 @@ class DatabaseTransaction {
|
|
|
787
838
|
return operation;
|
|
788
839
|
}
|
|
789
840
|
save(operation, transaction, reloadEntry = false, options) {
|
|
790
|
-
|
|
841
|
+
const lockHandle = operation.lockHandle;
|
|
842
|
+
// Guard: a write staged through an expired or released lock handle must not land.
|
|
843
|
+
// The handle's lease timer already unlocked the native key; another holder may have taken it.
|
|
844
|
+
if (lockHandle && (lockHandle.expired || lockHandle.released)) {
|
|
845
|
+
// Remove the operation from the staged set so subsequent writes on this context do not
|
|
846
|
+
// re-throw 409 due to a stale null-saved entry sitting in this.writes.
|
|
847
|
+
const failedIdx = this.writes.indexOf(operation);
|
|
848
|
+
if (failedIdx > -1)
|
|
849
|
+
this.writes[failedIdx] = null;
|
|
850
|
+
throw (0, recordLock_ts_1.lockNotHeldError)(lockHandle);
|
|
851
|
+
}
|
|
852
|
+
// Lock-write timestamp rules.
|
|
853
|
+
if (lockHandle) {
|
|
854
|
+
if (this.open === exports.TRANSACTION_STATE.CLOSED || this.saveCommits) {
|
|
855
|
+
// CLOSED path (second+ write per ImmediateTransaction cycle) OR the first write in
|
|
856
|
+
// an ImmediateTransaction (open=OPEN until commit sets it CLOSED, but saveCommits
|
|
857
|
+
// signals the per-write-commit semantics): stamp with nextHolderVersion() so that
|
|
858
|
+
// each sequential save() gets its own monotonically increasing stamp — scoped or hold.
|
|
859
|
+
// The stamp stays on the operation and is consumed below rather than assigned to
|
|
860
|
+
// this.timestamp: pinning the link's clock would stamp every OTHER write staged on
|
|
861
|
+
// the same context before the commit resets it — a concurrent write in the caller's
|
|
862
|
+
// own Promise.all, or the next operation in a retry/replay save loop — with the
|
|
863
|
+
// lock's version, which LWW then silently drops against a newer record version.
|
|
864
|
+
if (!operation.lockStamp)
|
|
865
|
+
operation.lockStamp = lockHandle.holderVersionCandidate();
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
let txnTime = operation.lockStamp ?? this.timestamp;
|
|
791
869
|
// Only an OPEN transaction accepts new staged writes. After commit, this.transaction may still
|
|
792
870
|
// be retained for outstanding read iterators; staging into it would silently discard the write
|
|
793
871
|
// when doneReadTxn() aborts the handle, so such writes commit immediately on a fresh
|
|
@@ -832,13 +910,18 @@ class DatabaseTransaction {
|
|
|
832
910
|
}
|
|
833
911
|
if (!txnTime)
|
|
834
912
|
txnTime = this.timestamp = transaction.getTimestamp();
|
|
913
|
+
// `txnTime` is this transaction's timestamp — the key its entries take in the per-origin log.
|
|
914
|
+
// A write applied from elsewhere carries the origin's record version too, and that is what the
|
|
915
|
+
// record is stored at; the two coincide for every locally-originated write. Gated on the apply
|
|
916
|
+
// flags so an ordinary write never reads the property (harper#2412).
|
|
917
|
+
const writeVersion = this.sourceApply || this.isReplay ? getAppliedWriteVersion(operation.recordVersion, txnTime) : txnTime;
|
|
835
918
|
if (reloadEntry || operation.entry === undefined) {
|
|
836
919
|
operation.entry = operation.store.getEntry(operation.key, { transaction });
|
|
837
920
|
}
|
|
838
921
|
if (!operation.saved) {
|
|
839
922
|
operation.saved = true;
|
|
840
923
|
// immediately execute in this transaction
|
|
841
|
-
if (operation.validate?.(
|
|
924
|
+
if (operation.validate?.(writeVersion, this) === false) {
|
|
842
925
|
operation.commit = () => { }; // noop if we try again
|
|
843
926
|
return;
|
|
844
927
|
}
|
|
@@ -849,7 +932,13 @@ class DatabaseTransaction {
|
|
|
849
932
|
if (result?.then)
|
|
850
933
|
this.stageCompletion(result);
|
|
851
934
|
}
|
|
852
|
-
|
|
935
|
+
if (lockHandle || this.recordLocks)
|
|
936
|
+
operation.trackRecordVersion = true;
|
|
937
|
+
if (operation.trackRecordVersion)
|
|
938
|
+
operation.recordVersionApplied = false;
|
|
939
|
+
const completion = operation.commit(writeVersion, operation.entry, this.retries > 0, transaction);
|
|
940
|
+
if (operation.trackRecordVersion)
|
|
941
|
+
operation.appliedRecordVersion = operation.recordVersionApplied ? writeVersion : undefined;
|
|
853
942
|
if (typeof completion?.then === 'function')
|
|
854
943
|
this.stageCompletion(completion);
|
|
855
944
|
// Sticky record that THIS write staged with its audit entry appended (log entries batch on the
|
|
@@ -862,7 +951,13 @@ class DatabaseTransaction {
|
|
|
862
951
|
operation.appendedAuditEntry = true;
|
|
863
952
|
}
|
|
864
953
|
if (immediateCommit) {
|
|
865
|
-
|
|
954
|
+
// immediately commit if the harper transaction is closed
|
|
955
|
+
const innerCommit = this.commit({ ...options, transaction });
|
|
956
|
+
// Expose on the operation so the lock-writable Table.save() path can await the real
|
|
957
|
+
// native commit — without this the ImmediateTransaction outer commit resolves before
|
|
958
|
+
// the native transaction.commit() settles (fire-and-forget from the if-branch).
|
|
959
|
+
operation.innerCommit = innerCommit;
|
|
960
|
+
return innerCommit;
|
|
866
961
|
}
|
|
867
962
|
}
|
|
868
963
|
/**
|
|
@@ -874,17 +969,38 @@ class DatabaseTransaction {
|
|
|
874
969
|
// reused across retries — the native layer resets it in place (fresh snapshot) on IsBusy/TryAgain —
|
|
875
970
|
// but reassigned to a fresh replay transaction when outstanding read iterators retain this.transaction
|
|
876
971
|
let transaction = options.transaction ?? this.transaction;
|
|
877
|
-
|
|
878
|
-
let
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
972
|
+
try {
|
|
973
|
+
for (let i = 0; i < this.writes.length; i++) {
|
|
974
|
+
let operation = this.writes[i];
|
|
975
|
+
if (!operation || (this.retries === 0 && operation.saved))
|
|
976
|
+
continue;
|
|
977
|
+
this.save(operation, transaction, i < this.validated, options);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
catch (error) {
|
|
981
|
+
this.abort();
|
|
982
|
+
throw error;
|
|
882
983
|
}
|
|
883
984
|
this.validated = this.writes.length;
|
|
884
985
|
const completions = this.completions;
|
|
885
986
|
if (completions.length > 0)
|
|
886
987
|
this.completions = []; // reset
|
|
988
|
+
const stagedWrites = this.writes.length;
|
|
989
|
+
if (completions.length > 0) {
|
|
990
|
+
this.setCommitPhase(true);
|
|
991
|
+
}
|
|
887
992
|
return (0, when_ts_1.when)(completions.length > 0 ? Promise.all(completions) : null, () => {
|
|
993
|
+
if (completions.length > 0)
|
|
994
|
+
this.setCommitPhase(false);
|
|
995
|
+
// The transaction can be aborted underneath us while we are parked in the await above — by the
|
|
996
|
+
// monitor once the commit phase outlives its grace, or through the multi-store poison chain.
|
|
997
|
+
// abort() cleared the write set and released the handle, so resuming would commit nothing and
|
|
998
|
+
// resolve as SUCCESS: the caller is told its write landed when it was dropped, and a write
|
|
999
|
+
// carrying a blob is left holding an instance whose file was unlinked (issue #2062).
|
|
1000
|
+
if (this.timedOut)
|
|
1001
|
+
throw transactionOpenTooLongError();
|
|
1002
|
+
if (stagedWrites > 0 && this.writes.length === 0 && this.open === exports.TRANSACTION_STATE.CLOSED)
|
|
1003
|
+
throw new hdbError_ts_1.ServerError('Transaction was aborted while its commit was waiting on pre-commit work', 500);
|
|
888
1004
|
if (this.writes.length > this.validated) {
|
|
889
1005
|
// check just in case we got any more transactions while we were waiting, if so just recursively continue to finish the additional writes now
|
|
890
1006
|
return this.commit(options);
|
|
@@ -980,8 +1096,6 @@ class DatabaseTransaction {
|
|
|
980
1096
|
if (transaction) {
|
|
981
1097
|
this.writes = this.writes.filter((write) => write); // filter out removed entries
|
|
982
1098
|
if (this.writes.length > 0) {
|
|
983
|
-
// Commit retries can construct fresh ranges on this live handle after read ownership ends.
|
|
984
|
-
readTransactionOwners.delete(transaction);
|
|
985
1099
|
// The transaction was created with coordinatedRetry:true (see
|
|
986
1100
|
// getReadTxn), so commit() can resolve to RETRY_NOW_VALUE. That
|
|
987
1101
|
// sentinel (a number) is why commitResolution is typed
|
|
@@ -1020,14 +1134,22 @@ class DatabaseTransaction {
|
|
|
1020
1134
|
// claimed this per-database transaction in txnForContext and so can name the wrong
|
|
1021
1135
|
// table when a transaction spans more than one table in the same database.
|
|
1022
1136
|
trackOutstandingCommit(commitResolution, this.writes[0]?.store, this.startedFrom, transaction);
|
|
1137
|
+
// Every retry round and every chained store re-enters here and must inherit the chain
|
|
1138
|
+
// root's clock rather than restart it, so only the first submission stamps.
|
|
1139
|
+
const chainRoot = this.root ?? this;
|
|
1140
|
+
if (chainRoot.commitStartedAt == null)
|
|
1141
|
+
chainRoot.commitStartedAt = performance.now();
|
|
1023
1142
|
const completions = [];
|
|
1024
|
-
|
|
1143
|
+
const commitOutcome = commitResolution.then((commitResult) => {
|
|
1025
1144
|
if (commitResult === RETRY_NOW_VALUE) {
|
|
1026
1145
|
this.retries++;
|
|
1027
1146
|
harperLogger.debug?.('coordinated retry', transaction.id, this.retries);
|
|
1028
1147
|
// Mark this specific native transaction as a retry so RocksTransactionLogStore
|
|
1029
1148
|
// skips re-writing its already-staged txn-log entries (#2).
|
|
1030
1149
|
transaction.isRetry = true;
|
|
1150
|
+
const pastBudget = this.elapsedPastCommitBudget();
|
|
1151
|
+
if (pastBudget)
|
|
1152
|
+
this.abandonCommitAfterDeadline(transaction, pastBudget);
|
|
1031
1153
|
// Mirror the ERR_BUSY cap/warn policy: non-sourceApply transactions abort
|
|
1032
1154
|
// at MAX_RETRIES; sourceApply transactions keep retrying with periodic warn.
|
|
1033
1155
|
if (this.retries > MAX_RETRIES) {
|
|
@@ -1080,9 +1202,12 @@ class DatabaseTransaction {
|
|
|
1080
1202
|
if (write?.savedBlobs && (write.skipped || (write.superseded && !write.blobsAuditReferenced)))
|
|
1081
1203
|
(0, blob_ts_1.cleanupUnusedBlobs)(write.savedBlobs, (0, blob_ts_1.collectRetainedFileIds)(write.store.getEntry(write.key)?.value));
|
|
1082
1204
|
}
|
|
1205
|
+
if (this.recordLocks)
|
|
1206
|
+
this.noteCommittedLockVersions();
|
|
1083
1207
|
// now reset transactions tracking; this transaction be reused and committed again
|
|
1084
1208
|
this.retries = 0; // reset per-native-transaction retry counter so a reused DatabaseTransaction's next batch starts fresh
|
|
1085
1209
|
this.clearWrites();
|
|
1210
|
+
this.releaseRecordLocks();
|
|
1086
1211
|
if (options.doneWriting)
|
|
1087
1212
|
this.endScopeOwnership();
|
|
1088
1213
|
this.releaseContext(!!options.doneWriting);
|
|
@@ -1134,6 +1259,11 @@ class DatabaseTransaction {
|
|
|
1134
1259
|
// premature publish, and no fresh-transaction replay that would drop the entry.
|
|
1135
1260
|
// Mark the native transaction as a retry so RocksTransactionLogStore skips re-staging entries.
|
|
1136
1261
|
transaction.isRetry = true;
|
|
1262
|
+
// Before the backoff gate below: the budget can already be spent on the first
|
|
1263
|
+
// retry when an earlier store in this chain consumed it.
|
|
1264
|
+
const pastBudget = this.elapsedPastCommitBudget();
|
|
1265
|
+
if (pastBudget)
|
|
1266
|
+
this.abandonCommitAfterDeadline(transaction, pastBudget);
|
|
1137
1267
|
if (this.retries > 2) {
|
|
1138
1268
|
// Transactions applying data from a canonical source of truth (replication peer or
|
|
1139
1269
|
// external caching source) must never drop a write on a transient conflict: there is no
|
|
@@ -1178,24 +1308,43 @@ class DatabaseTransaction {
|
|
|
1178
1308
|
// finished and its durability is unknown, so ownership goes with it.
|
|
1179
1309
|
this.endScopeOwnership();
|
|
1180
1310
|
this.releaseContext(!!options.doneWriting);
|
|
1311
|
+
this.releaseRecordLocks();
|
|
1312
|
+
this.timestamp = 0;
|
|
1181
1313
|
throw error;
|
|
1182
1314
|
}
|
|
1183
1315
|
});
|
|
1316
|
+
// `commitOutcome` settles when the LOGICAL commit ends — its success branch awaits the
|
|
1317
|
+
// chained stores' own commits — so releasing here covers every terminal exit (success,
|
|
1318
|
+
// retry exhaustion, abandonment, terminal failure) in one place rather than five.
|
|
1319
|
+
// Released through the RETURNED promise rather than a second subscriber on
|
|
1320
|
+
// `commitOutcome`: a subscriber would mark a dropped commit rejection as handled and
|
|
1321
|
+
// silence the unhandled-rejection that surfaces it.
|
|
1322
|
+
return commitOutcome.then((resolution) => {
|
|
1323
|
+
chainRoot.commitStartedAt = undefined;
|
|
1324
|
+
return resolution;
|
|
1325
|
+
}, (error) => {
|
|
1326
|
+
chainRoot.commitStartedAt = undefined;
|
|
1327
|
+
throw error;
|
|
1328
|
+
});
|
|
1184
1329
|
}
|
|
1185
1330
|
for (const write of this.writes) {
|
|
1186
1331
|
if (write?.savedBlobs && (write.skipped || (write.superseded && !write.blobsAuditReferenced)))
|
|
1187
1332
|
(0, blob_ts_1.cleanupUnusedBlobs)(write.savedBlobs, (0, blob_ts_1.collectRetainedFileIds)(write.store.getEntry(write.key)?.value));
|
|
1188
1333
|
}
|
|
1334
|
+
if (this.recordLocks)
|
|
1335
|
+
this.noteCommittedLockVersions();
|
|
1189
1336
|
this.clearWrites();
|
|
1337
|
+
this.releaseRecordLocks();
|
|
1190
1338
|
if (options.doneWriting)
|
|
1191
1339
|
this.endScopeOwnership();
|
|
1192
1340
|
this.releaseContext(!!options.doneWriting);
|
|
1193
1341
|
const txnResolution = {
|
|
1194
1342
|
txnTime: this.timestamp,
|
|
1195
1343
|
};
|
|
1344
|
+
this.timestamp = 0; // reset like the async path (~1279) so stale lock stamps don't persist
|
|
1196
1345
|
if (this.next) {
|
|
1197
1346
|
// now run any other transactions
|
|
1198
|
-
options.timestamp =
|
|
1347
|
+
options.timestamp = txnResolution.txnTime;
|
|
1199
1348
|
// as above: the next store must not inherit this store's explicit native transaction
|
|
1200
1349
|
let nextResolution;
|
|
1201
1350
|
try {
|
|
@@ -1214,7 +1363,7 @@ class DatabaseTransaction {
|
|
|
1214
1363
|
// scope resumable after a partially failed mid-scope commit.
|
|
1215
1364
|
this.completeMidScopeCommit(options);
|
|
1216
1365
|
return {
|
|
1217
|
-
txnTime:
|
|
1366
|
+
txnTime: txnResolution.txnTime,
|
|
1218
1367
|
next: nextResolution,
|
|
1219
1368
|
};
|
|
1220
1369
|
}, (error) => {
|
|
@@ -1230,6 +1379,7 @@ class DatabaseTransaction {
|
|
|
1230
1379
|
this.completeMidScopeCommit(options);
|
|
1231
1380
|
return txnResolution;
|
|
1232
1381
|
}, (error) => {
|
|
1382
|
+
this.setCommitPhase(false);
|
|
1233
1383
|
this.abort();
|
|
1234
1384
|
throw error;
|
|
1235
1385
|
});
|
|
@@ -1277,6 +1427,7 @@ class DatabaseTransaction {
|
|
|
1277
1427
|
if (this.transaction)
|
|
1278
1428
|
this.releaseReadTxn();
|
|
1279
1429
|
this.open = exports.TRANSACTION_STATE.CLOSED;
|
|
1430
|
+
this.timestamp = 0; // a lock stamp pinned for this write set must not leak into the next
|
|
1280
1431
|
this.drainCompletions();
|
|
1281
1432
|
try {
|
|
1282
1433
|
for (const write of this.writes) {
|
|
@@ -1287,6 +1438,7 @@ class DatabaseTransaction {
|
|
|
1287
1438
|
finally {
|
|
1288
1439
|
this.endScopeOwnership(); // the scope is over; nothing may rotate this instance again
|
|
1289
1440
|
this.clearWrites();
|
|
1441
|
+
this.releaseRecordLocks();
|
|
1290
1442
|
// A timeout-poisoned abort (abortDueToTimeout()) is the one abort that is NOT "reuse-free":
|
|
1291
1443
|
// Resource.ts's dispatcher deliberately keeps joining a `timedOut` transaction (instead of
|
|
1292
1444
|
// starting a fresh one) so the rest of the logical operation fails atomically via the
|
|
@@ -1306,6 +1458,56 @@ class DatabaseTransaction {
|
|
|
1306
1458
|
}
|
|
1307
1459
|
}
|
|
1308
1460
|
}
|
|
1461
|
+
/** How long this logical commit may keep retrying: the thread-wide queue limit, or its own larger explicit budget. */
|
|
1462
|
+
commitConflictBudget() {
|
|
1463
|
+
return Math.max(MAX_OUTSTANDING_TXN_DURATION, (this.root ?? this).timeoutBudget || 0);
|
|
1464
|
+
}
|
|
1465
|
+
/**
|
|
1466
|
+
* How long this logical commit has been retrying once it is past its budget, else 0. rocksdb-js
|
|
1467
|
+
* returns control from a parked commit every `ROCKSDB_JS_PARK_TIMEOUT_MS` even when the intent
|
|
1468
|
+
* holder never releases, so without this bound a request-path commit retries the attempt cap out
|
|
1469
|
+
* — minutes past the queue limit an operator configured (issue #2450).
|
|
1470
|
+
*
|
|
1471
|
+
* Source-applied writes are exempt for the same reason they are exempt from the attempt cap:
|
|
1472
|
+
* there is no resubscribe/sequence-resume path, so dropping one permanently diverges this node.
|
|
1473
|
+
*/
|
|
1474
|
+
elapsedPastCommitBudget() {
|
|
1475
|
+
if (this.sourceApply)
|
|
1476
|
+
return 0;
|
|
1477
|
+
const startedAt = (this.root ?? this).commitStartedAt;
|
|
1478
|
+
if (startedAt == null)
|
|
1479
|
+
return 0;
|
|
1480
|
+
const elapsed = performance.now() - startedAt;
|
|
1481
|
+
return elapsed > this.commitConflictBudget() ? elapsed : 0;
|
|
1482
|
+
}
|
|
1483
|
+
/**
|
|
1484
|
+
* Abandon a logical commit that stayed in write-intent conflict past its budget. Cleanup is
|
|
1485
|
+
* retry exhaustion's, so no link leaks a native handle or read snapshot; the error is distinct
|
|
1486
|
+
* because the condition is distinct — every attempt reported transient contention, so a later
|
|
1487
|
+
* request can succeed once the holder releases, which the generic exhaustion 500 does not say.
|
|
1488
|
+
*
|
|
1489
|
+
* Only a chain root may report `retryable`: a link commits solely from its predecessor's success
|
|
1490
|
+
* handler, so anywhere else in the chain an earlier store has already landed durable audit
|
|
1491
|
+
* entries and hooks that a replayed request would run twice. A head whose scope already rotated
|
|
1492
|
+
* through a mid-scope commit is in the same position.
|
|
1493
|
+
*/
|
|
1494
|
+
abandonCommitAfterDeadline(headTransaction, elapsedMs) {
|
|
1495
|
+
const elapsed = Math.round(elapsedMs);
|
|
1496
|
+
const budget = this.commitConflictBudget();
|
|
1497
|
+
const retryable = !this.root && !this.snapshotFree;
|
|
1498
|
+
if (allowStuckCommitLog('abandon', performance.now())) {
|
|
1499
|
+
harperLogger.error(`Abandoning a write transaction: its commit has been in write-intent conflict for ${elapsed}ms ` +
|
|
1500
|
+
`(exceeds the ${budget}ms limit) across ${this.retries} retries, ` +
|
|
1501
|
+
describeCommitIdentity(this.writes[0]?.store, this.startedFrom, headTransaction) +
|
|
1502
|
+
`.` +
|
|
1503
|
+
(0, longLivedTransactions_ts_1.describeHolderCandidates)(this.writes[0]?.store?.rootStore?.path, headTransaction?.id) +
|
|
1504
|
+
` Another transaction holds a conflicting write intent and has not completed; the request is ` +
|
|
1505
|
+
`failed with a 503${retryable ? '' : ' (not retryable — an earlier store in this transaction already committed)'} ` +
|
|
1506
|
+
`rather than waiting further.`);
|
|
1507
|
+
}
|
|
1508
|
+
this.abortChainAfterRetries(headTransaction);
|
|
1509
|
+
throw new hdbError_ts_1.TransactionCommitConflictTimeoutError(`Commit was in conflict with ongoing writes for ${elapsed}ms, exceeding the ${budget}ms limit; transaction abandoned after ${this.retries} retries`, retryable);
|
|
1510
|
+
}
|
|
1309
1511
|
/**
|
|
1310
1512
|
* Give up on a chain of linked transactions after exhausting conflict retries: poison every link
|
|
1311
1513
|
* first, then abort each link's native transaction and release its DatabaseTransaction-level
|
|
@@ -1401,12 +1603,7 @@ class DatabaseTransaction {
|
|
|
1401
1603
|
// throws. abort() rather than the native abort alone: it reclaims blobs a replayed write
|
|
1402
1604
|
// staged.
|
|
1403
1605
|
this.detachOwnedTransaction();
|
|
1404
|
-
|
|
1405
|
-
transaction?.abort();
|
|
1406
|
-
}
|
|
1407
|
-
catch (abortError) {
|
|
1408
|
-
harperLogger.debug?.('aborting a transaction whose synchronous commit failed', abortError);
|
|
1409
|
-
}
|
|
1606
|
+
abortNativeTransaction(transaction, 'aborting a transaction whose synchronous commit failed');
|
|
1410
1607
|
try {
|
|
1411
1608
|
this.abort();
|
|
1412
1609
|
}
|
|
@@ -1425,6 +1622,18 @@ class DatabaseTransaction {
|
|
|
1425
1622
|
}
|
|
1426
1623
|
}
|
|
1427
1624
|
exports.DatabaseTransaction = DatabaseTransaction;
|
|
1625
|
+
function shouldSpareCommitPhase(txn, checkedCommitPhaseChains) {
|
|
1626
|
+
if (!txn.committing)
|
|
1627
|
+
return false;
|
|
1628
|
+
if (txn.sourceApply || txn.isReplay)
|
|
1629
|
+
return true;
|
|
1630
|
+
const commitChainHead = txn.commitChainHead ?? txn;
|
|
1631
|
+
if (!checkedCommitPhaseChains.has(commitChainHead)) {
|
|
1632
|
+
checkedCommitPhaseChains.add(commitChainHead);
|
|
1633
|
+
commitChainHead.commitPhaseTicks++;
|
|
1634
|
+
}
|
|
1635
|
+
return commitChainHead.commitPhaseTicks <= exports.COMMIT_PHASE_GRACE;
|
|
1636
|
+
}
|
|
1428
1637
|
class ImmediateTransaction extends DatabaseTransaction {
|
|
1429
1638
|
isCommitting = false;
|
|
1430
1639
|
saveCommits = true;
|
|
@@ -1433,18 +1642,50 @@ class ImmediateTransaction extends DatabaseTransaction {
|
|
|
1433
1642
|
this.db = db;
|
|
1434
1643
|
}
|
|
1435
1644
|
save(...args) {
|
|
1436
|
-
const
|
|
1645
|
+
const operation = args[0]; // the staged write, not a transaction — commit() re-enters here with it
|
|
1437
1646
|
if (this.isCommitting) {
|
|
1438
1647
|
// if we are in the commit, do the save and force a reload so we get a read within the transaction
|
|
1439
|
-
super.save(
|
|
1648
|
+
super.save(operation, null, true);
|
|
1440
1649
|
}
|
|
1441
1650
|
else {
|
|
1442
1651
|
this.isCommitting = true;
|
|
1443
|
-
|
|
1652
|
+
// A synchronous throw from commit() (e.g. a 409 from an expired lock handle) would
|
|
1653
|
+
// otherwise leave isCommitting latched at true, causing every subsequent save() in this
|
|
1654
|
+
// context to take the fire-and-forget if-branch and silently drop writes.
|
|
1655
|
+
let commitResult;
|
|
1656
|
+
try {
|
|
1657
|
+
commitResult = this.commit();
|
|
1658
|
+
}
|
|
1659
|
+
catch (err) {
|
|
1444
1660
|
this.isCommitting = false;
|
|
1661
|
+
throw err;
|
|
1662
|
+
}
|
|
1663
|
+
return (0, when_ts_1.when)(commitResult, () => {
|
|
1664
|
+
this.isCommitting = false;
|
|
1665
|
+
}, (err) => {
|
|
1666
|
+
// Async rejection (e.g. a rejected rocksdb commit promise) must also clear the
|
|
1667
|
+
// latch; without this, every subsequent save() silently fire-and-forgets.
|
|
1668
|
+
this.isCommitting = false;
|
|
1669
|
+
throw err;
|
|
1445
1670
|
});
|
|
1446
1671
|
}
|
|
1447
1672
|
}
|
|
1673
|
+
// Without an explicit transaction() a live lock is released only by unlock() or its lease, never
|
|
1674
|
+
// by the per-write commit. Expired handles are pruned so a reused context does not retain every key.
|
|
1675
|
+
releaseRecordLocks() {
|
|
1676
|
+
const recordLocks = this.recordLocks;
|
|
1677
|
+
if (!recordLocks)
|
|
1678
|
+
return;
|
|
1679
|
+
for (const [store, storeMap] of recordLocks) {
|
|
1680
|
+
for (const [keyId, handle] of storeMap)
|
|
1681
|
+
if (handle.released)
|
|
1682
|
+
storeMap.delete(keyId);
|
|
1683
|
+
if (storeMap.size === 0)
|
|
1684
|
+
recordLocks.delete(store);
|
|
1685
|
+
}
|
|
1686
|
+
if (recordLocks.size === 0)
|
|
1687
|
+
this.recordLocks = undefined;
|
|
1688
|
+
}
|
|
1448
1689
|
// @ts-expect-error accessor overriding property
|
|
1449
1690
|
get timestamp() {
|
|
1450
1691
|
return this._timestamp || (this._timestamp = (0, commonUtility_ts_1.getNextMonotonicTime)());
|
|
@@ -1517,6 +1758,7 @@ function isJoinableScope(transaction) {
|
|
|
1517
1758
|
return transaction?.open === exports.TRANSACTION_STATE.OPEN && !transaction.saveCommits;
|
|
1518
1759
|
}
|
|
1519
1760
|
let timer;
|
|
1761
|
+
let monitorTick = 0;
|
|
1520
1762
|
/**
|
|
1521
1763
|
* True when a link other than `txn` in the same multi-store chain was written recently enough to
|
|
1522
1764
|
* still be active — i.e. its `writeTimeout` (set only by addWrite, see the field comment) hasn't
|
|
@@ -1536,22 +1778,86 @@ function chainStillActive(txn) {
|
|
|
1536
1778
|
}
|
|
1537
1779
|
return false;
|
|
1538
1780
|
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Name a chain link still holding its native handle past the reporting threshold, and why the
|
|
1783
|
+
* branches below are not reaping it (harper#2471). Attribution only: it reads the recency clocks
|
|
1784
|
+
* those branches maintain rather than calling `chainStillActive`, which would decay `writeTimeout`
|
|
1785
|
+
* as a side effect, and it runs before them so it cannot reorder them.
|
|
1786
|
+
*/
|
|
1787
|
+
function reportLongLivedLink(link, thresholdMs, now, reportBudget) {
|
|
1788
|
+
if (!link.transaction || link.handleOpenedAt === 0)
|
|
1789
|
+
return;
|
|
1790
|
+
const ageMs = now - link.handleOpenedAt;
|
|
1791
|
+
if (ageMs < thresholdMs)
|
|
1792
|
+
return;
|
|
1793
|
+
const states = [];
|
|
1794
|
+
if (link.sourceApply)
|
|
1795
|
+
states.push('source-apply');
|
|
1796
|
+
if (link.isReplay)
|
|
1797
|
+
states.push('replay');
|
|
1798
|
+
if (link.committing)
|
|
1799
|
+
states.push('commit-phase');
|
|
1800
|
+
if (link.open === exports.TRANSACTION_STATE.CLOSED)
|
|
1801
|
+
states.push('closed-with-open-iterators');
|
|
1802
|
+
if (monitorTick - link.writeTick <= 1)
|
|
1803
|
+
states.push('active');
|
|
1804
|
+
if (states.length === 0)
|
|
1805
|
+
states.push('over-limit');
|
|
1806
|
+
(0, longLivedTransactions_ts_1.reportLongLivedHolder)({
|
|
1807
|
+
databasePath: link.db?.rootStore?.path,
|
|
1808
|
+
nativeId: link.transaction.id,
|
|
1809
|
+
openedAt: link.handleOpenedAt,
|
|
1810
|
+
ageMs,
|
|
1811
|
+
databaseName: link.db?.rootStore?.databaseName,
|
|
1812
|
+
tableName: link.db?.name,
|
|
1813
|
+
countPendingWrites: () => {
|
|
1814
|
+
let pendingWrites = 0;
|
|
1815
|
+
for (const write of link.writes)
|
|
1816
|
+
if (write)
|
|
1817
|
+
pendingWrites++;
|
|
1818
|
+
return pendingWrites;
|
|
1819
|
+
},
|
|
1820
|
+
states,
|
|
1821
|
+
timeoutBudget: link.timeoutBudget,
|
|
1822
|
+
startedFrom: link.startedFrom,
|
|
1823
|
+
}, reportBudget);
|
|
1824
|
+
}
|
|
1825
|
+
/**
|
|
1826
|
+
* Report every link of this logical transaction that holds its own native handle. A blind write to a
|
|
1827
|
+
* second database attaches a handle to a `.next` link while only the root is supervised, so reporting
|
|
1828
|
+
* the entry alone names an id the sweep's line can never be joined to when the child is the holder.
|
|
1829
|
+
* Links that read are in `trackedTxns` and get their own visit from the tick.
|
|
1830
|
+
*/
|
|
1831
|
+
function reportIfLongLived(txn, thresholdMs, now, reportBudget) {
|
|
1832
|
+
if (thresholdMs === 0)
|
|
1833
|
+
return;
|
|
1834
|
+
for (let link = txn; link; link = link.next) {
|
|
1835
|
+
if (link !== txn && trackedTxns.has(link))
|
|
1836
|
+
break;
|
|
1837
|
+
reportLongLivedLink(link, thresholdMs, now, reportBudget);
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1539
1840
|
function startMonitoringTxns() {
|
|
1540
1841
|
timer = setInterval(function () {
|
|
1842
|
+
monitorTick++;
|
|
1843
|
+
const checkedCommitPhaseChains = new Set();
|
|
1844
|
+
const reportThresholdMs = (0, longLivedTransactions_ts_1.getReportThresholdMs)();
|
|
1845
|
+
(0, longLivedTransactions_ts_1.rebaseLongLivedTransactionReports)(reportThresholdMs);
|
|
1846
|
+
const reportBudget = (0, longLivedTransactions_ts_1.createLongLivedHolderReportBudget)(reportThresholdMs);
|
|
1847
|
+
const reportNow = performance.now();
|
|
1541
1848
|
// Both registries, in sequence rather than as a union: a root can be in each (it read, and a
|
|
1542
1849
|
// later link blind-wrote), and the membership check is cheaper than allocating per tick.
|
|
1543
1850
|
for (const txn of trackedTxns)
|
|
1544
|
-
monitorTransaction(txn);
|
|
1851
|
+
monitorTransaction(txn, checkedCommitPhaseChains, reportThresholdMs, reportNow, reportBudget);
|
|
1545
1852
|
for (const txn of supervisedWriteRoots)
|
|
1546
1853
|
if (!trackedTxns.has(txn))
|
|
1547
|
-
monitorTransaction(txn);
|
|
1854
|
+
monitorTransaction(txn, checkedCommitPhaseChains, reportThresholdMs, reportNow, reportBudget);
|
|
1855
|
+
(0, longLivedTransactions_ts_1.finishLongLivedHolderReports)(reportBudget);
|
|
1548
1856
|
}, txnExpiration).unref();
|
|
1549
|
-
function monitorTransaction(txn) {
|
|
1550
|
-
|
|
1551
|
-
txn.rangeReadActive = false;
|
|
1552
|
-
txn.renewReadTimeout();
|
|
1553
|
-
}
|
|
1857
|
+
function monitorTransaction(txn, checkedCommitPhaseChains, reportThresholdMs, reportNow, reportBudget) {
|
|
1858
|
+
reportIfLongLived(txn, reportThresholdMs, reportNow, reportBudget);
|
|
1554
1859
|
{
|
|
1860
|
+
const commitChainHead = txn.commitChainHead ?? txn;
|
|
1555
1861
|
// Decay write recency once per tick for every tracked link, independent of the `timeout`
|
|
1556
1862
|
// branches below — a tracked link that keeps its own idle limit alive by reading must not
|
|
1557
1863
|
// thereby keep chainStillActive believing it was written recently too.
|
|
@@ -1577,6 +1883,21 @@ function startMonitoringTxns() {
|
|
|
1577
1883
|
harperLogger.warn?.(`Read iterators held a committed transaction's snapshot past the open-transaction limit; releasing it, from table: ${txn.db?.name + (url ? ' path: ' + url : '')}`);
|
|
1578
1884
|
txn.releaseReadTxn();
|
|
1579
1885
|
}
|
|
1886
|
+
else if (shouldSpareCommitPhase(txn, checkedCommitPhaseChains)) {
|
|
1887
|
+
// Parked in commit()'s pre-commit await — a `before`/`beforeIntermediate` hook, in practice a
|
|
1888
|
+
// blob's durable file write, which for a multi-tens-of-MB payload legitimately outruns the
|
|
1889
|
+
// limit. The write set is sealed and the caller is awaiting this commit, so the limit's
|
|
1890
|
+
// premise (the application is holding a transaction open) does not hold, and poisoning here
|
|
1891
|
+
// would unlink the blobs the write still references (issue #2062). The grace is bounded
|
|
1892
|
+
// because the transaction still pins a read snapshot: a source that stalls rather than
|
|
1893
|
+
// finishing falls through to the abort below once it runs out. A canonical-source apply or
|
|
1894
|
+
// replay is spared for as long as it takes: neither aborting it (harper-pro#348) nor the
|
|
1895
|
+
// force-commit below — which would durably commit a record whose blob file is still being
|
|
1896
|
+
// written — is acceptable, and their blob sources are bounded by the receive-side idle
|
|
1897
|
+
// watchdog instead.
|
|
1898
|
+
harperLogger.warn?.(`Transaction has been in its commit phase past the open-transaction limit, waiting on pre-commit work (e.g. a large blob write); letting it complete, from table: ${txn.db?.name + (url ? ' path: ' + url : '')}`, ...(txn.startedFrom ? [`was started from ${txn.startedFrom.resourceName}.${txn.startedFrom.method}`] : []));
|
|
1899
|
+
txn.timeout = Math.max(txnExpiration, txn.timeoutBudget ?? 0);
|
|
1900
|
+
}
|
|
1580
1901
|
else if (txn.hasPendingWrites() && chainStillActive(txn)) {
|
|
1581
1902
|
// A later link in the chain was written recently (writes re-arm only the link that
|
|
1582
1903
|
// receives them, and a multi-store transaction can be writing database B while this
|
|
@@ -1595,7 +1916,7 @@ function startMonitoringTxns() {
|
|
|
1595
1916
|
// those, keep the prior force-commit behavior below.
|
|
1596
1917
|
harperLogger.error(`Transaction was open too long and has been aborted after exceeding the open-transaction limit, from table: ${txn.db?.name + (url ? ' path: ' + url : '')}`, ...(txn.startedFrom ? [`was started from ${txn.startedFrom.resourceName}.${txn.startedFrom.method}`] : []), ...(DEBUG_LONG_TXNS ? ['starting stack trace', txn.stackTraces] : []));
|
|
1597
1918
|
try {
|
|
1598
|
-
|
|
1919
|
+
commitChainHead.abortDueToTimeout();
|
|
1599
1920
|
}
|
|
1600
1921
|
catch (error) {
|
|
1601
1922
|
harperLogger.debug?.(`Error aborting timed out transaction: ${error.message}`);
|
|
@@ -1634,6 +1955,11 @@ startMonitoringTxns();
|
|
|
1634
1955
|
function resetReplayedWritesWarning() {
|
|
1635
1956
|
replayedWritesWarned = false;
|
|
1636
1957
|
}
|
|
1958
|
+
function setMaxOutstandingTxnDuration(ms) {
|
|
1959
|
+
const previous = MAX_OUTSTANDING_TXN_DURATION;
|
|
1960
|
+
MAX_OUTSTANDING_TXN_DURATION = ms;
|
|
1961
|
+
return previous;
|
|
1962
|
+
}
|
|
1637
1963
|
/** Test seam: whether the monitor supervises this logical transaction for its writes. */
|
|
1638
1964
|
function isWriteSupervised(txn) {
|
|
1639
1965
|
return supervisedWriteRoots.has(txn);
|