@harperfast/harper 5.2.9 → 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 -0
- package/dist/resources/DatabaseTransaction.js +459 -45
- 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 +29 -0
- 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 -9
- 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 +1073 -191
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/auditStore.d.ts +11 -2
- package/dist/resources/auditStore.js +325 -71
- 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 -0
- package/dist/resources/databases.js +1200 -239
- 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 -6
- 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 +12 -0
- package/dist/server/storageReclamation.js +26 -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 -3
- package/dist/server/threads/manageThreads.js +517 -65
- 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 +196 -194
- package/package.json +7 -6
- package/resources/DESIGN.md +114 -52
- package/resources/DatabaseTransaction.ts +521 -43
- package/resources/LMDBTransaction.ts +26 -4
- package/resources/PrimaryRocksDatabase.ts +28 -1
- 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 -9
- package/resources/RocksTransactionLogStore.ts +78 -21
- package/resources/Table.ts +1110 -159
- package/resources/auditStore.ts +331 -73
- package/resources/blob.ts +1029 -110
- package/resources/branchDatabase.ts +941 -0
- package/resources/crdt.ts +76 -12
- package/resources/databases.ts +1310 -252
- 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 -7
- 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 +23 -0
- package/server/threads/itc.js +11 -1
- package/server/threads/manageThreads.js +526 -56
- 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-BnCBegQz.js +2267 -0
- package/studio/web/assets/FloatingChat-CoDW1ySS.js +23 -0
- package/studio/web/assets/{abnfDiagram-VCTEODGH-C0_BAZyO.js → abnfDiagram-VCTEODGH-B0BebmD2.js} +1 -1
- package/studio/web/assets/{alertDialog-DIHt7Z0r.js → alertDialog-CQyAJJhl.js} +1 -1
- package/studio/web/assets/{apiToken-c3Rd-w6g.js → apiToken-Bwk5BLXW.js} +1 -1
- package/studio/web/assets/applications-DHxGi7JH.js +296 -0
- package/studio/web/assets/architecture-7GRP2DOG-LB-MLAAb.js +1 -0
- package/studio/web/assets/{architectureDiagram-5GKGNRK7-BWzrASgm.js → architectureDiagram-5GKGNRK7-7SW3GD-K.js} +1 -1
- package/studio/web/assets/authStore-C3Nfubqr.js +3 -0
- package/studio/web/assets/{blockDiagram-NRAW4CY4-BdJX9Khj.js → blockDiagram-I7D4REHJ-BqguiadH.js} +2 -2
- package/studio/web/assets/{button-DhiX-njv.js → button-BIsUKRZq.js} +2 -2
- package/studio/web/assets/{c4Diagram-UCG6FXSJ-CI6MzGmQ.js → c4Diagram-7LVT6UL2-LBNf8t_X.js} +1 -1
- package/studio/web/assets/channel-yictG-U-.js +1 -0
- package/studio/web/assets/{chevron-up-Df2c8uoU.js → chevron-up-DtKGqDn3.js} +1 -1
- package/studio/web/assets/{chunk-TEH6E4GO-P87k5mNi.js → chunk-4HAMMTFA-DWtTut21.js} +1 -1
- package/studio/web/assets/{chunk-75Z2AOVW-BT8tVmks.js → chunk-75Z2AOVW-QGQD6th2.js} +1 -1
- package/studio/web/assets/{chunk-DU6HZSFF-9kAOOmI6.js → chunk-DU6HZSFF-Chq20Ba5.js} +1 -1
- package/studio/web/assets/{chunk-F27PBJKO-BW7ao8AY.js → chunk-F27PBJKO-BVA5EPhV.js} +1 -1
- package/studio/web/assets/{chunk-GMAD6QVW-BNyXpoQO.js → chunk-GMAD6QVW-BeS7S07A.js} +1 -1
- package/studio/web/assets/{chunk-OBVCFTLP-D4wWOqDQ.js → chunk-GVQU2GXP-sbwVIQ8i.js} +1 -1
- package/studio/web/assets/{chunk-G27WJ6UU-COyLMcgK.js → chunk-IMKFNOWR-Bnh3tAVd.js} +1 -1
- package/studio/web/assets/{chunk-JQ64N6SF-Cyz1IeLf.js → chunk-L3NEJ4N5-COfUyKII.js} +1 -1
- package/studio/web/assets/chunk-OSK3NFVY-ByciRftO.js +10 -0
- package/studio/web/assets/{chunk-P2QGCYS3-DmIFY4d7.js → chunk-P2QGCYS3-CP1VhG_c.js} +1 -1
- package/studio/web/assets/{chunk-POPQ4Y6H-BPrvMyKz.js → chunk-POPQ4Y6H-ClWhhkwW.js} +1 -1
- package/studio/web/assets/{chunk-PWAF6VOD-2zB6IW9i.js → chunk-PWAF6VOD-1z1THyS5.js} +1 -1
- package/studio/web/assets/{chunk-RHFEMEQ7-2FgyI8YU.js → chunk-SHT3W25Y-LpQkMsah.js} +2 -2
- package/studio/web/assets/{chunk-SVP7TREG-FwtbH2QC.js → chunk-SVP7TREG-jtdAHw0S.js} +1 -1
- package/studio/web/assets/{chunk-LCL6LL3I-HOzK_ppE.js → chunk-TICWLB2K-VOwzetX-.js} +1 -1
- package/studio/web/assets/classDiagram-ZZMXUADV-VaEwSy_g.js +1 -0
- package/studio/web/assets/classDiagram-v2-VYDZK3BY-VaEwSy_g.js +1 -0
- package/studio/web/assets/{createLucideIcon-BKGPfjm2.js → createLucideIcon-CzW9508A.js} +1 -1
- package/studio/web/assets/{cssMode-CEN2mzSA.js → cssMode-s0cWI-_M.js} +1 -1
- package/studio/web/assets/{cynefin-OW5HDTMX-BRkpLFQV.js → cynefin-OW5HDTMX-BbdbCvub.js} +1 -1
- package/studio/web/assets/{cynefinDiagram-5FMLGOSQ-CHT1DaX6.js → cynefinDiagram-5FMLGOSQ-TP-aIqbt.js} +1 -1
- package/studio/web/assets/{dagre-3AP2YEHR-DpUXBh63.js → dagre-GXQ25YYZ-DShnGpGo.js} +1 -1
- package/studio/web/assets/{diagram-S7CK7UJ4-BuymVFZT.js → diagram-S7CK7UJ4-aoCVTtcy.js} +1 -1
- package/studio/web/assets/{diagram-UQ7AKVKN-CyP148RM.js → diagram-UQ7AKVKN-DglXtQ6x.js} +1 -1
- package/studio/web/assets/{diagram-VSXAHHWV-CoCAg3M9.js → diagram-VSXAHHWV-fhEdmkwM.js} +1 -1
- package/studio/web/assets/{diagram-VX7I27RA-BpOqCFca.js → diagram-VX7I27RA-DccVJet6.js} +1 -1
- package/studio/web/assets/{diagram-Z3DM3KII-Bfpw7Vbj.js → diagram-Z3DM3KII-D-RyJJb7.js} +1 -1
- package/studio/web/assets/{dialog-CBf0Mr1d.js → dialog-Cn2uWgD4.js} +1 -1
- package/studio/web/assets/{dist-lkA3O3eM.js → dist-DP8UjMB_.js} +1 -1
- package/studio/web/assets/{download-BtTOBem-.js → download-B5T5r7ss.js} +1 -1
- package/studio/web/assets/{ebnfDiagram-PWID7BFC-DS_6aWqL.js → ebnfDiagram-PWID7BFC-DJGpIpz_.js} +1 -1
- package/studio/web/assets/{editor-D8oDeCTL.js → editor-DNcRHK54.js} +1 -1
- package/studio/web/assets/{erDiagram-SSCWMZ5O-DJNk6Fgw.js → erDiagram-RLTQ6QDP-CIfNlgkC.js} +1 -1
- package/studio/web/assets/eventmodeling-NTZA5JFV-CLxnp2CR.js +1 -0
- package/studio/web/assets/flowDiagram-HODETNUW-BIbhmz9f.js +1 -0
- package/studio/web/assets/{ganttDiagram-EL5Y4UJY-2pOExxMY.js → ganttDiagram-EL5Y4UJY-BxToTzzD.js} +1 -1
- package/studio/web/assets/{getAnalytics-D4LKGeVy.js → getAnalytics-GHK8ORfM.js} +1 -1
- package/studio/web/assets/{gitGraph-4MIJSDKK-CH5ZxwzF.js → gitGraph-4MIJSDKK-D2s2w8lE.js} +1 -1
- package/studio/web/assets/{gitGraphDiagram-WWUBYQGX-DVIsIhbO.js → gitGraphDiagram-WWUBYQGX-Dwntd4-x.js} +1 -1
- package/studio/web/assets/{html-u3vOg7LJ.js → html-Bdssedlg.js} +1 -1
- package/studio/web/assets/{htmlMode-DyO31v-P.js → htmlMode-CoDlJ3fw.js} +1 -1
- package/studio/web/assets/{index-Cxj2_wsl.css → index-7RMEgVG1.css} +1 -1
- package/studio/web/assets/index-D6sxmFLR.js +824 -0
- package/studio/web/assets/index.lazy-tmU5BS8s.js +2 -0
- package/studio/web/assets/{info-A6RAGUB7-CPQfTnaG.js → info-A6RAGUB7-DYjkvb0C.js} +1 -1
- package/studio/web/assets/{infoDiagram-RXCK75RN-DlwLYlwm.js → infoDiagram-27XIBGKW-Bnp1FJE5.js} +1 -1
- package/studio/web/assets/{ishikawaDiagram-5VMMS53U-BRXRp29U.js → ishikawaDiagram-5VMMS53U-D9Xh2r6X.js} +1 -1
- package/studio/web/assets/{javascript-CUvxOyTC.js → javascript-B8meVSTH.js} +1 -1
- package/studio/web/assets/{journeyDiagram-EYS64GPL-B0ou8k0n.js → journeyDiagram-3NMN7TZE-CokIi6ll.js} +2 -2
- package/studio/web/assets/{jsonMode-f_IwbF3D.js → jsonMode-DpIPd35T.js} +1 -1
- package/studio/web/assets/{kanban-definition-3QL26DDD-uYg7iYzp.js → kanban-definition-UXKFOSKX-CukSFJfX.js} +1 -1
- package/studio/web/assets/{languageServices-DXtZ6rEF.js → languageServices-C_5FMJzQ.js} +1 -1
- package/studio/web/assets/{lspLanguageFeatures-B4pCF1zO.js → lspLanguageFeatures-BIzNBzPK.js} +1 -1
- package/studio/web/assets/{mermaid-parser.core-Ck-fC8b7.js → mermaid-parser.core-BlEsOWNO.js} +3 -3
- package/studio/web/assets/{mermaid.core-CP8aNNYm.js → mermaid.core-BlkGaMIH.js} +5 -5
- package/studio/web/assets/{mindmap-definition-FBJOCRG2-CgTZ-rit.js → mindmap-definition-YA3MSWOX-IprMc_0j.js} +1 -1
- package/studio/web/assets/{notifications-D3tIQ4sg.js → notifications-CQf18QKb.js} +1 -1
- package/studio/web/assets/notifications-CvZivSbh.js +1 -0
- package/studio/web/assets/{packet-AYTQ26CC-DEyoPtPb.js → packet-AYTQ26CC-Bi3V04Zi.js} +1 -1
- package/studio/web/assets/{pegDiagram-XKGWAZYB-DrD-7sD9.js → pegDiagram-XKGWAZYB-BNuPDLZY.js} +1 -1
- package/studio/web/assets/{pie-WAS4IAKB-wjj-EI1d.js → pie-WAS4IAKB-_6DoDbng.js} +1 -1
- package/studio/web/assets/{pieDiagram-E7YTZNPT-GntqDCzv.js → pieDiagram-E7YTZNPT-DqNb6Ht2.js} +1 -1
- package/studio/web/assets/{profile-DZWU7MgT.js → profile-DdOwtntb.js} +1 -1
- package/studio/web/assets/{quadrantDiagram-AXDQQJYC-0UeqXQGd.js → quadrantDiagram-AXDQQJYC-BGH9E2YR.js} +1 -1
- package/studio/web/assets/{radar-RG4KPBEZ-DFSA5h7k.js → radar-RG4KPBEZ-DDdVczcL.js} +1 -1
- package/studio/web/assets/{railroad-74A4TZTK-CaOUG9wR.js → railroad-74A4TZTK-BJUP4Jds.js} +1 -1
- package/studio/web/assets/railroad-abnf-HS5TGJTU-Bm3L1L0h.js +1 -0
- package/studio/web/assets/railroad-ebnf-LZEXJU2U-CKzLGlkw.js +1 -0
- package/studio/web/assets/railroad-peg-WCYAUIDC-S8xLjslx.js +1 -0
- package/studio/web/assets/{railroadDiagram-O6MQD6OU-yHUELZaV.js → railroadDiagram-O6MQD6OU-DGTPh2KZ.js} +1 -1
- package/studio/web/assets/{regions-CkyurXzE.js → regions-n69fwagr.js} +1 -1
- package/studio/web/assets/{register-BUyhWjBO.js → register-PfWTCXWB.js} +3 -3
- package/studio/web/assets/{requirementDiagram-EFPCY7ZU-DNEGFjuW.js → requirementDiagram-BXWQKSXE-BJnO6uLz.js} +1 -1
- package/studio/web/assets/{sankeyDiagram-P5KCCOFB-DpyAmSVR.js → sankeyDiagram-P5KCCOFB-0vSOdymH.js} +1 -1
- package/studio/web/assets/{sequenceDiagram-WJ2MYXX4-TyaT7xNk.js → sequenceDiagram-WJ2MYXX4-hETizDWE.js} +1 -1
- package/studio/web/assets/{setComponentFile-CeyKSZAa.js → setComponentFile-Bg6O7X0S.js} +1 -1
- package/studio/web/assets/{setup-D2kn7cAA.js → setup-CUx_aUDl.js} +2 -2
- package/studio/web/assets/{stateDiagram-HBIQ2CUA-CeEdTArZ.js → stateDiagram-D77RDMKH-CdYQ_KtC.js} +1 -1
- package/studio/web/assets/stateDiagram-v2-MP3YSRHH-CdKuzQMT.js +1 -0
- package/studio/web/assets/status-D7BVKqX9.js +61 -0
- package/studio/web/assets/{swimlanes-XN3QIQJK-B54FmF46.js → swimlanes-42K2YHIH-B8cHIpU4.js} +1 -1
- package/studio/web/assets/swimlanesDiagram-VR7AAH4N-DmOSJwaH.js +8 -0
- package/studio/web/assets/{tabs-B_G5zscN.js → tabs-BrHu7gJi.js} +1 -1
- package/studio/web/assets/{timeline-definition-24CTP7MA-D-a9ujbo.js → timeline-definition-24CTP7MA-BJWYSXqF.js} +1 -1
- package/studio/web/assets/{toggleHighContrast-C0UW6rI2.js → toggleHighContrast-DBSyXzMr.js} +1 -1
- package/studio/web/assets/{treeView-Q6P3EWNA-CrW_6JnS.js → treeView-Q6P3EWNA-qxe_v6CQ.js} +1 -1
- package/studio/web/assets/{treemap-WGGIJYW6-BxyYLdP_.js → treemap-WGGIJYW6-dDo97XXF.js} +1 -1
- package/studio/web/assets/{tsMode-DBC0zmDx.js → tsMode-BByKCjBS.js} +1 -1
- package/studio/web/assets/{typescript-DApRQir3.js → typescript-DDLnLpw9.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-DB6JStU1.js → useEntityRestURL-31CHGaHk.js} +1 -1
- package/studio/web/assets/{useLocalStorage-Dtj1QS8_.js → useLocalStorage-BqMR3D8_.js} +1 -1
- package/studio/web/assets/vendor-core-c2JRRJpV.js +58 -0
- package/studio/web/assets/vendor-datadog-CLUcJXOo.js +6 -0
- package/studio/web/assets/{vendor-react-Dyj4O3HE.js → vendor-react-CJV_K1u4.js} +1 -1
- package/studio/web/assets/vendor-tanstack-DxzraizX.js +1 -0
- package/studio/web/assets/{vendor-ui-vhu-UHhF.js → vendor-ui-BUjK0h8a.js} +2 -2
- package/studio/web/assets/{vennDiagram-4TSXK5OY-Cy7s7Mpy.js → vennDiagram-4TSXK5OY-A3i-lCdl.js} +1 -1
- package/studio/web/assets/{wardley-WFR3VGLG-BeBL35g2.js → wardley-WFR3VGLG-B0ik-_6g.js} +1 -1
- package/studio/web/assets/{wardleyDiagram-VM6X3IG4-BylmIGSg.js → wardleyDiagram-VM6X3IG4-CjrkKWUR.js} +1 -1
- package/studio/web/assets/{workers-C0bFIedw.js → workers-pR3jRY9D.js} +1 -1
- package/studio/web/assets/x-DIzaLEdK.js +1 -0
- package/studio/web/assets/{xml-HWd01lU-.js → xml-2iRnMhQO.js} +1 -1
- package/studio/web/assets/{xychartDiagram-S5SC5T6Z-CoKALMXr.js → xychartDiagram-S5SC5T6Z-Biok4GYV.js} +1 -1
- package/studio/web/assets/{yaml-CIH0Nt-h.js → yaml-Bf92gJpd.js} +1 -1
- package/studio/web/index.html +14 -14
- 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
- package/studio/web/assets/Chat-JpO8EtUu.js +0 -2067
- package/studio/web/assets/FloatingChat-Bcj3xSZu.js +0 -23
- package/studio/web/assets/applications-ByqLRKyZ.js +0 -296
- package/studio/web/assets/architecture-7GRP2DOG-DNdx5tEU.js +0 -1
- package/studio/web/assets/authStore-qKmCZcaf.js +0 -3
- package/studio/web/assets/channel-DtCV8PTL.js +0 -1
- package/studio/web/assets/chunk-R7TYR2AO-Irip67yr.js +0 -10
- package/studio/web/assets/classDiagram-DTDB5LWJ-DbO_dCNE.js +0 -1
- package/studio/web/assets/classDiagram-v2-JRS7N3AN-DbO_dCNE.js +0 -1
- package/studio/web/assets/eventmodeling-NTZA5JFV-5jbe4A5P.js +0 -1
- package/studio/web/assets/flowDiagram-A5DVABFB-Dp9Ezlow.js +0 -1
- package/studio/web/assets/index-aSt5tY-L.js +0 -824
- package/studio/web/assets/index.lazy-B9jiPwT8.js +0 -2
- package/studio/web/assets/notifications-CUoYgU98.js +0 -1
- package/studio/web/assets/railroad-abnf-HS5TGJTU-Bc0Qi0WH.js +0 -1
- package/studio/web/assets/railroad-ebnf-LZEXJU2U-G8rVVZ2C.js +0 -1
- package/studio/web/assets/railroad-peg-WCYAUIDC-CrehKBhC.js +0 -1
- package/studio/web/assets/stateDiagram-v2-4QOOHH4V-D4tuw9Su.js +0 -1
- package/studio/web/assets/status-D7Xn5ePA.js +0 -61
- package/studio/web/assets/swimlanesDiagram-VK2B7HYN-XOhmNEvq.js +0 -8
- package/studio/web/assets/vendor-core-RCcadM3e.js +0 -73
- package/studio/web/assets/vendor-datadog-BRv-mOv1.js +0 -6
- package/studio/web/assets/vendor-tanstack-BiFWSB3W.js +0 -1
- package/studio/web/assets/x-B9o9hsep.js +0 -1
- /package/studio/web/assets/{sizeCapture-X5ZJPWSS-B0uUizjq.js → sizeCapture-INFHLROL-B0uUizjq.js} +0 -0
|
@@ -0,0 +1,892 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.reportClaimProgress = reportClaimProgress;
|
|
40
|
+
exports.claimDeadlineFor = claimDeadlineFor;
|
|
41
|
+
exports.getOrCreateBranch = getOrCreateBranch;
|
|
42
|
+
exports.closeBranchAt = closeBranchAt;
|
|
43
|
+
exports.removeBranches = removeBranches;
|
|
44
|
+
exports.applicationHasBranchStorage = applicationHasBranchStorage;
|
|
45
|
+
exports.removeBranchesForApplication = removeBranchesForApplication;
|
|
46
|
+
exports.prepareBranches = prepareBranches;
|
|
47
|
+
const node_fs_1 = require("node:fs");
|
|
48
|
+
const promises_1 = require("node:fs/promises");
|
|
49
|
+
const node_path_1 = require("node:path");
|
|
50
|
+
const harper_logger_ts_1 = __importDefault(require("../utility/logging/harper_logger.js"));
|
|
51
|
+
const hdbTerms_ts_1 = require("../utility/hdbTerms.js");
|
|
52
|
+
const common_validators_ts_1 = require("../validation/common_validators.js");
|
|
53
|
+
const env = __importStar(require("../utility/environment/environmentManager.js"));
|
|
54
|
+
const blobBackup_ts_1 = require("../dataLayer/blobBackup.js");
|
|
55
|
+
const blob_ts_1 = require("./blob.js");
|
|
56
|
+
const rocksdb_js_1 = require("@harperfast/rocksdb-js");
|
|
57
|
+
const databases_ts_1 = require("./databases.js");
|
|
58
|
+
const replayLogs_ts_1 = require("./replayLogs.js");
|
|
59
|
+
/**
|
|
60
|
+
* Private per-application forks of a database, for running several variants of an application against
|
|
61
|
+
* isolated copies of the same data (harper#642).
|
|
62
|
+
*
|
|
63
|
+
* A fork is durable and its location is derived only from the application and database names, so it
|
|
64
|
+
* survives restarts and every node in a cluster names it identically — the identity the data needs
|
|
65
|
+
* to be addressed, and eventually replicated, cluster-wide.
|
|
66
|
+
*
|
|
67
|
+
* Every worker thread loads the same applications, so all of them ask for the same branch at once.
|
|
68
|
+
* Only one may take the checkpoint — a second `createCheckpoint` against a populated directory fails,
|
|
69
|
+
* and worse, two checkpoints would give two threads divergent private data for one application. The
|
|
70
|
+
* claim is settled through a state word in a buffer the base store shares across threads, which is
|
|
71
|
+
* the same mechanism the blob id allocator and the HNSW node counter already use.
|
|
72
|
+
*/
|
|
73
|
+
const UNCLAIMED = 0n;
|
|
74
|
+
const CREATING = 1n;
|
|
75
|
+
const READY = 2n;
|
|
76
|
+
const CLAIM_STATE = 0;
|
|
77
|
+
const CLAIM_PROGRESS = 1;
|
|
78
|
+
const MAX_NAME_LENGTH = common_validators_ts_1.commonValidators.schema_length.maximum;
|
|
79
|
+
/** How long a loser waits on a winner that reports NO progress before giving up on the branch; the
|
|
80
|
+
* winner's replay budget is added on top where the deadline is built (`openOrCreate`). Progress is
|
|
81
|
+
* reported per checkpoint and per cloned file, so that is the granularity this bounds: a single
|
|
82
|
+
* unit that takes longer than the budget still expires it, which needs a byte copy (no hard links)
|
|
83
|
+
* of one blob tens of gigabytes large. */
|
|
84
|
+
const CLAIM_TIMEOUT_MS = 10 * 60 * 1000;
|
|
85
|
+
// Keyed on the in-flight open, not the finished one: within a thread the losers of the claim wake on
|
|
86
|
+
// READY at the same moment and would otherwise each try to open the directory the winner just opened.
|
|
87
|
+
const branchesByPath = new Map();
|
|
88
|
+
/**
|
|
89
|
+
* The claim word is process-local shared memory (in-process only, seeded UNCLAIMED each boot) and
|
|
90
|
+
* must stay that way: the winner's tail replay runs exactly once per boot because every boot starts
|
|
91
|
+
* from UNCLAIMED. A durable or cross-process claim would read READY on restart and silently skip
|
|
92
|
+
* recovery.
|
|
93
|
+
*/
|
|
94
|
+
function claimStateFor(baseName, branchPath) {
|
|
95
|
+
const baseStore = (0, databases_ts_1.database)({ database: baseName, table: undefined });
|
|
96
|
+
const seed = new BigInt64Array([UNCLAIMED, 0n]);
|
|
97
|
+
return new BigInt64Array(baseStore.getUserSharedBuffer(`branch-claim:${branchPath}`, seed.buffer));
|
|
98
|
+
}
|
|
99
|
+
function reportClaimProgress(state) {
|
|
100
|
+
Atomics.add(state, CLAIM_PROGRESS, 1n);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* A deadline that follows the winner's progress rather than the clock alone. The claim window covers a
|
|
104
|
+
* hard-link clone of the base's entire blob tree, so any fixed budget is wrong for a large enough base:
|
|
105
|
+
* every waiting thread's load fails while the winner is healthily copying, the winner then publishes,
|
|
106
|
+
* and the next deploy succeeds -- so a real outage reads as a flap. Restarting the budget on each unit
|
|
107
|
+
* of reported work bounds a winner that has STOPPED rather than one that is merely slow.
|
|
108
|
+
*/
|
|
109
|
+
function claimDeadlineFor(state, budget) {
|
|
110
|
+
let seen = Atomics.load(state, CLAIM_PROGRESS);
|
|
111
|
+
let at = Date.now() + budget;
|
|
112
|
+
return () => {
|
|
113
|
+
const progress = Atomics.load(state, CLAIM_PROGRESS);
|
|
114
|
+
if (progress !== seen) {
|
|
115
|
+
seen = progress;
|
|
116
|
+
at = Date.now() + budget;
|
|
117
|
+
}
|
|
118
|
+
return at;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Reproduce the base's blob roots as the branch's own, hard-linking each file so the OS inode
|
|
123
|
+
* refcount does the reference counting: the branch and base share the bytes, each holds its own
|
|
124
|
+
* directory entry, and the data is freed only when the last link to it goes. That is what lets the
|
|
125
|
+
* branch keep its own ID allocator -- `getNextFileId` seeds from the highest filename already in its
|
|
126
|
+
* directory, and the clone carries the base's filenames -- with no shared allocator, no high-water
|
|
127
|
+
* mark, and no gated deletion (harper#644).
|
|
128
|
+
*
|
|
129
|
+
* A blob that is mid-write or already reclaimed is substituted with a marker rather than linked. A
|
|
130
|
+
* partially-written blob would otherwise be linked as a whole one, and worse, the abort that follows
|
|
131
|
+
* stamps PENDING onto that inode IN PLACE -- through the branch's link as well, since it is the same
|
|
132
|
+
* inode. The marker makes that blob fail loudly in the branch instead.
|
|
133
|
+
*/
|
|
134
|
+
async function cloneBlobRoots(baseName, baseRoots, branchRoots, progress) {
|
|
135
|
+
let substituted = 0;
|
|
136
|
+
let copied = 0;
|
|
137
|
+
// A row's `storageIndex` is a position in the branch's own list, and its marker records every entry.
|
|
138
|
+
for (let index = 0; index < branchRoots.length; index++) {
|
|
139
|
+
const staging = `${branchRoots[index]}.staging`;
|
|
140
|
+
await (0, promises_1.rm)(staging, { recursive: true, force: true });
|
|
141
|
+
// Created up front rather than left to the walk: a base with no blobs at all copies nothing, and
|
|
142
|
+
// the branch still needs its own (empty) root to exist so its allocator starts where the base's
|
|
143
|
+
// would have.
|
|
144
|
+
await (0, promises_1.mkdir)(staging, { recursive: true });
|
|
145
|
+
const source = baseRoots[index];
|
|
146
|
+
if (source) {
|
|
147
|
+
const counts = await (0, blobBackup_ts_1.copyTree)(source, staging, true, {
|
|
148
|
+
gone: 'blob was already reclaimed from the base when this branch was created',
|
|
149
|
+
pending: 'blob was still being written to the base when this branch was created',
|
|
150
|
+
}, progress);
|
|
151
|
+
substituted += counts.substituted;
|
|
152
|
+
copied += counts.copied;
|
|
153
|
+
}
|
|
154
|
+
await (0, promises_1.rm)(branchRoots[index], { recursive: true, force: true });
|
|
155
|
+
await (0, promises_1.mkdir)((0, node_path_1.dirname)(branchRoots[index]), { recursive: true });
|
|
156
|
+
await (0, promises_1.rename)(staging, branchRoots[index]);
|
|
157
|
+
progress();
|
|
158
|
+
}
|
|
159
|
+
if (substituted > 0) {
|
|
160
|
+
harper_logger_ts_1.default.warn?.(`Branch of '${baseName}' substituted ${substituted} blob file(s) with markers because they were ` +
|
|
161
|
+
`mid-write or already reclaimed when it was created; reads of those blobs in the branch will fail`);
|
|
162
|
+
}
|
|
163
|
+
if (copied > 0) {
|
|
164
|
+
// The hard link is the whole design: without it the branch is a second full copy of the base's
|
|
165
|
+
// blobs, taken while the application is loading, and nothing else in the log would say so.
|
|
166
|
+
harper_logger_ts_1.default.warn?.(`Branch of '${baseName}' could not hard-link ${copied} blob file(s) and copied their bytes instead; ` +
|
|
167
|
+
`on a filesystem without hard links a branch costs a full copy of the base's blobs, in disk and ` +
|
|
168
|
+
`in the time every application load that creates one takes`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Remove the given blob roots and any staging siblings left by an interrupted clone. The roots are
|
|
173
|
+
* passed in rather than re-derived, because a published branch owns exactly what its completion
|
|
174
|
+
* marker recorded: deriving them from current configuration would let a `storage.blobPaths` entry
|
|
175
|
+
* added since -- one that may already hold a `<storeName>` directory restored from elsewhere --
|
|
176
|
+
* be deleted by a branch that never wrote it.
|
|
177
|
+
*
|
|
178
|
+
* Reports whether every removal succeeded, because a root left behind still belongs to this
|
|
179
|
+
* identity and the caller must not hand the name back while it does.
|
|
180
|
+
*/
|
|
181
|
+
async function removeBlobRoots(roots) {
|
|
182
|
+
let removed = true;
|
|
183
|
+
for (const root of roots) {
|
|
184
|
+
for (const target of [root, `${root}.staging`]) {
|
|
185
|
+
await (0, promises_1.rm)(target, { recursive: true, force: true }).catch((error) => {
|
|
186
|
+
removed = false;
|
|
187
|
+
harper_logger_ts_1.default.warn?.(`Could not remove branch blob root ${target}`, error);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return removed;
|
|
192
|
+
}
|
|
193
|
+
const COMPLETION_MARKER = '.branch-complete';
|
|
194
|
+
/**
|
|
195
|
+
* Does this directory hold database data? Not "does CURRENT exist" -- a store that lost CURRENT still
|
|
196
|
+
* has its MANIFEST and SSTs, and a single missing file cannot stand for "there is nothing here to
|
|
197
|
+
* lose". Anything that answers yes is never deleted, only refused.
|
|
198
|
+
*/
|
|
199
|
+
function holdsStoreData(branchPath) {
|
|
200
|
+
let entries;
|
|
201
|
+
try {
|
|
202
|
+
entries = (0, node_fs_1.readdirSync)(branchPath);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
return entries.some((name) => name === 'CURRENT' || /^(MANIFEST|OPTIONS)-/.test(name) || name.endsWith('.sst') || name.endsWith('.log'));
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* What a branch directory on disk actually is, and in particular whether it may be destroyed.
|
|
211
|
+
*
|
|
212
|
+
* A branch's blob roots live on the volumes named by `storage.blobPaths`, not inside the branch
|
|
213
|
+
* directory, so the two cannot be published by one atomic rename and nothing fsyncs either parent. A
|
|
214
|
+
* directory alone therefore proves nothing: trusting it would adopt a branch whose allocator restarts
|
|
215
|
+
* from an empty root and mints file ids its own checkpointed rows already reference. The marker,
|
|
216
|
+
* written inside staging before the single rename that publishes it, is what proves both halves
|
|
217
|
+
* landed -- and recording the roots keeps an operator adding a `blobPaths` entry from reading as
|
|
218
|
+
* damage, since the new root was never this branch's.
|
|
219
|
+
*
|
|
220
|
+
* Materialization cannot leave a marker-less branch directory, so anything marker-less that still
|
|
221
|
+
* looks like a store came from elsewhere -- a branch older than the marker, or one whose marker was
|
|
222
|
+
* lost -- and is carrying data. It is refused, never deleted.
|
|
223
|
+
*/
|
|
224
|
+
function readBranchState(branchPath, storeName) {
|
|
225
|
+
if (!(0, node_fs_1.existsSync)(branchPath))
|
|
226
|
+
return { state: 'absent' };
|
|
227
|
+
const looksLikeAStore = holdsStoreData(branchPath);
|
|
228
|
+
let recorded;
|
|
229
|
+
try {
|
|
230
|
+
recorded = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(branchPath, COMPLETION_MARKER), 'utf8'));
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return looksLikeAStore ? { state: 'unmarked', why: 'it has no readable completion marker' } : { state: 'debris' };
|
|
234
|
+
}
|
|
235
|
+
// `JSON.parse('null')` succeeds and yields null, so this cannot go straight to a property access.
|
|
236
|
+
// An empty list would pass the prefix check below and pin the branch to no roots at all, so the
|
|
237
|
+
// first blob write would fail on an undefined path instead of here, where it can say why.
|
|
238
|
+
if (recorded == null ||
|
|
239
|
+
!Array.isArray(recorded.blobRoots) ||
|
|
240
|
+
recorded.blobRoots.length === 0 ||
|
|
241
|
+
recorded.blobRoots.some((r) => typeof r !== 'string')) {
|
|
242
|
+
// Same split as an unreadable marker above: refuse anything carrying store data, but a directory
|
|
243
|
+
// holding nothing but a bad marker has nothing to lose and must stay clearable.
|
|
244
|
+
return looksLikeAStore ? { state: 'unmarked', why: 'its completion marker is malformed' } : { state: 'debris' };
|
|
245
|
+
}
|
|
246
|
+
const configured = (0, blob_ts_1.getBlobPathsForDatabaseName)(storeName);
|
|
247
|
+
// By index, not by set: `storageIndex` on a row is a position in this list, so a reordered
|
|
248
|
+
// `storage.blobPaths` would silently resolve every row through the wrong root. A configured list
|
|
249
|
+
// that only GREW leaves every recorded index exactly where it was, which is why an appended volume
|
|
250
|
+
// is compatible where a removed, reordered or replaced one is not -- the branch goes on resolving
|
|
251
|
+
// through what it recorded, so the appended root is simply never its.
|
|
252
|
+
if (recorded.blobRoots.length > configured.length || recorded.blobRoots.some((root, i) => root !== configured[i])) {
|
|
253
|
+
return {
|
|
254
|
+
state: 'damaged',
|
|
255
|
+
problem: `its blob roots no longer match the configured storage.blobPaths ` +
|
|
256
|
+
`(recorded ${JSON.stringify(recorded.blobRoots)}, configured ${JSON.stringify(configured)})`,
|
|
257
|
+
blobRoots: recorded.blobRoots,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
const missing = recorded.blobRoots.filter((root) => !(0, node_fs_1.existsSync)(root));
|
|
261
|
+
return missing.length
|
|
262
|
+
? { state: 'damaged', problem: `blob root(s) are missing: ${missing.join(', ')}`, blobRoots: recorded.blobRoots }
|
|
263
|
+
: { state: 'complete', blobRoots: recorded.blobRoots };
|
|
264
|
+
}
|
|
265
|
+
function warnAboutUnusedBlobRoots(branchPath, storeName, blobRoots) {
|
|
266
|
+
const configured = (0, blob_ts_1.getBlobPathsForDatabaseName)(storeName);
|
|
267
|
+
if (blobRoots.length < configured.length) {
|
|
268
|
+
harper_logger_ts_1.default.warn?.(`Branch database at ${branchPath} keeps the ${blobRoots.length} blob root(s) it was published ` +
|
|
269
|
+
`with; the ${configured.length - blobRoots.length} storage.blobPaths entry(ies) added since are ` +
|
|
270
|
+
`not used by it. Its rows address roots by position, so only a branch rebuilt from the base ` +
|
|
271
|
+
`can take the added capacity.`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Take the checkpoint into a temporary sibling and rename it into place, so a crash mid-copy leaves
|
|
276
|
+
* debris rather than a half-populated directory that RocksDB would then refuse to open. Reports the
|
|
277
|
+
* blob roots the marker records, which is what the branch is then pinned to.
|
|
278
|
+
*/
|
|
279
|
+
async function materializeBranch(baseName, branchPath, storeName, report) {
|
|
280
|
+
const staging = `${branchPath}.staging`;
|
|
281
|
+
const blobRoots = (0, blob_ts_1.getBlobPathsForDatabaseName)(storeName);
|
|
282
|
+
// Publishing an empty root list would record a marker no later load can accept, bricking the branch
|
|
283
|
+
// on every subsequent boot instead of failing here, where the configuration is the visible problem.
|
|
284
|
+
if (blobRoots.length === 0) {
|
|
285
|
+
throw new Error(`Cannot create the branch at ${branchPath}: storage.blobPaths is configured with no entries, so ` +
|
|
286
|
+
`the branch has nowhere of its own to keep blobs`);
|
|
287
|
+
}
|
|
288
|
+
const base = (0, databases_ts_1.database)({ database: baseName, table: undefined });
|
|
289
|
+
// What the base's own rows resolve through, which is not the configured list if `storage.blobPaths`
|
|
290
|
+
// changed while its store was open. Refuse before taking a checkpoint: the configuration already
|
|
291
|
+
// proves that publishing this clone would leave rows on an omitted volume unreadable.
|
|
292
|
+
const baseRoots = (0, blob_ts_1.getRootBlobPathsForDB)(base);
|
|
293
|
+
if (baseRoots.length > blobRoots.length) {
|
|
294
|
+
throw new Error(`Cannot create a branch of '${baseName}': the open base resolves through ${baseRoots.length} blob ` +
|
|
295
|
+
`root(s), but storage.blobPaths now provides ${blobRoots.length}; publishing the branch would ` +
|
|
296
|
+
`make rows on the omitted volume(s) unreadable`);
|
|
297
|
+
}
|
|
298
|
+
await (0, promises_1.rm)(staging, { recursive: true, force: true });
|
|
299
|
+
await (0, promises_1.mkdir)((0, node_path_1.dirname)(branchPath), { recursive: true });
|
|
300
|
+
try {
|
|
301
|
+
await base.createCheckpoint(staging);
|
|
302
|
+
report.progress();
|
|
303
|
+
await cloneBlobRoots(baseName, baseRoots, blobRoots, report.progress);
|
|
304
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(staging, COMPLETION_MARKER), JSON.stringify({ blobRoots }));
|
|
305
|
+
await (0, promises_1.rename)(staging, branchPath);
|
|
306
|
+
return blobRoots;
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
await (0, promises_1.rm)(staging, { recursive: true, force: true }).catch(() => { });
|
|
310
|
+
// The blob roots too, staging siblings included: a clone that failed partway leaves the roots it
|
|
311
|
+
// already published and nothing owns them once this attempt is abandoned. Only safe because this
|
|
312
|
+
// branch never became complete -- one that did is never rebuilt, so this cannot remove the last
|
|
313
|
+
// copy of anything.
|
|
314
|
+
// Surviving roots still hold this identity's files, so the caller must not release the name.
|
|
315
|
+
if (!(await removeBlobRoots(blobRoots)))
|
|
316
|
+
report.strandedRoots();
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* `Atomics.wait`/`notify` need shared memory, and `getUserSharedBuffer` hands back a plain
|
|
322
|
+
* `ArrayBuffer` when nothing else has asked for the key yet — a single-threaded process, or the
|
|
323
|
+
* first caller in. The state word still works there (load/store/compareExchange do not require
|
|
324
|
+
* sharing) and concurrent async callers within one thread still contend, so waiting falls back to
|
|
325
|
+
* polling rather than throwing.
|
|
326
|
+
*/
|
|
327
|
+
function isShared(state) {
|
|
328
|
+
return typeof SharedArrayBuffer !== 'undefined' && state.buffer instanceof SharedArrayBuffer;
|
|
329
|
+
}
|
|
330
|
+
function wakeWaiters(state) {
|
|
331
|
+
if (isShared(state))
|
|
332
|
+
Atomics.notify(state, CLAIM_STATE);
|
|
333
|
+
}
|
|
334
|
+
/** Wait until the claim leaves CREATING, and report the state it settled on. */
|
|
335
|
+
async function awaitClaim(state, branchPath, deadline) {
|
|
336
|
+
// A progress-extended deadline has no ceiling, so a wait past the original budget must say why it is
|
|
337
|
+
// still waiting rather than reading as a hang with no thread to point at.
|
|
338
|
+
const patience = deadline();
|
|
339
|
+
let announced = false;
|
|
340
|
+
for (;;) {
|
|
341
|
+
const current = Atomics.load(state, CLAIM_STATE);
|
|
342
|
+
if (current !== CREATING)
|
|
343
|
+
return current;
|
|
344
|
+
const remaining = deadline() - Date.now();
|
|
345
|
+
if (remaining <= 0)
|
|
346
|
+
throw new Error(`Timed out waiting for another thread to create the branch at ${branchPath}`);
|
|
347
|
+
// After the timeout check, and only once the deadline itself has moved: on the clock alone this
|
|
348
|
+
// would claim the winner is alive on the very turn the wait gives up on it.
|
|
349
|
+
if (!announced && Date.now() > patience && deadline() > patience) {
|
|
350
|
+
announced = true;
|
|
351
|
+
harper_logger_ts_1.default.warn?.(`Still waiting for another thread to create the branch at ${branchPath}; it is reporting ` +
|
|
352
|
+
`progress, so this wait is extended for as long as it keeps doing so`);
|
|
353
|
+
}
|
|
354
|
+
const slice = Math.min(remaining, 1000);
|
|
355
|
+
if (isShared(state)) {
|
|
356
|
+
const wait = Atomics.waitAsync(state, CLAIM_STATE, CREATING, slice);
|
|
357
|
+
if (wait.async)
|
|
358
|
+
await wait.value;
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(slice, 5)));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Get this process's branch of `baseName` for `appName`, creating it on first call. Concurrent
|
|
367
|
+
* callers across worker threads all receive the same branch.
|
|
368
|
+
*/
|
|
369
|
+
async function getOrCreateBranch(baseName, appName) {
|
|
370
|
+
const branchPath = (0, databases_ts_1.resolveBranchPath)(baseName, appName);
|
|
371
|
+
let pending = branchesByPath.get(branchPath);
|
|
372
|
+
if (!pending) {
|
|
373
|
+
pending = openOrCreate(baseName, appName, branchPath);
|
|
374
|
+
branchesByPath.set(branchPath, pending);
|
|
375
|
+
// A failed attempt must not be cached, or the application can never load on a later retry.
|
|
376
|
+
pending.catch(() => branchesByPath.delete(branchPath));
|
|
377
|
+
}
|
|
378
|
+
return (await pending).branch;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Two (application, database) pairs must never name one store: blob file ids restart from each
|
|
382
|
+
* store's own counter, so a shared identity means two branches minting identical file paths. Joining
|
|
383
|
+
* with a separator is not injective -- ('a__b', 'c') and ('a', 'b__c') both give 'a__b__c' -- so the
|
|
384
|
+
* application name's length goes in front, the way the directory layout keeps them separate.
|
|
385
|
+
*/
|
|
386
|
+
function branchStoreName(appName, baseName) {
|
|
387
|
+
return `${appName.length}_${appName}__${baseName}`;
|
|
388
|
+
}
|
|
389
|
+
async function openOrCreate(baseName, appName, branchPath) {
|
|
390
|
+
const claimState = claimStateFor(baseName, branchPath);
|
|
391
|
+
const storeName = branchStoreName(appName, baseName);
|
|
392
|
+
// Reserved before the claim, and so before materialization: cloning the blob tree REMOVES and
|
|
393
|
+
// replaces the root this identity resolves to, while `openBranchDatabase`'s own copy of this check
|
|
394
|
+
// runs only after that has happened. A database created while the checkpoint is still copying
|
|
395
|
+
// cannot take the same name.
|
|
396
|
+
(0, databases_ts_1.reserveBranchIdentity)(storeName);
|
|
397
|
+
let handedOver = false;
|
|
398
|
+
// An abandoned attempt whose roots could not be removed does not hand the name back to a DATABASE:
|
|
399
|
+
// those roots still hold this identity's files, and a database created under the name would resolve
|
|
400
|
+
// its own new ids onto them. The branch may still retake it, which is what repairs the condition.
|
|
401
|
+
let blobRootsStranded = false;
|
|
402
|
+
try {
|
|
403
|
+
// The claim's CREATING window now covers the winner's replay as well as its checkpoint, so
|
|
404
|
+
// waiters must outlast the replay budget too, or a merely slow (but live) recovery would time
|
|
405
|
+
// out every other thread's application load minutes before the winner publishes READY. The
|
|
406
|
+
// clone in between is unbounded in the base's size, which is why the budget follows progress.
|
|
407
|
+
const deadline = claimDeadlineFor(claimState, CLAIM_TIMEOUT_MS + (0, replayLogs_ts_1.replayTimeBudgetMs)());
|
|
408
|
+
for (;;) {
|
|
409
|
+
// The deadline bounds the whole protocol, not just a single wait: an unexpected state word --
|
|
410
|
+
// a future state, a buffer another version wrote -- must fail this load, never spin forever.
|
|
411
|
+
if (Date.now() > deadline())
|
|
412
|
+
throw new Error(`Timed out claiming the branch at ${branchPath}`);
|
|
413
|
+
const previous = Atomics.compareExchange(claimState, CLAIM_STATE, UNCLAIMED, CREATING);
|
|
414
|
+
if (previous === READY)
|
|
415
|
+
break;
|
|
416
|
+
if (previous === UNCLAIMED) {
|
|
417
|
+
let branch;
|
|
418
|
+
let blobRoots = [];
|
|
419
|
+
try {
|
|
420
|
+
// Adopt rather than recreate: the branch outlives the process that first made it, so on
|
|
421
|
+
// every restart after the first the directory is already here. What it is, though, is a
|
|
422
|
+
// question -- the blob roots are published separately from the directory, so existence
|
|
423
|
+
// alone proves nothing and the marker is what has to be read.
|
|
424
|
+
await finishInterruptedRemoval(branchPath, storeName, { identityHeld: true });
|
|
425
|
+
const existing = readBranchState(branchPath, storeName);
|
|
426
|
+
if (existing.state === 'damaged' || existing.state === 'unmarked') {
|
|
427
|
+
const problem = existing.state === 'damaged' ? existing.problem : existing.why;
|
|
428
|
+
throw new Error(`Branch database at ${branchPath} cannot be trusted: ${problem}. Refusing to serve or rebuild ` +
|
|
429
|
+
`it, because rebuilding re-forks from the base and would discard anything written to this ` +
|
|
430
|
+
`branch. If this branch's data is not needed, delete the directory (and its blob roots) to ` +
|
|
431
|
+
`have it recreated from the base on the next load.`);
|
|
432
|
+
}
|
|
433
|
+
if (existing.state === 'complete') {
|
|
434
|
+
blobRoots = existing.blobRoots;
|
|
435
|
+
warnAboutUnusedBlobRoots(branchPath, storeName, blobRoots);
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
// Absent, or leftovers that are not a store: nothing here can be lost.
|
|
439
|
+
await (0, promises_1.rm)(branchPath, { recursive: true, force: true });
|
|
440
|
+
blobRoots = await materializeBranch(baseName, branchPath, storeName, {
|
|
441
|
+
progress: () => reportClaimProgress(claimState),
|
|
442
|
+
strandedRoots: () => (blobRootsStranded = true),
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
(0, databases_ts_1.releaseBranchIdentity)(storeName);
|
|
446
|
+
branch = (0, databases_ts_1.openBranchDatabase)(branchPath, baseName, storeName, blobRoots);
|
|
447
|
+
// The branch's column families write with the WAL disabled, so writes since its last
|
|
448
|
+
// memtable flush exist only in its own transaction log — which a process that died
|
|
449
|
+
// without the exit-time flush (a crash, a Windows hard kill) always leaves behind.
|
|
450
|
+
// Replay must finish before READY, because READY is what releases the other threads to
|
|
451
|
+
// serve reads. A fresh checkpoint carries no transaction_logs (verified: the native
|
|
452
|
+
// checkpoint copies only RocksDB files), so replay after materialize is a no-op; the
|
|
453
|
+
// read-only skip mirrors the base's boot replay.
|
|
454
|
+
if (!(0, databases_ts_1.isReadOnlyMode)())
|
|
455
|
+
await (0, replayLogs_ts_1.replayLogs)(branch.rootStore, branch.tables, true);
|
|
456
|
+
Atomics.store(claimState, CLAIM_STATE, READY);
|
|
457
|
+
}
|
|
458
|
+
catch (error) {
|
|
459
|
+
// Release rather than record the failure. A claim that stays un-releasable turns one
|
|
460
|
+
// transient error -- a full disk, a rename losing a race -- into a branch that can never
|
|
461
|
+
// be created again for the life of the process, because the buffer outlives every retry.
|
|
462
|
+
// The branch is closed first — it holds the path and store identity a retry needs — and
|
|
463
|
+
// a close failure must not leave the claim wedged in CREATING for the whole deadline.
|
|
464
|
+
try {
|
|
465
|
+
branch?.close();
|
|
466
|
+
}
|
|
467
|
+
catch (closeError) {
|
|
468
|
+
harper_logger_ts_1.default.warn(`Error closing branch at ${branchPath} after a failed open`, closeError);
|
|
469
|
+
}
|
|
470
|
+
Atomics.store(claimState, CLAIM_STATE, UNCLAIMED);
|
|
471
|
+
throw error;
|
|
472
|
+
}
|
|
473
|
+
finally {
|
|
474
|
+
wakeWaiters(claimState);
|
|
475
|
+
}
|
|
476
|
+
handedOver = true;
|
|
477
|
+
return { branch, claimState, blobRoots };
|
|
478
|
+
}
|
|
479
|
+
// Someone else holds it. A wait that settles on UNCLAIMED means they failed and released, so
|
|
480
|
+
// take another turn at being the one who creates it.
|
|
481
|
+
if ((await awaitClaim(claimState, branchPath, deadline)) === READY)
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
// This thread never read the branch it is adopting, and a branch resolves blob ids through the
|
|
485
|
+
// roots its marker names, so the marker has to be read here too rather than pinned only on the
|
|
486
|
+
// thread that won the claim.
|
|
487
|
+
const published = readBranchState(branchPath, storeName);
|
|
488
|
+
if (published.state !== 'complete') {
|
|
489
|
+
throw new Error(`Branch database at ${branchPath} was published by another thread but no longer reads as ` +
|
|
490
|
+
`complete: ${published.state === 'damaged' ? published.problem : published.state === 'unmarked' ? published.why : published.state}`);
|
|
491
|
+
}
|
|
492
|
+
warnAboutUnusedBlobRoots(branchPath, storeName, published.blobRoots);
|
|
493
|
+
// Released immediately before the open, with no `await` in between, so nothing can slip into the
|
|
494
|
+
// gap -- and so `openBranchDatabase`'s check stays strict rather than being taught to ignore a
|
|
495
|
+
// reservation, which would also make it ignore a DIFFERENT branch holding the same name.
|
|
496
|
+
(0, databases_ts_1.releaseBranchIdentity)(storeName);
|
|
497
|
+
const adopted = (0, databases_ts_1.openBranchDatabase)(branchPath, baseName, storeName, published.blobRoots);
|
|
498
|
+
handedOver = true;
|
|
499
|
+
return { branch: adopted, claimState, blobRoots: published.blobRoots };
|
|
500
|
+
}
|
|
501
|
+
finally {
|
|
502
|
+
// `openBranchDatabase` takes the identity over for the life of the handle; anything short of
|
|
503
|
+
// that has to hand it back, or the application can never load again in this process.
|
|
504
|
+
if (!handedOver) {
|
|
505
|
+
if (blobRootsStranded)
|
|
506
|
+
(0, databases_ts_1.quarantineBranchIdentity)(storeName);
|
|
507
|
+
else
|
|
508
|
+
(0, databases_ts_1.releaseBranchIdentity)(storeName);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
/** Remove the now-empty `<app>` directory a removed branch leaves behind. */
|
|
513
|
+
async function pruneEmptyParents(root, branchPath) {
|
|
514
|
+
let dir = (0, node_path_1.dirname)(branchPath);
|
|
515
|
+
// Stop at the branch root itself: it is shared, and `relative` going outside it means we walked past.
|
|
516
|
+
while (dir !== root && !(0, node_path_1.relative)(root, dir).startsWith('..')) {
|
|
517
|
+
try {
|
|
518
|
+
await (0, promises_1.rmdir)(dir);
|
|
519
|
+
}
|
|
520
|
+
catch {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
dir = (0, node_path_1.dirname)(dir);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
function branchRootOf(branchPath) {
|
|
527
|
+
return (0, node_path_1.dirname)((0, node_path_1.dirname)(branchPath));
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Release this thread's handle on a branch WITHOUT touching the directory. One branch directory is
|
|
531
|
+
* shared by every worker thread that loaded the application, so a thread that gives up its handle --
|
|
532
|
+
* a failed load, most often -- must not delete storage another thread is serving queries from.
|
|
533
|
+
*/
|
|
534
|
+
async function closeBranchAt(branchPath) {
|
|
535
|
+
const pending = branchesByPath.get(branchPath);
|
|
536
|
+
branchesByPath.delete(branchPath);
|
|
537
|
+
const opened = await pending?.catch(() => null);
|
|
538
|
+
opened?.branch.close();
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Open references to this branch's store anywhere in the process (rocksdb-js's registry spans worker
|
|
542
|
+
* threads). A registry read that fails propagates and refuses the removal rather than reading as zero.
|
|
543
|
+
*/
|
|
544
|
+
function branchReferenceCount(branchPath) {
|
|
545
|
+
let resolved = branchPath;
|
|
546
|
+
try {
|
|
547
|
+
resolved = (0, node_fs_1.realpathSync)(branchPath);
|
|
548
|
+
}
|
|
549
|
+
catch (error) {
|
|
550
|
+
if (error.code === 'ENOENT')
|
|
551
|
+
return 0;
|
|
552
|
+
throw error;
|
|
553
|
+
}
|
|
554
|
+
return ((0, rocksdb_js_1.registryStatus)().find((entry) => entry.path === resolved || canonicalOrSelf(entry.path) === resolved)
|
|
555
|
+
?.refCount ?? 0);
|
|
556
|
+
}
|
|
557
|
+
function canonicalOrSelf(path) {
|
|
558
|
+
try {
|
|
559
|
+
return (0, node_fs_1.realpathSync)(path);
|
|
560
|
+
}
|
|
561
|
+
catch {
|
|
562
|
+
return path;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Destroy a branch's storage: checkpoint, blob roots and claim, the way `dropDatabase` does for a real
|
|
567
|
+
* database. Only safe as the last step of the undeploy sequence -- component dropped, workers
|
|
568
|
+
* restarted -- and it does not take that on trust: any remaining open reference to the store refuses
|
|
569
|
+
* the removal before anything is unlinked.
|
|
570
|
+
*
|
|
571
|
+
* The branch is first renamed to its `removing` sibling so a removal cut short by a crash is finished
|
|
572
|
+
* later instead of being read as damage. `opened` is the handle this thread held, if any, already closed.
|
|
573
|
+
*/
|
|
574
|
+
async function destroyBranchStorage(branchPath, opened) {
|
|
575
|
+
const held = branchReferenceCount(branchPath);
|
|
576
|
+
if (held > 0) {
|
|
577
|
+
throw new Error(`Cannot remove the branch at ${branchPath}: ${held} open reference(s) to its store remain in this ` +
|
|
578
|
+
`process. Restart the workers that loaded the application first; removing it now would unlink ` +
|
|
579
|
+
`storage out from under a live reader.`);
|
|
580
|
+
}
|
|
581
|
+
const storeName = branchStoreNameFor(branchPath);
|
|
582
|
+
const blobRoots = opened?.blobRoots ?? recordedBlobRootsAt(branchPath, storeName);
|
|
583
|
+
let holdsIdentity = true;
|
|
584
|
+
let blobRootsStranded = false;
|
|
585
|
+
if (opened) {
|
|
586
|
+
(0, databases_ts_1.retakeBranchIdentity)(storeName);
|
|
587
|
+
releaseClaim(opened.claimState);
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
try {
|
|
591
|
+
(0, databases_ts_1.reserveBranchIdentity)(storeName);
|
|
592
|
+
}
|
|
593
|
+
catch (error) {
|
|
594
|
+
holdsIdentity = false;
|
|
595
|
+
harper_logger_ts_1.default.warn?.(`Leaving the blob roots of the branch at ${branchPath} in place: its identity is in use`, error);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
branchesByPath.delete(branchPath);
|
|
599
|
+
const removing = `${branchPath}${databases_ts_1.BRANCH_REMOVING_SUFFIX}`;
|
|
600
|
+
try {
|
|
601
|
+
await (0, promises_1.rm)(removing, { recursive: true, force: true });
|
|
602
|
+
if ((0, node_fs_1.existsSync)(branchPath))
|
|
603
|
+
await (0, promises_1.rename)(branchPath, removing);
|
|
604
|
+
if (holdsIdentity && !(0, node_fs_1.existsSync)(branchPath)) {
|
|
605
|
+
if (!(await removeBlobRoots(blobRoots))) {
|
|
606
|
+
blobRootsStranded = true;
|
|
607
|
+
harper_logger_ts_1.default.warn?.(`Refusing '${storeName}' to new databases on this worker for the life of the process: the ` +
|
|
608
|
+
`branch at ${branchPath} left blob files behind and a database under that name would ` +
|
|
609
|
+
`resolve onto them`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
// only while the base exists: `claimStateFor` goes through `database()`, which recreates a dropped one
|
|
613
|
+
if (!opened && databases_ts_1.databases[(0, node_path_1.basename)(branchPath)]) {
|
|
614
|
+
try {
|
|
615
|
+
releaseClaim(claimStateFor((0, node_path_1.basename)(branchPath), branchPath));
|
|
616
|
+
}
|
|
617
|
+
catch (error) {
|
|
618
|
+
harper_logger_ts_1.default.warn?.(`Could not release the branch claim for ${branchPath}`, error);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (blobRootsStranded) {
|
|
622
|
+
throw new Error(`The branch at ${branchPath} was not fully removed: one or more of its blob roots could not be ` +
|
|
623
|
+
`deleted. Its \`removing\` tombstone was kept; retry the removal once the cause is fixed.`);
|
|
624
|
+
}
|
|
625
|
+
await (0, promises_1.rm)(removing, { recursive: true, force: true });
|
|
626
|
+
await pruneEmptyParents(branchRootOf(branchPath), branchPath);
|
|
627
|
+
}
|
|
628
|
+
finally {
|
|
629
|
+
if (holdsIdentity) {
|
|
630
|
+
if (blobRootsStranded)
|
|
631
|
+
(0, databases_ts_1.quarantineBranchIdentity)(storeName);
|
|
632
|
+
else
|
|
633
|
+
(0, databases_ts_1.releaseBranchIdentity)(storeName);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Finish a removal that was interrupted; a `removing` sibling can only mean that.
|
|
639
|
+
*
|
|
640
|
+
* `identityHeld`: the caller already reserved the store identity (the load path does). Otherwise it is
|
|
641
|
+
* reserved here, and a database that already took the name owns the root it resolves to.
|
|
642
|
+
*/
|
|
643
|
+
async function finishInterruptedRemoval(branchPath, storeName, { identityHeld }) {
|
|
644
|
+
const removing = `${branchPath}${databases_ts_1.BRANCH_REMOVING_SUFFIX}`;
|
|
645
|
+
if (!(0, node_fs_1.existsSync)(removing))
|
|
646
|
+
return;
|
|
647
|
+
harper_logger_ts_1.default.warn?.(`Finishing an interrupted removal of the branch at ${branchPath}`);
|
|
648
|
+
if (!identityHeld)
|
|
649
|
+
(0, databases_ts_1.reserveBranchIdentity)(storeName);
|
|
650
|
+
let stranded = false;
|
|
651
|
+
try {
|
|
652
|
+
// Only what the tombstone's own marker names. The marker goes with the final delete, after the
|
|
653
|
+
// roots have; a tombstone without one had its roots removed already, or lost the record of them
|
|
654
|
+
// -- and guessing from configuration would take a same-named directory on a volume this branch
|
|
655
|
+
// never wrote.
|
|
656
|
+
const published = readBranchState(removing, storeName);
|
|
657
|
+
if (published.state === 'complete' || published.state === 'damaged') {
|
|
658
|
+
const configured = (0, blob_ts_1.getBlobPathsForDatabaseName)(storeName);
|
|
659
|
+
if (!(await removeBlobRoots(published.blobRoots.filter((root) => configured.includes(root))))) {
|
|
660
|
+
stranded = true;
|
|
661
|
+
throw new Error(`Could not finish removing the branch at ${branchPath}: its blob roots could not be deleted; ` +
|
|
662
|
+
`the \`removing\` tombstone was kept`);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
else {
|
|
666
|
+
stranded = true;
|
|
667
|
+
harper_logger_ts_1.default.warn?.(`The removal tombstone of the branch at ${branchPath} no longer names its blob roots; leaving any ` +
|
|
668
|
+
`that remain in place and refusing '${storeName}' to new databases`);
|
|
669
|
+
}
|
|
670
|
+
await (0, promises_1.rm)(removing, { recursive: true, force: true });
|
|
671
|
+
}
|
|
672
|
+
finally {
|
|
673
|
+
if (stranded)
|
|
674
|
+
(0, databases_ts_1.quarantineBranchIdentity)(storeName);
|
|
675
|
+
else if (!identityHeld)
|
|
676
|
+
(0, databases_ts_1.releaseBranchIdentity)(storeName);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Hand a branch's claim back, but only from READY: a CREATING word belongs to a loader that won the
|
|
681
|
+
* claim since, and must not be clobbered into making a second waiter the creator.
|
|
682
|
+
*/
|
|
683
|
+
function releaseClaim(claimState) {
|
|
684
|
+
if (Atomics.compareExchange(claimState, CLAIM_STATE, READY, UNCLAIMED) === READY)
|
|
685
|
+
wakeWaiters(claimState);
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Release this thread's handle, then destroy the storage. Deliberate removal only.
|
|
689
|
+
*/
|
|
690
|
+
async function removeBranchAt(branchPath) {
|
|
691
|
+
const pending = branchesByPath.get(branchPath);
|
|
692
|
+
branchesByPath.delete(branchPath);
|
|
693
|
+
const opened = (await pending?.catch(() => null)) ?? null;
|
|
694
|
+
opened?.branch.close();
|
|
695
|
+
await destroyBranchStorage(branchPath, opened);
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* The roots a branch on disk recorded, falling back to configuration only for one that never
|
|
699
|
+
* published a usable marker -- an abandoned materialization, whose roots ARE the configured ones.
|
|
700
|
+
*/
|
|
701
|
+
function recordedBlobRootsAt(branchPath, storeName) {
|
|
702
|
+
const published = readBranchState(branchPath, storeName);
|
|
703
|
+
const configured = (0, blob_ts_1.getBlobPathsForDatabaseName)(storeName);
|
|
704
|
+
// A marker that reads as damaged still names what this branch published, and only a branch with no
|
|
705
|
+
// usable marker at all leaves configuration as the best answer. Nothing outside this node's
|
|
706
|
+
// configured volumes is deleted either way: a branch directory carried over from a host configured
|
|
707
|
+
// differently names paths this node never wrote.
|
|
708
|
+
return published.state === 'complete' || published.state === 'damaged'
|
|
709
|
+
? published.blobRoots.filter((root) => configured.includes(root))
|
|
710
|
+
: configured;
|
|
711
|
+
}
|
|
712
|
+
/** `<storage>/`branches`/<app>/<db>` — the same identity `branchStoreName` composed on the way in. */
|
|
713
|
+
function branchStoreNameFor(branchPath) {
|
|
714
|
+
return branchStoreName((0, node_path_1.basename)((0, node_path_1.dirname)(branchPath)), (0, node_path_1.basename)(branchPath));
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Discard every branch this process has open, storage included. Deliberate removal only: a branch is
|
|
718
|
+
* durable and is meant to survive restarts, so no shutdown path calls this.
|
|
719
|
+
*/
|
|
720
|
+
async function removeBranches() {
|
|
721
|
+
for (const branchPath of [...branchesByPath.keys()])
|
|
722
|
+
await removeBranchAt(branchPath);
|
|
723
|
+
}
|
|
724
|
+
/** Every branch directory and removal tombstone an application owns, under every storage root a database could have. */
|
|
725
|
+
function branchDirectoriesFor(appName) {
|
|
726
|
+
if (!appName || appName.includes('/') || appName.includes('\\') || appName === '.' || appName === '..') {
|
|
727
|
+
throw new Error(`Invalid application name for branch removal: ${JSON.stringify(appName)}`);
|
|
728
|
+
}
|
|
729
|
+
(0, databases_ts_1.getDatabases)();
|
|
730
|
+
const branches = [];
|
|
731
|
+
const tombstones = [];
|
|
732
|
+
const failures = [];
|
|
733
|
+
const fail = (what, error) => failures.push(`${what} (${error.message})`);
|
|
734
|
+
const baseNames = new Set([
|
|
735
|
+
...Object.keys(databases_ts_1.databases),
|
|
736
|
+
hdbTerms_ts_1.DEFAULT_DATABASE_NAME,
|
|
737
|
+
// a dropped base with a dedicated path has left `databases`, not its branches
|
|
738
|
+
...Object.keys(env.get(hdbTerms_ts_1.CONFIG_PARAMS.DATABASES) ?? {}),
|
|
739
|
+
]);
|
|
740
|
+
const branchRoots = new Set();
|
|
741
|
+
for (const baseName of baseNames) {
|
|
742
|
+
try {
|
|
743
|
+
branchRoots.add((0, node_path_1.join)((0, databases_ts_1.resolveDatabaseStorageRoot)(baseName), databases_ts_1.BRANCH_ROOT_DIR));
|
|
744
|
+
}
|
|
745
|
+
catch (error) {
|
|
746
|
+
fail(`the storage root of database '${baseName}'`, error);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
for (const root of branchRoots) {
|
|
750
|
+
const appDirectory = (0, node_path_1.join)(root, appName);
|
|
751
|
+
let entries;
|
|
752
|
+
try {
|
|
753
|
+
entries = (0, node_fs_1.readdirSync)(appDirectory, { withFileTypes: true });
|
|
754
|
+
}
|
|
755
|
+
catch (error) {
|
|
756
|
+
if (error.code !== 'ENOENT')
|
|
757
|
+
fail(appDirectory, error);
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
for (const entry of entries) {
|
|
761
|
+
if (!entry.isDirectory())
|
|
762
|
+
continue;
|
|
763
|
+
const entryPath = (0, node_path_1.join)(appDirectory, entry.name);
|
|
764
|
+
if (entry.name.endsWith(databases_ts_1.BRANCH_REMOVING_SUFFIX))
|
|
765
|
+
tombstones.push(entryPath.slice(0, -databases_ts_1.BRANCH_REMOVING_SUFFIX.length));
|
|
766
|
+
else if (entry.name.includes('`'))
|
|
767
|
+
fail(entryPath, new Error('not a branch directory and not a known control path'));
|
|
768
|
+
else
|
|
769
|
+
branches.push(entryPath);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return { branches, tombstones, failures };
|
|
773
|
+
}
|
|
774
|
+
/** Whether undeploying this application would leave branched database storage behind. */
|
|
775
|
+
function applicationHasBranchStorage(appName) {
|
|
776
|
+
const { branches, tombstones, failures } = branchDirectoriesFor(appName);
|
|
777
|
+
return branches.length > 0 || tombstones.length > 0 || failures.length > 0;
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Discard every branch an application owns: what undeploying it means for its private data. A branch is
|
|
781
|
+
* durable, so if this does not happen nothing else ever will, and a redeploy under the same name would
|
|
782
|
+
* adopt the previous tenant's rows.
|
|
783
|
+
*
|
|
784
|
+
* Runs on the thread that saw the worker restart through, after it completed. Own handles are closed
|
|
785
|
+
* first; any reference held elsewhere refuses. Every failure is collected and thrown: an undeploy must
|
|
786
|
+
* not answer "dropped" with private data still on disk.
|
|
787
|
+
*/
|
|
788
|
+
async function removeBranchesForApplication(appName) {
|
|
789
|
+
const { branches, tombstones, failures } = branchDirectoriesFor(appName);
|
|
790
|
+
const fail = (what, error) => failures.push(`${what} (${error.message})`);
|
|
791
|
+
for (const branchPath of tombstones) {
|
|
792
|
+
try {
|
|
793
|
+
await finishInterruptedRemoval(branchPath, branchStoreNameFor(branchPath), { identityHeld: false });
|
|
794
|
+
}
|
|
795
|
+
catch (error) {
|
|
796
|
+
fail(`${branchPath}${databases_ts_1.BRANCH_REMOVING_SUFFIX}`, error);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
for (const branchPath of branches) {
|
|
800
|
+
try {
|
|
801
|
+
await removeBranchAt(branchPath);
|
|
802
|
+
}
|
|
803
|
+
catch (error) {
|
|
804
|
+
fail(branchPath, error);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
if (failures.length) {
|
|
808
|
+
throw new Error(`Could not remove branch storage for application '${appName}': ${failures.join('; ')}`);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Create every branch an application declared, or fail its load. These checks need the live
|
|
813
|
+
* environment, so they cannot live in the static config validator: whether the database exists,
|
|
814
|
+
* which storage engine is in effect, and whether the application's loader mode can carry a scoped
|
|
815
|
+
* `databases` binding at all.
|
|
816
|
+
*
|
|
817
|
+
* Failing is always right here. Falling back to the base would hand the application the shared
|
|
818
|
+
* database it explicitly asked not to have, and nothing downstream could tell the difference.
|
|
819
|
+
*/
|
|
820
|
+
async function prepareBranches(appName, branchedDatabases, loaderMode) {
|
|
821
|
+
const branches = new Map();
|
|
822
|
+
if (branchedDatabases === undefined)
|
|
823
|
+
return branches;
|
|
824
|
+
// The scoped `databases` binding is delivered through the module loader. Under `native` the
|
|
825
|
+
// loader hands back the process-wide exports, so a branch would be created and then never
|
|
826
|
+
// reached — the application would silently write the base.
|
|
827
|
+
if (loaderMode === 'native') {
|
|
828
|
+
throw new Error(`Application '${appName}' declares branchedDatabases but runs under the 'native' module loader, ` +
|
|
829
|
+
`which cannot carry a scoped databases binding; use the default loader or remove branchedDatabases`);
|
|
830
|
+
}
|
|
831
|
+
// An explicitly empty list branches nothing, so it needs no engine capability.
|
|
832
|
+
if (branchedDatabases !== true && !branchedDatabases.length)
|
|
833
|
+
return branches;
|
|
834
|
+
if ((process.env.HARPER_STORAGE_ENGINE || env.get(hdbTerms_ts_1.CONFIG_PARAMS.STORAGE_ENGINE)) === 'lmdb') {
|
|
835
|
+
throw new Error(`Application '${appName}' declares branchedDatabases, which requires the RocksDB storage engine`);
|
|
836
|
+
}
|
|
837
|
+
(0, databases_ts_1.getDatabases)();
|
|
838
|
+
if (branchedDatabases === true) {
|
|
839
|
+
// A snapshot, not a subscription: every database that exists at THIS load. One created afterward
|
|
840
|
+
// is not retroactively branched, and this application's own declarations into an unbranched
|
|
841
|
+
// database land in the base. `system` is excluded the same way an explicit declaration of it is
|
|
842
|
+
// refused (assertBranchedDatabases): it carries the instance's catalog, users and jobs.
|
|
843
|
+
branchedDatabases = Object.keys(databases_ts_1.databases).filter((name) => name !== 'system');
|
|
844
|
+
}
|
|
845
|
+
if (!branchedDatabases.length)
|
|
846
|
+
return branches;
|
|
847
|
+
for (const baseName of branchedDatabases) {
|
|
848
|
+
if (!databases_ts_1.databases[baseName]) {
|
|
849
|
+
throw new Error(`Application '${appName}' declares a branch of database '${baseName}', which does not exist`);
|
|
850
|
+
}
|
|
851
|
+
// Up front, not inside the open: two individually legal names can compose a store identity past
|
|
852
|
+
// the 250-character limit, and `openBranchDatabase` only checks it after the claim is taken and
|
|
853
|
+
// a potentially full-copy checkpoint is already on disk.
|
|
854
|
+
const storeName = branchStoreName(appName, baseName);
|
|
855
|
+
if (storeName.length > MAX_NAME_LENGTH) {
|
|
856
|
+
throw new Error(`Application '${appName}' cannot branch '${baseName}': the branch store identity would be ` +
|
|
857
|
+
`${storeName.length} characters, over the ${MAX_NAME_LENGTH} allowed`);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
// Only the branches this call actually opened are this call's to give up. A branch handle is
|
|
861
|
+
// cached per path for the whole thread, so a later load of the same application -- a component
|
|
862
|
+
// reload, or a second component with the same appName -- must not close the branch the load
|
|
863
|
+
// already running is serving from.
|
|
864
|
+
const opened = [];
|
|
865
|
+
try {
|
|
866
|
+
for (const baseName of branchedDatabases) {
|
|
867
|
+
const branchPath = (0, databases_ts_1.resolveBranchPath)(baseName, appName);
|
|
868
|
+
const isNew = !branchesByPath.has(branchPath);
|
|
869
|
+
branches.set(baseName, await getOrCreateBranch(baseName, appName));
|
|
870
|
+
if (isNew)
|
|
871
|
+
opened.push(branchPath);
|
|
872
|
+
}
|
|
873
|
+
// Only now: a relationship whose target this application also branched has to resolve to that
|
|
874
|
+
// branch, and the whole set has to exist before any of them can resolve that way. The set stays
|
|
875
|
+
// on each branch for the reloads its own schema declarations trigger later.
|
|
876
|
+
for (const branch of branches.values()) {
|
|
877
|
+
branch.relatedBranches = branches;
|
|
878
|
+
(0, databases_ts_1.hydrateBranchRelationships)(branch, branches);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
catch (error) {
|
|
882
|
+
// A partially branched application is worse than one that failed to load: some of its names
|
|
883
|
+
// would resolve to a branch and the rest to the base. Only this application's handles go, and
|
|
884
|
+
// only handles: another worker thread may have loaded the same application successfully and be
|
|
885
|
+
// serving queries out of the very directories this thread is giving up.
|
|
886
|
+
for (const branchPath of opened)
|
|
887
|
+
await closeBranchAt(branchPath).catch(() => { });
|
|
888
|
+
throw error;
|
|
889
|
+
}
|
|
890
|
+
return branches;
|
|
891
|
+
}
|
|
892
|
+
//# sourceMappingURL=branchDatabase.js.map
|