@harperfast/harper-pro 5.0.25 → 5.1.0-beta.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/analytics/profile.ts +4 -0
- package/core/AGENTS.md +92 -6
- package/core/DESIGN.md +24 -0
- package/core/README.md +11 -10
- package/core/agent/agent.ts +203 -0
- package/core/agent/loop.ts +205 -0
- package/core/agent/operations.ts +148 -0
- package/core/agent/session.ts +187 -0
- package/core/agent/tools/fsTools.ts +276 -0
- package/core/agent/tools/httpFetchTool.ts +112 -0
- package/core/agent/tools/scheduleTool.ts +68 -0
- package/core/agent/toolset.ts +43 -0
- package/core/agent/types.ts +85 -0
- package/core/benchmarks/hnsw-search.js +157 -0
- package/core/benchmarks/ycsb/README.md +144 -0
- package/core/benchmarks/ycsb/analyze-profile.mts +120 -0
- package/core/benchmarks/ycsb/app/config.yaml +3 -0
- package/core/benchmarks/ycsb/app/schema.graphql +17 -0
- package/core/benchmarks/ycsb/harness.mts +328 -0
- package/core/benchmarks/ycsb/restClient.mts +120 -0
- package/core/benchmarks/ycsb/run-single-node.mts +97 -0
- package/core/benchmarks/ycsb/to-benchmark-json.mts +58 -0
- package/core/benchmarks/ycsb/workload.mts +499 -0
- package/core/benchmarks/ycsb/workload.test.mts +174 -0
- package/core/bin/{BinObjects.js → BinObjects.ts} +4 -5
- package/core/bin/cliCredentials.ts +133 -0
- package/core/bin/cliOperations.ts +339 -0
- package/core/bin/copyDb.ts +10 -10
- package/core/bin/deployRenderer.ts +196 -0
- package/core/bin/{harper.js → harper.ts} +48 -24
- package/core/bin/{install.js → install.ts} +3 -3
- package/core/bin/lite.ts +2 -0
- package/core/bin/login.ts +168 -0
- package/core/bin/logout.ts +11 -0
- package/core/bin/mcp/client.ts +407 -0
- package/core/bin/mcp/doctor.ts +189 -0
- package/core/bin/mcp/index.ts +80 -0
- package/core/bin/mcp/options.ts +122 -0
- package/core/bin/mcp/printConfig.ts +89 -0
- package/core/bin/multipartBuilder.ts +74 -0
- package/core/bin/{restart.js → restart.ts} +31 -32
- package/core/bin/{run.js → run.ts} +57 -46
- package/core/bin/sseConsumer.ts +126 -0
- package/core/bin/{status.js → status.ts} +10 -10
- package/core/bin/stop.ts +21 -0
- package/core/bin/upgrade.js +6 -6
- package/core/components/Application.ts +144 -18
- package/core/components/ApplicationScope.ts +2 -2
- package/core/components/ComponentV1.ts +2 -2
- package/core/components/EntryHandler.ts +159 -9
- package/core/components/OptionsWatcher.ts +75 -11
- package/core/components/Scope.ts +125 -15
- package/core/components/anthropic/index.ts +547 -0
- package/core/components/bedrock/index.ts +823 -0
- package/core/components/componentLoader.ts +63 -32
- package/core/components/deployLifecycle.ts +161 -0
- package/core/components/deploymentOperations.ts +173 -0
- package/core/components/deploymentRecorder.ts +402 -0
- package/core/components/deriveURLPath.ts +4 -4
- package/core/components/mcp/adapters/fastify.ts +87 -0
- package/core/components/mcp/adapters/harperHttp.ts +103 -0
- package/core/components/mcp/audit.ts +75 -0
- package/core/components/mcp/index.ts +134 -0
- package/core/components/mcp/jsonrpc.ts +134 -0
- package/core/components/mcp/lifecycle.ts +105 -0
- package/core/components/mcp/listChanged.ts +270 -0
- package/core/components/mcp/rateLimit.ts +217 -0
- package/core/components/mcp/resources.ts +593 -0
- package/core/components/mcp/session.ts +151 -0
- package/core/components/mcp/sessionRegistry.ts +140 -0
- package/core/components/mcp/toolRegistry.ts +292 -0
- package/core/components/mcp/tools/application.ts +603 -0
- package/core/components/mcp/tools/operations.ts +283 -0
- package/core/components/mcp/tools/schemas/derive.ts +256 -0
- package/core/components/mcp/tools/schemas/operations.ts +245 -0
- package/core/components/mcp/transport.ts +517 -0
- package/core/components/ollama/index.ts +316 -0
- package/core/components/openai/index.ts +563 -0
- package/core/components/operations.js +211 -60
- package/core/components/operationsValidation.js +3 -3
- package/core/components/packageComponent.ts +97 -29
- package/core/components/requestRestart.ts +17 -2
- package/core/components/status/crossThread.ts +14 -5
- package/core/components/status/errors.ts +1 -1
- package/core/config/RootConfigWatcher.ts +56 -2
- package/core/config/configUtils.js +29 -8
- package/core/config/harperConfigEnvVars.ts +1 -1
- package/core/dataLayer/{CreateAttributeObject.js → CreateAttributeObject.ts} +4 -3
- package/core/dataLayer/{CreateTableObject.js → CreateTableObject.ts} +2 -1
- package/core/dataLayer/{DataLayerObjects.js → DataLayerObjects.ts} +17 -9
- package/core/dataLayer/{DeleteBeforeObject.js → DeleteBeforeObject.ts} +2 -1
- package/core/dataLayer/{DeleteObject.js → DeleteObject.ts} +3 -2
- package/core/dataLayer/{DropAttributeObject.js → DropAttributeObject.ts} +2 -1
- package/core/dataLayer/{GetBackupObject.js → GetBackupObject.ts} +3 -2
- package/core/dataLayer/{InsertObject.js → InsertObject.ts} +3 -2
- package/core/dataLayer/{ReadAuditLogObject.js → ReadAuditLogObject.ts} +3 -2
- package/core/dataLayer/{SQLSearch.js → SQLSearch.ts} +97 -43
- package/core/dataLayer/{SearchByConditionsObject.js → SearchByConditionsObject.ts} +5 -6
- package/core/dataLayer/{SearchByHashObject.js → SearchByHashObject.ts} +2 -1
- package/core/dataLayer/{SearchObject.js → SearchObject.ts} +2 -1
- package/core/dataLayer/{SqlSearchObject.js → SqlSearchObject.ts} +2 -1
- package/core/dataLayer/{UpdateObject.js → UpdateObject.ts} +3 -2
- package/core/dataLayer/{UpsertObject.js → UpsertObject.ts} +3 -2
- package/core/dataLayer/{bulkLoad.js → bulkLoad.ts} +40 -49
- package/core/dataLayer/{delete.js → delete.ts} +21 -26
- package/core/dataLayer/{export.js → export.ts} +22 -26
- package/core/dataLayer/{getBackup.js → getBackup.ts} +7 -9
- package/core/dataLayer/harperBridge/BridgeMethods.ts +102 -0
- package/core/dataLayer/harperBridge/ResourceBridge.ts +27 -26
- package/core/dataLayer/harperBridge/TableSizeObject.ts +1 -0
- package/core/dataLayer/harperBridge/bridgeUtility/insertUpdateValidate.js +4 -4
- package/core/dataLayer/harperBridge/{harperBridge.js → harperBridge.ts} +3 -3
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateAttribute.js +8 -6
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateRecords.js +4 -4
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateSchema.js +1 -1
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateTable.js +6 -4
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDeleteAuditLogsBefore.js +5 -4
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDeleteRecords.js +4 -4
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropAttribute.js +6 -5
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropSchema.js +5 -4
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropTable.js +5 -5
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbFlush.js +1 -1
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetBackup.js +3 -3
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetDataByHash.js +1 -1
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetDataByValue.js +3 -2
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbReadAuditLog.js +5 -5
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByConditions.js +10 -8
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByHash.js +1 -1
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByValue.js +4 -3
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbUpdateRecords.js +3 -3
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbUpsertRecords.js +6 -5
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/LMDBCreateAttributeObject.js +2 -1
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializeHashSearch.js +3 -2
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +2 -2
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCheckForNewAttributes.js +5 -4
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCreateTransactionsAuditEnvironment.js +6 -3
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbGetTableSize.ts +1 -1
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbProcessRows.js +4 -4
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbSearch.js +5 -5
- package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbWriteTransaction.js +4 -4
- package/core/dataLayer/{hdbInfoController.js → hdbInfoController.ts} +20 -26
- package/core/dataLayer/{insert.js → insert.ts} +24 -24
- package/core/dataLayer/{readAuditLog.js → readAuditLog.ts} +8 -10
- package/core/dataLayer/{schema.js → schema.ts} +32 -44
- package/core/dataLayer/{schemaDescribe.js → schemaDescribe.ts} +23 -26
- package/core/dataLayer/{search.js → search.ts} +9 -15
- package/core/dataLayer/{transaction.js → transaction.ts} +2 -5
- package/core/dataLayer/{update.js → update.ts} +19 -22
- package/core/index.ts +5 -0
- package/core/json/systemSchema.json +65 -0
- package/core/launchServiceScripts/utility/checkNodeVersion.js +2 -0
- package/core/package-lock.json +9051 -4318
- package/core/resources/DESIGN.md +98 -0
- package/core/resources/DatabaseTransaction.ts +58 -30
- package/core/resources/ErrorResource.ts +2 -1
- package/core/resources/LMDBTransaction.ts +38 -29
- package/core/resources/RecordEncoder.ts +12 -4
- package/core/resources/RequestTarget.ts +2 -0
- package/core/resources/Resource.ts +89 -56
- package/core/resources/ResourceInterface.ts +44 -21
- package/core/resources/Resources.ts +8 -8
- package/core/resources/RocksIndexStore.ts +3 -0
- package/core/resources/RocksTransactionLogStore.ts +47 -28
- package/core/resources/Table.ts +386 -214
- package/core/resources/analytics/metadata.ts +1 -0
- package/core/resources/analytics/read.ts +24 -6
- package/core/resources/analytics/write.ts +240 -17
- package/core/resources/auditStore.ts +28 -19
- package/core/resources/blob.ts +53 -53
- package/core/resources/dataLoader.ts +4 -4
- package/core/resources/databases.ts +190 -71
- package/core/resources/graphql.ts +227 -164
- package/core/resources/indexes/HierarchicalNavigableSmallWorld.ts +294 -65
- package/core/resources/indexes/vector.ts +17 -0
- package/core/resources/loadEnv.ts +21 -17
- package/core/resources/login.ts +5 -3
- package/core/resources/models/Models.ts +304 -0
- package/core/resources/models/TestBackend.ts +83 -0
- package/core/resources/models/agentLoop.ts +895 -0
- package/core/resources/models/analyticsTable.ts +199 -0
- package/core/resources/models/backendHelpers.ts +116 -0
- package/core/resources/models/backendRegistry.ts +66 -0
- package/core/resources/models/bootstrap.ts +135 -0
- package/core/resources/models/embedHook.ts +138 -0
- package/core/resources/models/types.ts +296 -0
- package/core/resources/replayLogs.ts +15 -7
- package/core/resources/roles.ts +62 -67
- package/core/resources/search.ts +355 -135
- package/core/resources/tracked.ts +8 -8
- package/core/resources/transaction.ts +8 -8
- package/core/resources/transactionBroadcast.ts +3 -3
- package/core/security/auth.ts +35 -26
- package/core/security/certificateVerification/crlVerification.ts +11 -4
- package/core/security/{cryptoHash.js → cryptoHash.ts} +3 -8
- package/core/security/data_objects/{PermissionAttributeResponseObject.js → PermissionAttributeResponseObject.ts} +4 -4
- package/core/security/data_objects/{PermissionResponseObject.js → PermissionResponseObject.ts} +12 -11
- package/core/security/data_objects/{PermissionTableResponseObject.js → PermissionTableResponseObject.ts} +6 -4
- package/core/security/{fastifyAuth.js → fastifyAuth.ts} +93 -20
- package/core/security/impersonation.ts +3 -3
- package/core/security/jsLoader.ts +22 -8
- package/core/security/{keys.js → keys.ts} +113 -121
- package/core/security/permissionsTranslator.js +2 -2
- package/core/security/{role.js → role.ts} +26 -33
- package/core/security/tokenAuthentication.ts +34 -7
- package/core/security/user.ts +26 -22
- package/core/server/DESIGN.md +139 -0
- package/core/server/DurableSubscriptionsSession.ts +67 -50
- package/core/server/REST.ts +120 -107
- package/core/server/Server.ts +31 -12
- package/core/server/fastifyRoutes/helpers/getCORSOptions.js +1 -1
- package/core/server/fastifyRoutes/helpers/getHeaderTimeoutConfig.js +1 -1
- package/core/server/fastifyRoutes/helpers/getServerOptions.js +1 -1
- package/core/server/fastifyRoutes.ts +37 -33
- package/core/server/graphqlQuerying.ts +6 -5
- package/core/server/http.ts +517 -26
- package/core/server/itc/serverHandlers.js +75 -14
- package/core/server/jobs/{JobObject.js → JobObject.ts} +13 -6
- package/core/server/jobs/{jobProcess.js → jobProcess.ts} +20 -16
- package/core/server/jobs/{jobRunner.js → jobRunner.ts} +20 -21
- package/core/server/jobs/{jobs.js → jobs.ts} +41 -44
- package/core/server/loadRootComponents.js +1 -1
- package/core/server/middlewareChain.ts +270 -0
- package/core/server/mqtt.ts +35 -26
- package/core/server/nodeName.ts +2 -1
- package/core/server/operationsServer.ts +85 -10
- package/core/server/serverHelpers/Headers.ts +10 -8
- package/core/server/serverHelpers/JSONStream.ts +15 -5
- package/core/server/serverHelpers/Request.ts +370 -13
- package/core/server/serverHelpers/contentTypes.ts +42 -11
- package/core/server/serverHelpers/multipartParser.ts +152 -0
- package/core/server/serverHelpers/progressEmitter.ts +110 -0
- package/core/server/serverHelpers/serverHandlers.js +43 -7
- package/core/server/serverHelpers/serverUtilities.ts +40 -30
- package/core/server/static.ts +9 -6
- package/core/server/status/index.ts +2 -2
- package/core/server/storageReclamation.ts +39 -2
- package/core/server/threads/itc.js +7 -4
- package/core/server/threads/manageThreads.js +100 -26
- package/core/server/threads/socketRouter.ts +12 -275
- package/core/server/threads/threadServer.js +345 -105
- package/core/server/threads/workerProcessGuard.ts +93 -0
- package/core/server/throttle.ts +18 -0
- package/core/sqlTranslator/{SelectValidator.js → SelectValidator.ts} +41 -49
- package/core/sqlTranslator/{alasqlFunctionImporter.js → alasqlFunctionImporter.ts} +5 -5
- package/core/sqlTranslator/{deleteTranslator.js → deleteTranslator.ts} +13 -18
- package/core/sqlTranslator/{index.js → index.ts} +30 -33
- package/core/sqlTranslator/{sql_statement_bucket.js → sql_statement_bucket.ts} +49 -20
- package/core/static/README.md +10 -9
- package/core/system/000004.log +0 -0
- package/core/system/CURRENT +1 -0
- package/core/system/IDENTITY +1 -0
- package/core/system/LOCK +0 -0
- package/core/system/LOG +1351 -0
- package/core/system/MANIFEST-000005 +0 -0
- package/core/system/OPTIONS-000013 +607 -0
- package/core/system/OPTIONS-000015 +734 -0
- package/core/upgrade/{UpgradeObjects.js → UpgradeObjects.ts} +3 -6
- package/core/upgrade/directives/5-2-0.ts +49 -0
- package/core/upgrade/directives/{directivesController.js → directivesController.ts} +16 -16
- package/core/upgrade/{directivesManager.js → directivesManager.ts} +7 -11
- package/core/upgrade/{upgradePrompt.js → upgradePrompt.ts} +8 -14
- package/core/upgrade/{upgradeUtilities.js → upgradeUtilities.ts} +3 -7
- package/core/utility/{OperationFunctionCaller.js → OperationFunctionCaller.ts} +7 -7
- package/core/utility/{assignCmdEnvVariables.js → assignCmdEnvVariables.ts} +6 -8
- package/core/utility/{common_utils.js → common_utils.ts} +113 -139
- package/core/utility/environment/{environmentManager.js → environmentManager.ts} +34 -33
- package/core/utility/environment/systemInformation.ts +18 -4
- package/core/utility/errors/{commonErrors.js → commonErrors.ts} +9 -9
- package/core/utility/errors/{hdbError.js → hdbError.ts} +39 -45
- package/core/utility/expandEnvVar.ts +110 -0
- package/core/utility/functions/geo.js +2 -2
- package/core/utility/functions/sql/alaSQLExtension.js +1 -1
- package/core/utility/globalSchema.ts +30 -0
- package/core/utility/hdbTerms.ts +56 -0
- package/core/utility/install/checkJWTTokensExist.js +1 -1
- package/core/utility/install/{installer.js → installer.ts} +58 -59
- package/core/utility/installation.ts +2 -2
- package/core/utility/lmdb/{DBIDefinition.js → DBIDefinition.ts} +4 -1
- package/core/utility/lmdb/{DeleteRecordsResponseObject.js → DeleteRecordsResponseObject.ts} +2 -1
- package/core/utility/lmdb/{InsertRecordsResponseObject.js → InsertRecordsResponseObject.ts} +2 -1
- package/core/utility/lmdb/OpenDBIObject.ts +43 -0
- package/core/utility/lmdb/{OpenEnvironmentObject.js → OpenEnvironmentObject.ts} +19 -6
- package/core/utility/lmdb/{UpdateRecordsResponseObject.js → UpdateRecordsResponseObject.ts} +2 -1
- package/core/utility/lmdb/{UpsertRecordsResponseObject.js → UpsertRecordsResponseObject.ts} +2 -1
- package/core/utility/lmdb/{cleanLMDBMap.js → cleanLMDBMap.ts} +5 -5
- package/core/utility/lmdb/{commonUtility.js → commonUtility.ts} +13 -21
- package/core/utility/lmdb/{deleteUtility.js → deleteUtility.ts} +8 -12
- package/core/utility/lmdb/{environmentUtility.js → environmentUtility.ts} +43 -52
- package/core/utility/lmdb/{searchCursorFunctions.js → searchCursorFunctions.ts} +12 -26
- package/core/utility/lmdb/{searchUtility.js → searchUtility.ts} +75 -64
- package/core/utility/lmdb/{terms.js → terms.ts} +10 -23
- package/core/utility/lmdb/{writeUtility.js → writeUtility.ts} +37 -22
- package/core/utility/logging/{harper_logger.js → harper_logger.ts} +136 -89
- package/core/utility/logging/{logRotator.js → logRotator.ts} +13 -13
- package/core/utility/logging/logger.ts +1 -1
- package/core/utility/logging/{readLog.js → readLog.ts} +19 -19
- package/core/utility/logging/{transactionLog.js → transactionLog.ts} +10 -14
- package/core/utility/{mount_hdb.js → mount_hdb.ts} +15 -16
- package/core/utility/{npmUtilities.js → npmUtilities.ts} +14 -17
- package/core/utility/{operation_authorization.js → operation_authorization.ts} +173 -124
- package/core/utility/packageUtils.js +7 -16
- package/core/utility/password.ts +1 -1
- package/core/utility/processManagement/processManagement.js +2 -2
- package/core/utility/processManagement/servicesConfig.js +1 -1
- package/core/utility/{signalling.js → signalling.ts} +6 -11
- package/core/utility/watcherFallback.ts +74 -0
- package/core/validation/analyticsValidator.ts +43 -0
- package/core/validation/{bulkDeleteValidator.js → bulkDeleteValidator.ts} +5 -5
- package/core/validation/{check_permissions.js → check_permissions.ts} +3 -3
- package/core/validation/{common_validators.js → common_validators.ts} +12 -24
- package/core/validation/{configValidator.js → configValidator.ts} +114 -18
- package/core/validation/{deleteValidator.js → deleteValidator.ts} +5 -5
- package/core/validation/{fileLoadValidator.js → fileLoadValidator.ts} +12 -19
- package/core/validation/{insertValidator.js → insertValidator.ts} +5 -5
- package/core/validation/{installValidator.js → installValidator.ts} +8 -8
- package/core/validation/{readLogValidator.js → readLogValidator.ts} +10 -10
- package/core/validation/{role_validation.js → role_validation.ts} +26 -32
- package/core/validation/{schemaMetadataValidator.js → schemaMetadataValidator.ts} +5 -11
- package/core/validation/{searchValidator.js → searchValidator.ts} +12 -11
- package/core/validation/statusValidator.ts +1 -1
- package/core/validation/{transactionLogValidator.js → transactionLogValidator.ts} +4 -9
- package/core/validation/{user_validation.js → user_validation.ts} +4 -10
- package/core/validation/{validationWrapper.js → validationWrapper.ts} +3 -9
- package/dist/analytics/profile.js +4 -0
- package/dist/analytics/profile.js.map +1 -1
- package/dist/cloneNode/cloneNode.js +224 -12
- package/dist/cloneNode/cloneNode.js.map +1 -1
- package/dist/core/agent/agent.js +175 -0
- package/dist/core/agent/agent.js.map +1 -0
- package/dist/core/agent/loop.js +176 -0
- package/dist/core/agent/loop.js.map +1 -0
- package/dist/core/agent/operations.js +137 -0
- package/dist/core/agent/operations.js.map +1 -0
- package/dist/core/agent/session.js +182 -0
- package/dist/core/agent/session.js.map +1 -0
- package/dist/core/agent/tools/fsTools.js +286 -0
- package/dist/core/agent/tools/fsTools.js.map +1 -0
- package/dist/core/agent/tools/httpFetchTool.js +116 -0
- package/dist/core/agent/tools/httpFetchTool.js.map +1 -0
- package/dist/core/agent/tools/scheduleTool.js +54 -0
- package/dist/core/agent/tools/scheduleTool.js.map +1 -0
- package/dist/core/agent/toolset.js +33 -0
- package/dist/core/agent/toolset.js.map +1 -0
- package/dist/core/agent/types.js +10 -0
- package/dist/core/agent/types.js.map +1 -0
- package/dist/core/bin/BinObjects.js +6 -3
- package/dist/core/bin/BinObjects.js.map +1 -1
- package/dist/core/bin/cliCredentials.js +130 -0
- package/dist/core/bin/cliCredentials.js.map +1 -0
- package/dist/core/bin/cliOperations.js +254 -40
- package/dist/core/bin/cliOperations.js.map +1 -1
- package/dist/core/bin/copyDb.js +16 -16
- package/dist/core/bin/copyDb.js.map +1 -1
- package/dist/core/bin/deployRenderer.js +185 -0
- package/dist/core/bin/deployRenderer.js.map +1 -0
- package/dist/core/bin/harper.js +92 -31
- package/dist/core/bin/harper.js.map +1 -1
- package/dist/core/bin/install.js +41 -4
- package/dist/core/bin/install.js.map +1 -1
- package/dist/core/bin/lite.js +3 -4
- package/dist/core/bin/lite.js.map +1 -1
- package/dist/core/bin/login.js +158 -0
- package/dist/core/bin/login.js.map +1 -0
- package/dist/core/bin/logout.js +16 -0
- package/dist/core/bin/logout.js.map +1 -0
- package/dist/core/bin/mcp/client.js +395 -0
- package/dist/core/bin/mcp/client.js.map +1 -0
- package/dist/core/bin/mcp/doctor.js +193 -0
- package/dist/core/bin/mcp/doctor.js.map +1 -0
- package/dist/core/bin/mcp/index.js +81 -0
- package/dist/core/bin/mcp/index.js.map +1 -0
- package/dist/core/bin/mcp/options.js +113 -0
- package/dist/core/bin/mcp/options.js.map +1 -0
- package/dist/core/bin/mcp/printConfig.js +85 -0
- package/dist/core/bin/mcp/printConfig.js.map +1 -0
- package/dist/core/bin/multipartBuilder.js +55 -0
- package/dist/core/bin/multipartBuilder.js.map +1 -0
- package/dist/core/bin/restart.js +85 -48
- package/dist/core/bin/restart.js.map +1 -1
- package/dist/core/bin/run.js +123 -77
- package/dist/core/bin/run.js.map +1 -1
- package/dist/core/bin/sseConsumer.js +127 -0
- package/dist/core/bin/sseConsumer.js.map +1 -0
- package/dist/core/bin/status.js +48 -11
- package/dist/core/bin/status.js.map +1 -1
- package/dist/core/bin/stop.js +44 -7
- package/dist/core/bin/stop.js.map +1 -1
- package/dist/core/bin/upgrade.js +6 -6
- package/dist/core/components/Application.js +134 -28
- package/dist/core/components/Application.js.map +1 -1
- package/dist/core/components/ApplicationScope.js +2 -2
- package/dist/core/components/ComponentV1.js +5 -5
- package/dist/core/components/ComponentV1.js.map +1 -1
- package/dist/core/components/EntryHandler.js +153 -13
- package/dist/core/components/EntryHandler.js.map +1 -1
- package/dist/core/components/OptionsWatcher.js +72 -10
- package/dist/core/components/OptionsWatcher.js.map +1 -1
- package/dist/core/components/Scope.js +105 -9
- package/dist/core/components/Scope.js.map +1 -1
- package/dist/core/components/anthropic/index.js +428 -0
- package/dist/core/components/anthropic/index.js.map +1 -0
- package/dist/core/components/bedrock/index.js +734 -0
- package/dist/core/components/bedrock/index.js.map +1 -0
- package/dist/core/components/componentLoader.js +63 -38
- package/dist/core/components/componentLoader.js.map +1 -1
- package/dist/core/components/deployLifecycle.js +156 -0
- package/dist/core/components/deployLifecycle.js.map +1 -0
- package/dist/core/components/deploymentOperations.js +185 -0
- package/dist/core/components/deploymentOperations.js.map +1 -0
- package/dist/core/components/deploymentRecorder.js +401 -0
- package/dist/core/components/deploymentRecorder.js.map +1 -0
- package/dist/core/components/deriveURLPath.js +2 -2
- package/dist/core/components/deriveURLPath.js.map +1 -1
- package/dist/core/components/mcp/adapters/fastify.js +66 -0
- package/dist/core/components/mcp/adapters/fastify.js.map +1 -0
- package/dist/core/components/mcp/adapters/harperHttp.js +78 -0
- package/dist/core/components/mcp/adapters/harperHttp.js.map +1 -0
- package/dist/core/components/mcp/audit.js +73 -0
- package/dist/core/components/mcp/audit.js.map +1 -0
- package/dist/core/components/mcp/index.js +109 -0
- package/dist/core/components/mcp/index.js.map +1 -0
- package/dist/core/components/mcp/jsonrpc.js +93 -0
- package/dist/core/components/mcp/jsonrpc.js.map +1 -0
- package/dist/core/components/mcp/lifecycle.js +79 -0
- package/dist/core/components/mcp/lifecycle.js.map +1 -0
- package/dist/core/components/mcp/listChanged.js +257 -0
- package/dist/core/components/mcp/listChanged.js.map +1 -0
- package/dist/core/components/mcp/rateLimit.js +226 -0
- package/dist/core/components/mcp/rateLimit.js.map +1 -0
- package/dist/core/components/mcp/resources.js +515 -0
- package/dist/core/components/mcp/resources.js.map +1 -0
- package/dist/core/components/mcp/session.js +170 -0
- package/dist/core/components/mcp/session.js.map +1 -0
- package/dist/core/components/mcp/sessionRegistry.js +124 -0
- package/dist/core/components/mcp/sessionRegistry.js.map +1 -0
- package/dist/core/components/mcp/toolRegistry.js +176 -0
- package/dist/core/components/mcp/toolRegistry.js.map +1 -0
- package/dist/core/components/mcp/tools/application.js +549 -0
- package/dist/core/components/mcp/tools/application.js.map +1 -0
- package/dist/core/components/mcp/tools/operations.js +303 -0
- package/dist/core/components/mcp/tools/operations.js.map +1 -0
- package/dist/core/components/mcp/tools/schemas/derive.js +216 -0
- package/dist/core/components/mcp/tools/schemas/derive.js.map +1 -0
- package/dist/core/components/mcp/tools/schemas/operations.js +243 -0
- package/dist/core/components/mcp/tools/schemas/operations.js.map +1 -0
- package/dist/core/components/mcp/transport.js +467 -0
- package/dist/core/components/mcp/transport.js.map +1 -0
- package/dist/core/components/ollama/index.js +239 -0
- package/dist/core/components/ollama/index.js.map +1 -0
- package/dist/core/components/openai/index.js +475 -0
- package/dist/core/components/openai/index.js.map +1 -0
- package/dist/core/components/operations.js +198 -52
- package/dist/core/components/operations.js.map +1 -1
- package/dist/core/components/operationsValidation.js +3 -3
- package/dist/core/components/packageComponent.js +87 -26
- package/dist/core/components/packageComponent.js.map +1 -1
- package/dist/core/components/requestRestart.js +12 -1
- package/dist/core/components/requestRestart.js.map +1 -1
- package/dist/core/components/status/crossThread.js +12 -5
- package/dist/core/components/status/crossThread.js.map +1 -1
- package/dist/core/components/status/errors.js +7 -7
- package/dist/core/config/RootConfigWatcher.js +52 -1
- package/dist/core/config/RootConfigWatcher.js.map +1 -1
- package/dist/core/config/configUtils.js +31 -8
- package/dist/core/config/configUtils.js.map +1 -1
- package/dist/core/config/harperConfigEnvVars.js +1 -1
- package/dist/core/config/harperConfigEnvVars.js.map +1 -1
- package/dist/core/dataLayer/CreateAttributeObject.js +4 -3
- package/dist/core/dataLayer/CreateAttributeObject.js.map +1 -1
- package/dist/core/dataLayer/CreateTableObject.js +2 -1
- package/dist/core/dataLayer/CreateTableObject.js.map +1 -1
- package/dist/core/dataLayer/DataLayerObjects.js +19 -5
- package/dist/core/dataLayer/DataLayerObjects.js.map +1 -1
- package/dist/core/dataLayer/DeleteBeforeObject.js +2 -1
- package/dist/core/dataLayer/DeleteBeforeObject.js.map +1 -1
- package/dist/core/dataLayer/DeleteObject.js +4 -3
- package/dist/core/dataLayer/DeleteObject.js.map +1 -1
- package/dist/core/dataLayer/DropAttributeObject.js +2 -1
- package/dist/core/dataLayer/DropAttributeObject.js.map +1 -1
- package/dist/core/dataLayer/GetBackupObject.js +4 -3
- package/dist/core/dataLayer/GetBackupObject.js.map +1 -1
- package/dist/core/dataLayer/InsertObject.js +4 -3
- package/dist/core/dataLayer/InsertObject.js.map +1 -1
- package/dist/core/dataLayer/ReadAuditLogObject.js +4 -3
- package/dist/core/dataLayer/ReadAuditLogObject.js.map +1 -1
- package/dist/core/dataLayer/SQLSearch.js +140 -78
- package/dist/core/dataLayer/SQLSearch.js.map +1 -1
- package/dist/core/dataLayer/SearchByConditionsObject.js +5 -7
- package/dist/core/dataLayer/SearchByConditionsObject.js.map +1 -1
- package/dist/core/dataLayer/SearchByHashObject.js +2 -1
- package/dist/core/dataLayer/SearchByHashObject.js.map +1 -1
- package/dist/core/dataLayer/SearchObject.js +2 -1
- package/dist/core/dataLayer/SearchObject.js.map +1 -1
- package/dist/core/dataLayer/SqlSearchObject.js +2 -1
- package/dist/core/dataLayer/SqlSearchObject.js.map +1 -1
- package/dist/core/dataLayer/UpdateObject.js +4 -3
- package/dist/core/dataLayer/UpdateObject.js.map +1 -1
- package/dist/core/dataLayer/UpsertObject.js +4 -3
- package/dist/core/dataLayer/UpsertObject.js.map +1 -1
- package/dist/core/dataLayer/bulkLoad.js +122 -88
- package/dist/core/dataLayer/bulkLoad.js.map +1 -1
- package/dist/core/dataLayer/delete.js +74 -39
- package/dist/core/dataLayer/delete.js.map +1 -1
- package/dist/core/dataLayer/export.js +90 -55
- package/dist/core/dataLayer/export.js.map +1 -1
- package/dist/core/dataLayer/getBackup.js +43 -11
- package/dist/core/dataLayer/getBackup.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/BridgeMethods.js +41 -20
- package/dist/core/dataLayer/harperBridge/BridgeMethods.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/ResourceBridge.js +26 -25
- package/dist/core/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/TableSizeObject.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/bridgeUtility/insertUpdateValidate.js +4 -4
- package/dist/core/dataLayer/harperBridge/harperBridge.js +38 -4
- package/dist/core/dataLayer/harperBridge/harperBridge.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateAttribute.js +7 -6
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateAttribute.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateRecords.js +4 -4
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateRecords.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateSchema.js +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateSchema.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateTable.js +5 -4
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateTable.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDeleteAuditLogsBefore.js +4 -4
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDeleteAuditLogsBefore.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDeleteRecords.js +4 -4
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropAttribute.js +5 -5
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropAttribute.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropSchema.js +4 -4
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropSchema.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropTable.js +5 -5
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropTable.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbFlush.js +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetBackup.js +3 -3
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetDataByHash.js +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetDataByValue.js +2 -2
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetDataByValue.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbReadAuditLog.js +5 -5
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByConditions.js +8 -8
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByConditions.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByHash.js +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByValue.js +3 -3
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByValue.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbUpdateRecords.js +3 -3
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbUpsertRecords.js +5 -5
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbUpsertRecords.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/LMDBCreateAttributeObject.js +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/LMDBCreateAttributeObject.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializeHashSearch.js +2 -2
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializeHashSearch.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +2 -2
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCheckForNewAttributes.js +4 -4
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCheckForNewAttributes.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCreateTransactionsAuditEnvironment.js +5 -3
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCreateTransactionsAuditEnvironment.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbGetTableSize.js +2 -2
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbProcessRows.js +4 -4
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbProcessRows.js.map +1 -1
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbSearch.js +5 -5
- package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbWriteTransaction.js +4 -4
- package/dist/core/dataLayer/hdbInfoController.js +66 -31
- package/dist/core/dataLayer/hdbInfoController.js.map +1 -1
- package/dist/core/dataLayer/insert.js +63 -28
- package/dist/core/dataLayer/insert.js.map +1 -1
- package/dist/core/dataLayer/readAuditLog.js +45 -13
- package/dist/core/dataLayer/readAuditLog.js.map +1 -1
- package/dist/core/dataLayer/schema.js +124 -89
- package/dist/core/dataLayer/schema.js.map +1 -1
- package/dist/core/dataLayer/schemaDescribe.js +78 -41
- package/dist/core/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/core/dataLayer/search.js +12 -13
- package/dist/core/dataLayer/search.js.map +1 -1
- package/dist/core/dataLayer/transaction.js +3 -4
- package/dist/core/dataLayer/transaction.js.map +1 -1
- package/dist/core/dataLayer/update.js +53 -18
- package/dist/core/dataLayer/update.js.map +1 -1
- package/dist/core/globals.js +1 -0
- package/dist/core/globals.js.map +1 -1
- package/dist/core/index.js +4 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/json/systemSchema.json +65 -0
- package/dist/core/launchServiceScripts/utility/checkNodeVersion.js +3 -0
- package/dist/core/launchServiceScripts/utility/checkNodeVersion.js.map +1 -1
- package/dist/core/resources/DatabaseTransaction.js +39 -15
- package/dist/core/resources/DatabaseTransaction.js.map +1 -1
- package/dist/core/resources/ErrorResource.js +3 -1
- package/dist/core/resources/ErrorResource.js.map +1 -1
- package/dist/core/resources/LMDBTransaction.js +18 -7
- package/dist/core/resources/LMDBTransaction.js.map +1 -1
- package/dist/core/resources/RecordEncoder.js +5 -2
- package/dist/core/resources/RecordEncoder.js.map +1 -1
- package/dist/core/resources/RequestTarget.js.map +1 -1
- package/dist/core/resources/Resource.js +37 -10
- package/dist/core/resources/Resource.js.map +1 -1
- package/dist/core/resources/ResourceInterface.js +20 -0
- package/dist/core/resources/ResourceInterface.js.map +1 -1
- package/dist/core/resources/Resources.js +7 -6
- package/dist/core/resources/Resources.js.map +1 -1
- package/dist/core/resources/RocksIndexStore.js +3 -0
- package/dist/core/resources/RocksIndexStore.js.map +1 -1
- package/dist/core/resources/RocksTransactionLogStore.js +46 -27
- package/dist/core/resources/RocksTransactionLogStore.js.map +1 -1
- package/dist/core/resources/Table.js +267 -107
- package/dist/core/resources/Table.js.map +1 -1
- package/dist/core/resources/analytics/metadata.js +1 -0
- package/dist/core/resources/analytics/metadata.js.map +1 -1
- package/dist/core/resources/analytics/read.js +16 -5
- package/dist/core/resources/analytics/read.js.map +1 -1
- package/dist/core/resources/analytics/write.js +232 -20
- package/dist/core/resources/analytics/write.js.map +1 -1
- package/dist/core/resources/auditStore.js +13 -8
- package/dist/core/resources/auditStore.js.map +1 -1
- package/dist/core/resources/blob.js +8 -8
- package/dist/core/resources/blob.js.map +1 -1
- package/dist/core/resources/dataLoader.js +13 -13
- package/dist/core/resources/databases.js +160 -52
- package/dist/core/resources/databases.js.map +1 -1
- package/dist/core/resources/graphql.js +224 -174
- package/dist/core/resources/graphql.js.map +1 -1
- package/dist/core/resources/indexes/HierarchicalNavigableSmallWorld.js +308 -59
- package/dist/core/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/core/resources/indexes/vector.js +14 -0
- package/dist/core/resources/indexes/vector.js.map +1 -1
- package/dist/core/resources/loadEnv.js +21 -18
- package/dist/core/resources/loadEnv.js.map +1 -1
- package/dist/core/resources/login.js +5 -4
- package/dist/core/resources/login.js.map +1 -1
- package/dist/core/resources/models/Models.js +265 -0
- package/dist/core/resources/models/Models.js.map +1 -0
- package/dist/core/resources/models/TestBackend.js +71 -0
- package/dist/core/resources/models/TestBackend.js.map +1 -0
- package/dist/core/resources/models/agentLoop.js +746 -0
- package/dist/core/resources/models/agentLoop.js.map +1 -0
- package/dist/core/resources/models/analyticsTable.js +166 -0
- package/dist/core/resources/models/analyticsTable.js.map +1 -0
- package/dist/core/resources/models/backendHelpers.js +109 -0
- package/dist/core/resources/models/backendHelpers.js.map +1 -0
- package/dist/core/resources/models/backendRegistry.js +54 -0
- package/dist/core/resources/models/backendRegistry.js.map +1 -0
- package/dist/core/resources/models/bootstrap.js +112 -0
- package/dist/core/resources/models/bootstrap.js.map +1 -0
- package/dist/core/resources/models/embedHook.js +127 -0
- package/dist/core/resources/models/embedHook.js.map +1 -0
- package/dist/core/resources/models/types.js +11 -0
- package/dist/core/resources/models/types.js.map +1 -0
- package/dist/core/resources/replayLogs.js +7 -1
- package/dist/core/resources/replayLogs.js.map +1 -1
- package/dist/core/resources/roles.js +67 -71
- package/dist/core/resources/roles.js.map +1 -1
- package/dist/core/resources/search.js +349 -140
- package/dist/core/resources/search.js.map +1 -1
- package/dist/core/resources/tracked.js +14 -14
- package/dist/core/resources/tracked.js.map +1 -1
- package/dist/core/resources/transaction.js +1 -1
- package/dist/core/resources/transaction.js.map +1 -1
- package/dist/core/resources/transactionBroadcast.js.map +1 -1
- package/dist/core/security/auth.js +34 -25
- package/dist/core/security/auth.js.map +1 -1
- package/dist/core/security/certificateVerification/crlVerification.js +7 -1
- package/dist/core/security/certificateVerification/crlVerification.js.map +1 -1
- package/dist/core/security/cryptoHash.js +37 -5
- package/dist/core/security/cryptoHash.js.map +1 -1
- package/dist/core/security/data_objects/PermissionAttributeResponseObject.js +4 -1
- package/dist/core/security/data_objects/PermissionAttributeResponseObject.js.map +1 -1
- package/dist/core/security/data_objects/PermissionResponseObject.js +15 -8
- package/dist/core/security/data_objects/PermissionResponseObject.js.map +1 -1
- package/dist/core/security/data_objects/PermissionTableResponseObject.js +6 -1
- package/dist/core/security/data_objects/PermissionTableResponseObject.js.map +1 -1
- package/dist/core/security/fastifyAuth.js +131 -22
- package/dist/core/security/fastifyAuth.js.map +1 -1
- package/dist/core/security/impersonation.js +15 -15
- package/dist/core/security/jsLoader.js +18 -5
- package/dist/core/security/jsLoader.js.map +1 -1
- package/dist/core/security/keys.js +160 -114
- package/dist/core/security/keys.js.map +1 -1
- package/dist/core/security/permissionsTranslator.js +2 -2
- package/dist/core/security/role.js +70 -35
- package/dist/core/security/role.js.map +1 -1
- package/dist/core/security/tokenAuthentication.js +57 -27
- package/dist/core/security/tokenAuthentication.js.map +1 -1
- package/dist/core/security/user.js +74 -38
- package/dist/core/security/user.js.map +1 -1
- package/dist/core/server/DurableSubscriptionsSession.js +71 -55
- package/dist/core/server/DurableSubscriptionsSession.js.map +1 -1
- package/dist/core/server/REST.js +17 -16
- package/dist/core/server/REST.js.map +1 -1
- package/dist/core/server/Server.js +1 -1
- package/dist/core/server/Server.js.map +1 -1
- package/dist/core/server/fastifyRoutes/helpers/getCORSOptions.js +1 -1
- package/dist/core/server/fastifyRoutes/helpers/getHeaderTimeoutConfig.js +1 -1
- package/dist/core/server/fastifyRoutes/helpers/getServerOptions.js +1 -1
- package/dist/core/server/fastifyRoutes.js +34 -30
- package/dist/core/server/fastifyRoutes.js.map +1 -1
- package/dist/core/server/graphqlQuerying.js +8 -7
- package/dist/core/server/graphqlQuerying.js.map +1 -1
- package/dist/core/server/http.js +523 -47
- package/dist/core/server/http.js.map +1 -1
- package/dist/core/server/itc/serverHandlers.js +66 -15
- package/dist/core/server/itc/serverHandlers.js.map +1 -1
- package/dist/core/server/jobs/JobObject.js +53 -7
- package/dist/core/server/jobs/JobObject.js.map +1 -1
- package/dist/core/server/jobs/jobProcess.js +64 -24
- package/dist/core/server/jobs/jobProcess.js.map +1 -1
- package/dist/core/server/jobs/jobRunner.js +68 -30
- package/dist/core/server/jobs/jobRunner.js.map +1 -1
- package/dist/core/server/jobs/jobs.js +93 -61
- package/dist/core/server/jobs/jobs.js.map +1 -1
- package/dist/core/server/loadRootComponents.js +1 -1
- package/dist/core/server/middlewareChain.js +252 -0
- package/dist/core/server/middlewareChain.js.map +1 -0
- package/dist/core/server/mqtt.js +22 -17
- package/dist/core/server/mqtt.js.map +1 -1
- package/dist/core/server/nodeName.js +44 -13
- package/dist/core/server/nodeName.js.map +1 -1
- package/dist/core/server/operationsServer.js +106 -33
- package/dist/core/server/operationsServer.js.map +1 -1
- package/dist/core/server/serverHelpers/Headers.js +2 -0
- package/dist/core/server/serverHelpers/Headers.js.map +1 -1
- package/dist/core/server/serverHelpers/JSONStream.js +12 -3
- package/dist/core/server/serverHelpers/JSONStream.js.map +1 -1
- package/dist/core/server/serverHelpers/Request.js +370 -9
- package/dist/core/server/serverHelpers/Request.js.map +1 -1
- package/dist/core/server/serverHelpers/contentTypes.js +36 -7
- package/dist/core/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/core/server/serverHelpers/multipartParser.js +142 -0
- package/dist/core/server/serverHelpers/multipartParser.js.map +1 -0
- package/dist/core/server/serverHelpers/progressEmitter.js +103 -0
- package/dist/core/server/serverHelpers/progressEmitter.js.map +1 -0
- package/dist/core/server/serverHelpers/serverHandlers.js +38 -7
- package/dist/core/server/serverHelpers/serverHandlers.js.map +1 -1
- package/dist/core/server/serverHelpers/serverUtilities.js +97 -93
- package/dist/core/server/serverHelpers/serverUtilities.js.map +1 -1
- package/dist/core/server/static.js +8 -5
- package/dist/core/server/static.js.map +1 -1
- package/dist/core/server/status/index.js +3 -3
- package/dist/core/server/storageReclamation.js +68 -9
- package/dist/core/server/storageReclamation.js.map +1 -1
- package/dist/core/server/threads/itc.js +7 -4
- package/dist/core/server/threads/itc.js.map +1 -1
- package/dist/core/server/threads/manageThreads.js +110 -26
- package/dist/core/server/threads/manageThreads.js.map +1 -1
- package/dist/core/server/threads/socketRouter.js +8 -271
- package/dist/core/server/threads/socketRouter.js.map +1 -1
- package/dist/core/server/threads/threadServer.js +360 -118
- package/dist/core/server/threads/threadServer.js.map +1 -1
- package/dist/core/server/threads/workerProcessGuard.js +114 -0
- package/dist/core/server/threads/workerProcessGuard.js.map +1 -0
- package/dist/core/server/throttle.js +17 -0
- package/dist/core/server/throttle.js.map +1 -1
- package/dist/core/sqlTranslator/SelectValidator.js +86 -47
- package/dist/core/sqlTranslator/SelectValidator.js.map +1 -1
- package/dist/core/sqlTranslator/alasqlFunctionImporter.js +40 -3
- package/dist/core/sqlTranslator/alasqlFunctionImporter.js.map +1 -1
- package/dist/core/sqlTranslator/deleteTranslator.js +48 -14
- package/dist/core/sqlTranslator/deleteTranslator.js.map +1 -1
- package/dist/core/sqlTranslator/index.js +69 -30
- package/dist/core/sqlTranslator/index.js.map +1 -1
- package/dist/core/sqlTranslator/sql_statement_bucket.js +55 -13
- package/dist/core/sqlTranslator/sql_statement_bucket.js.map +1 -1
- package/dist/core/upgrade/UpgradeObjects.js +37 -4
- package/dist/core/upgrade/UpgradeObjects.js.map +1 -1
- package/dist/core/upgrade/directives/5-2-0.js +77 -0
- package/dist/core/upgrade/directives/5-2-0.js.map +1 -0
- package/dist/core/upgrade/directives/directivesController.js +52 -11
- package/dist/core/upgrade/directives/directivesController.js.map +1 -1
- package/dist/core/upgrade/directivesManager.js +53 -18
- package/dist/core/upgrade/directivesManager.js.map +1 -1
- package/dist/core/upgrade/upgradePrompt.js +65 -30
- package/dist/core/upgrade/upgradePrompt.js.map +1 -1
- package/dist/core/upgrade/upgradeUtilities.js +37 -5
- package/dist/core/upgrade/upgradeUtilities.js.map +1 -1
- package/dist/core/utility/OperationFunctionCaller.js +45 -10
- package/dist/core/utility/OperationFunctionCaller.js.map +1 -1
- package/dist/core/utility/assignCmdEnvVariables.js +8 -4
- package/dist/core/utility/assignCmdEnvVariables.js.map +1 -1
- package/dist/core/utility/common_utils.js +140 -79
- package/dist/core/utility/common_utils.js.map +1 -1
- package/dist/core/utility/environment/environmentManager.js +75 -29
- package/dist/core/utility/environment/environmentManager.js.map +1 -1
- package/dist/core/utility/environment/systemInformation.js +27 -16
- package/dist/core/utility/environment/systemInformation.js.map +1 -1
- package/dist/core/utility/errors/commonErrors.js +49 -18
- package/dist/core/utility/errors/commonErrors.js.map +1 -1
- package/dist/core/utility/errors/hdbError.js +65 -26
- package/dist/core/utility/errors/hdbError.js.map +1 -1
- package/dist/core/utility/expandEnvVar.js +113 -0
- package/dist/core/utility/expandEnvVar.js.map +1 -0
- package/dist/core/utility/functions/geo.js +2 -2
- package/dist/core/utility/functions/sql/alaSQLExtension.js +1 -1
- package/dist/core/utility/globalSchema.js +14 -11
- package/dist/core/utility/globalSchema.js.map +1 -1
- package/dist/core/utility/hdbTerms.js +56 -0
- package/dist/core/utility/hdbTerms.js.map +1 -1
- package/dist/core/utility/install/checkJWTTokensExist.js +1 -1
- package/dist/core/utility/install/installer.js +106 -70
- package/dist/core/utility/install/installer.js.map +1 -1
- package/dist/core/utility/installation.js +3 -3
- package/dist/core/utility/lmdb/DBIDefinition.js +5 -1
- package/dist/core/utility/lmdb/DBIDefinition.js.map +1 -1
- package/dist/core/utility/lmdb/DeleteRecordsResponseObject.js +2 -1
- package/dist/core/utility/lmdb/DeleteRecordsResponseObject.js.map +1 -1
- package/dist/core/utility/lmdb/InsertRecordsResponseObject.js +2 -1
- package/dist/core/utility/lmdb/InsertRecordsResponseObject.js.map +1 -1
- package/dist/core/utility/lmdb/OpenDBIObject.js +54 -6
- package/dist/core/utility/lmdb/OpenDBIObject.js.map +1 -1
- package/dist/core/utility/lmdb/OpenEnvironmentObject.js +52 -4
- package/dist/core/utility/lmdb/OpenEnvironmentObject.js.map +1 -1
- package/dist/core/utility/lmdb/UpdateRecordsResponseObject.js +2 -1
- package/dist/core/utility/lmdb/UpdateRecordsResponseObject.js.map +1 -1
- package/dist/core/utility/lmdb/UpsertRecordsResponseObject.js +2 -1
- package/dist/core/utility/lmdb/UpsertRecordsResponseObject.js.map +1 -1
- package/dist/core/utility/lmdb/cleanLMDBMap.js +44 -7
- package/dist/core/utility/lmdb/cleanLMDBMap.js.map +1 -1
- package/dist/core/utility/lmdb/commonUtility.js +46 -17
- package/dist/core/utility/lmdb/commonUtility.js.map +1 -1
- package/dist/core/utility/lmdb/deleteUtility.js +51 -16
- package/dist/core/utility/lmdb/deleteUtility.js.map +1 -1
- package/dist/core/utility/lmdb/environmentUtility.js +91 -51
- package/dist/core/utility/lmdb/environmentUtility.js.map +1 -1
- package/dist/core/utility/lmdb/searchCursorFunctions.js +46 -14
- package/dist/core/utility/lmdb/searchCursorFunctions.js.map +1 -1
- package/dist/core/utility/lmdb/searchUtility.js +91 -55
- package/dist/core/utility/lmdb/searchUtility.js.map +1 -1
- package/dist/core/utility/lmdb/terms.js +12 -22
- package/dist/core/utility/lmdb/terms.js.map +1 -1
- package/dist/core/utility/lmdb/writeUtility.js +61 -28
- package/dist/core/utility/lmdb/writeUtility.js.map +1 -1
- package/dist/core/utility/logging/harper_logger.js +176 -81
- package/dist/core/utility/logging/harper_logger.js.map +1 -1
- package/dist/core/utility/logging/logRotator.js +65 -28
- package/dist/core/utility/logging/logRotator.js.map +1 -1
- package/dist/core/utility/logging/logger.js +4 -4
- package/dist/core/utility/logging/readLog.js +54 -17
- package/dist/core/utility/logging/readLog.js.map +1 -1
- package/dist/core/utility/logging/transactionLog.js +51 -16
- package/dist/core/utility/logging/transactionLog.js.map +1 -1
- package/dist/core/utility/mount_hdb.js +54 -17
- package/dist/core/utility/mount_hdb.js.map +1 -1
- package/dist/core/utility/npmUtilities.js +54 -19
- package/dist/core/utility/npmUtilities.js.map +1 -1
- package/dist/core/utility/operation_authorization.js +135 -86
- package/dist/core/utility/operation_authorization.js.map +1 -1
- package/dist/core/utility/packageUtils.js +7 -17
- package/dist/core/utility/packageUtils.js.map +1 -1
- package/dist/core/utility/password.js +2 -2
- package/dist/core/utility/processManagement/processManagement.js +2 -2
- package/dist/core/utility/processManagement/servicesConfig.js +1 -1
- package/dist/core/utility/signalling.js +51 -16
- package/dist/core/utility/signalling.js.map +1 -1
- package/dist/core/utility/watcherFallback.js +73 -0
- package/dist/core/utility/watcherFallback.js.map +1 -0
- package/dist/core/validation/analyticsValidator.js +79 -0
- package/dist/core/validation/analyticsValidator.js.map +1 -0
- package/dist/core/validation/bulkDeleteValidator.js +49 -11
- package/dist/core/validation/bulkDeleteValidator.js.map +1 -1
- package/dist/core/validation/check_permissions.js +38 -3
- package/dist/core/validation/check_permissions.js.map +1 -1
- package/dist/core/validation/common_validators.js +62 -31
- package/dist/core/validation/common_validators.js.map +1 -1
- package/dist/core/validation/configValidator.js +189 -54
- package/dist/core/validation/configValidator.js.map +1 -1
- package/dist/core/validation/deleteValidator.js +49 -11
- package/dist/core/validation/deleteValidator.js.map +1 -1
- package/dist/core/validation/fileLoadValidator.js +67 -32
- package/dist/core/validation/fileLoadValidator.js.map +1 -1
- package/dist/core/validation/insertValidator.js +48 -10
- package/dist/core/validation/insertValidator.js.map +1 -1
- package/dist/core/validation/installValidator.js +47 -10
- package/dist/core/validation/installValidator.js.map +1 -1
- package/dist/core/validation/readLogValidator.js +60 -22
- package/dist/core/validation/readLogValidator.js.map +1 -1
- package/dist/core/validation/role_validation.js +55 -19
- package/dist/core/validation/role_validation.js.map +1 -1
- package/dist/core/validation/schemaMetadataValidator.js +11 -12
- package/dist/core/validation/schemaMetadataValidator.js.map +1 -1
- package/dist/core/validation/searchValidator.js +82 -43
- package/dist/core/validation/searchValidator.js.map +1 -1
- package/dist/core/validation/transactionLogValidator.js +52 -17
- package/dist/core/validation/transactionLogValidator.js.map +1 -1
- package/dist/core/validation/user_validation.js +38 -6
- package/dist/core/validation/user_validation.js.map +1 -1
- package/dist/core/validation/validationWrapper.js +4 -5
- package/dist/core/validation/validationWrapper.js.map +1 -1
- package/dist/licensing/usageLicensing.js +30 -21
- package/dist/licensing/usageLicensing.js.map +1 -1
- package/dist/replication/knownNodes.js +173 -39
- package/dist/replication/knownNodes.js.map +1 -1
- package/dist/replication/replicationConnection.js +441 -85
- package/dist/replication/replicationConnection.js.map +1 -1
- package/dist/replication/replicator.js +44 -26
- package/dist/replication/replicator.js.map +1 -1
- package/dist/replication/setNode.js +22 -2
- package/dist/replication/setNode.js.map +1 -1
- package/dist/replication/subscriptionManager.js +121 -9
- package/dist/replication/subscriptionManager.js.map +1 -1
- package/dist/security/certificate.js +41 -6
- package/dist/security/certificate.js.map +1 -1
- package/dist/security/sshKeyOperations.js +35 -2
- package/dist/security/sshKeyOperations.js.map +1 -1
- package/licensing/usageLicensing.ts +32 -37
- package/npm-shrinkwrap.json +8994 -4305
- package/package.json +16 -11
- package/replication/DESIGN.md +139 -0
- package/replication/knownNodes.ts +166 -43
- package/replication/replicationConnection.ts +475 -92
- package/replication/replicator.ts +42 -25
- package/replication/setNode.ts +28 -9
- package/replication/subscriptionManager.ts +138 -14
- package/security/certificate.ts +8 -4
- package/security/sshKeyOperations.ts +1 -1
- package/static/defaultConfig.yaml +1 -0
- package/studio/web/assets/{index-CmtPP0YO.js → index-COfIkCT-.js} +5 -5
- package/studio/web/assets/index-COfIkCT-.js.map +1 -0
- package/studio/web/assets/{index.lazy-C8jvGtlu.js → index.lazy-CIvl7Fj9.js} +2 -2
- package/studio/web/assets/{index.lazy-C8jvGtlu.js.map → index.lazy-CIvl7Fj9.js.map} +1 -1
- package/studio/web/assets/{profile-O0DYlJUv.js → profile-B2ZVB--r.js} +2 -2
- package/studio/web/assets/{profile-O0DYlJUv.js.map → profile-B2ZVB--r.js.map} +1 -1
- package/studio/web/assets/{status-BIlJkJby.js → status-Db6WBmhf.js} +2 -2
- package/studio/web/assets/{status-BIlJkJby.js.map → status-Db6WBmhf.js.map} +1 -1
- package/studio/web/index.html +1 -1
- package/core/bin/cliOperations.js +0 -159
- package/core/bin/lite.js +0 -5
- package/core/bin/stop.js +0 -21
- package/core/dataLayer/harperBridge/BridgeMethods.js +0 -85
- package/core/utility/globalSchema.js +0 -35
- package/core/utility/lmdb/OpenDBIObject.js +0 -31
- package/studio/web/assets/index-CmtPP0YO.js.map +0 -1
|
@@ -31,10 +31,11 @@ import {
|
|
|
31
31
|
urlToNodeName,
|
|
32
32
|
} from './replicator.ts';
|
|
33
33
|
import { getThisNodeName } from '../core/server/nodeName.ts';
|
|
34
|
-
import env from '../core/utility/environment/environmentManager.js';
|
|
34
|
+
import * as env from '../core/utility/environment/environmentManager.js';
|
|
35
35
|
import { CONFIG_PARAMS } from '../core/utility/hdbTerms.ts';
|
|
36
36
|
import { HAS_STRUCTURE_UPDATE, lastMetadata, lastValueEncoding, METADATA } from '../core/resources/RecordEncoder.ts';
|
|
37
37
|
import { decode, encode, Packr } from 'msgpackr';
|
|
38
|
+
import { createStructon } from 'structon';
|
|
38
39
|
import { WebSocket } from 'ws';
|
|
39
40
|
import { threadId } from 'worker_threads';
|
|
40
41
|
import harperLogger from '../core/utility/logging/harper_logger.js';
|
|
@@ -43,11 +44,17 @@ import { disconnectedFromNode, connectedToNode, ensureNode } from './subscriptio
|
|
|
43
44
|
import { EventEmitter } from 'events';
|
|
44
45
|
import { createTLSSelector } from '../core/security/keys.js';
|
|
45
46
|
import * as tls from 'node:tls';
|
|
46
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
getHDBNodeTable,
|
|
49
|
+
getNodeURL,
|
|
50
|
+
getReplicationSharedStatus,
|
|
51
|
+
getExcludedTablesForRouteEntries,
|
|
52
|
+
} from './knownNodes.ts';
|
|
47
53
|
import * as process from 'node:process';
|
|
48
54
|
import { isIP } from 'node:net';
|
|
49
55
|
import { recordAction } from '../core/resources/analytics/write.ts';
|
|
50
56
|
import {
|
|
57
|
+
createBlob,
|
|
51
58
|
decodeBlobsWithWrites,
|
|
52
59
|
decodeFromDatabase,
|
|
53
60
|
decodeWithBlobCallback,
|
|
@@ -59,6 +66,14 @@ import { PassThrough } from 'node:stream';
|
|
|
59
66
|
import { getLastVersion } from 'lmdb';
|
|
60
67
|
const logger = forComponent('replication').conditional as Logger;
|
|
61
68
|
|
|
69
|
+
// msgpackr v2 removed the built-in `randomAccessStructure` option; that random-access
|
|
70
|
+
// struct support now lives in the `structon` package (the same wrapper core's
|
|
71
|
+
// RecordEncoder uses). Replication decoders must be structon-wrapped so they install
|
|
72
|
+
// the `_readStruct` hook and can decode typed-struct records — without it, struct
|
|
73
|
+
// marker bytes (0x20–0x3f) decode as plain integers and the decode aborts with
|
|
74
|
+
// "Data read, but end of buffer not reached".
|
|
75
|
+
const StructonPackr = createStructon(Packr);
|
|
76
|
+
|
|
62
77
|
// these are the codes we use for the different commands
|
|
63
78
|
const SUBSCRIPTION_REQUEST = 129;
|
|
64
79
|
const NODE_NAME = 140;
|
|
@@ -75,6 +90,8 @@ const COMMITTED_UPDATE = 144;
|
|
|
75
90
|
const DB_SCHEMA = 145;
|
|
76
91
|
const BLOB_CHUNK = 146;
|
|
77
92
|
const SUBSCRIPTION_UPDATE = 147;
|
|
93
|
+
const COPY_START = 148; // leader -> follower: a bulk table copy is starting; carries copyStartTime
|
|
94
|
+
const COPY_COMPLETE = 149; // leader -> follower: the bulk table copy finished; follower clears its resume cursor
|
|
78
95
|
export const CONFIRMATION_STATUS_POSITION = 0;
|
|
79
96
|
export const RECEIVED_VERSION_POSITION = 1;
|
|
80
97
|
export const RECEIVED_TIME_POSITION = 2;
|
|
@@ -91,6 +108,16 @@ const MAX_PAYLOAD = env.get('replication_maxPayload') ?? 100_000_000;
|
|
|
91
108
|
// heap past its limit. If the local replicator queue grows beyond this threshold we pause
|
|
92
109
|
// the WS connection and wait for it to drain before continuing the decode loop.
|
|
93
110
|
const RECEIVE_EVENT_HIGH_WATER_MARK = env.get('replication_receiveEventHighWaterMark') ?? 100;
|
|
111
|
+
// Even when the consumer keeps up (queue below the high-water mark), a single large inbound message
|
|
112
|
+
// would otherwise decode thousands of records in one synchronous turn — pegging the worker, blocking
|
|
113
|
+
// replication ping responses, and tripping core's "JavaScript execution has taken too long" monitor
|
|
114
|
+
// (MAX_EVENT_DELAY_TIME = 3 s). Yield the event loop at least this often (ms) while decoding so the
|
|
115
|
+
// worker stays responsive during a bulk copy/clone.
|
|
116
|
+
const RECEIVE_YIELD_INTERVAL = env.get('replication_receiveYieldInterval') ?? 100;
|
|
117
|
+
// During a bulk clone copy the leader flushes a checkpoint transaction every this many records so the
|
|
118
|
+
// follower commits incrementally and persists a resume cursor. On reconnect the copy resumes from that
|
|
119
|
+
// cursor instead of restarting from zero. Larger = less overhead but coarser resume granularity.
|
|
120
|
+
const COPY_CHECKPOINT_RECORDS = env.get('replication_copyCheckpointRecords') ?? 1000;
|
|
94
121
|
|
|
95
122
|
export const tableUpdateListeners = new Map();
|
|
96
123
|
// This a map of the database name to the subscription object, for the subscriptions from our tables to the replication module
|
|
@@ -104,7 +131,95 @@ const SKIPPED_MESSAGE_SEQUENCE_UPDATE_DELAY = 300;
|
|
|
104
131
|
// We want it be fairly quick so we can let the sending node know that we have received and committed the update.
|
|
105
132
|
// (but still allow for batching so we aren't sending out a message for every update under load)
|
|
106
133
|
const COMMITTED_UPDATE_DELAY = 2;
|
|
107
|
-
const PING_INTERVAL = 30000;
|
|
134
|
+
const PING_INTERVAL = env.get(CONFIG_PARAMS.REPLICATION_PINGINTERVAL) ?? 30000;
|
|
135
|
+
// Time (ms) without any socket activity before a connection is treated as dead.
|
|
136
|
+
const PING_TIMEOUT = env.get(CONFIG_PARAMS.REPLICATION_PINGTIMEOUT) ?? PING_INTERVAL * 2;
|
|
137
|
+
// The receive-side watchdog fires after this much silence on a replication WS. Both client and
|
|
138
|
+
// server arm it: the client also runs an active 30s sendPing tick that should normally catch a
|
|
139
|
+
// silent peer first, but if that tick is missed (event-loop stall, ws.terminate() not propagating
|
|
140
|
+
// to 'close', etc.) this timer-based watchdog is the belt-and-suspenders that forces the
|
|
141
|
+
// reconnect — see harper-pro#233.
|
|
142
|
+
const RECEIVE_SILENCE_THRESHOLD_MS = PING_TIMEOUT;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Decide whether an idle replication connection should be terminated as dead.
|
|
146
|
+
*
|
|
147
|
+
* Liveness is measured from the last observed socket byte movement (in either direction), not from a
|
|
148
|
+
* single ping interval. A bulk transfer — notably the initial clone copy of a large table — makes
|
|
149
|
+
* slow but real progress: the sender's socket buffer drains in bursts as the peer consumes, so bytes
|
|
150
|
+
* keep moving within the timeout window even while it is otherwise stalled. A genuinely dead or
|
|
151
|
+
* unreachable peer moves no bytes at all, so it still trips the timeout. We deliberately do NOT exempt
|
|
152
|
+
* the sender's `isPausedForBackPressure` drain-wait here: if the peer dies after we have filled our
|
|
153
|
+
* socket buffer, the drain event never fires, and exempting it would hang the connection forever.
|
|
154
|
+
*
|
|
155
|
+
* The one exemption is `pauseReasons > 0`: the receiver has intentionally stopped reading to drain its
|
|
156
|
+
* own queue. That stall is local and self-clearing (it does not depend on the peer), so it is never a
|
|
157
|
+
* death signal; the caller keeps liveness fresh while paused and resumes normal detection afterward.
|
|
158
|
+
*/
|
|
159
|
+
export function shouldTerminateIdlePing(idleMs: number, pingTimeout: number, pauseReasons: number): boolean {
|
|
160
|
+
return pauseReasons === 0 && idleMs >= pingTimeout;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Receive-side silence watchdog. Arm with `reset()` whenever incoming activity is observed
|
|
165
|
+
* (peer ping, pong, message). If `intervalMs` elapses with the underlying socket's `bytesRead`
|
|
166
|
+
* unchanged, `onSilence` is invoked exactly once.
|
|
167
|
+
*
|
|
168
|
+
* Only `bytesRead` is checked — `bytesWritten` reflects our own outbound traffic (pings, blob
|
|
169
|
+
* sends) and is not proof that the peer is alive. Including it would let our own keepalive
|
|
170
|
+
* pings suppress the watchdog in the exact missed-sendPing scenario this is meant to recover.
|
|
171
|
+
*
|
|
172
|
+
* Callers should suspend the watchdog (via `stop()`) when the underlying WS is intentionally
|
|
173
|
+
* paused for backpressure: `bytesRead` is frozen by design while paused and would otherwise
|
|
174
|
+
* cause a spurious termination of a healthy connection.
|
|
175
|
+
*
|
|
176
|
+
* Exported so the timer logic can be exercised in isolation by `unitTests/replication/
|
|
177
|
+
* receiveWatchdog.test.mjs` — production callers go through `replicateOverWS`.
|
|
178
|
+
*/
|
|
179
|
+
export function createReceiveWatchdog(opts: {
|
|
180
|
+
intervalMs: number;
|
|
181
|
+
getBytesRead: () => number;
|
|
182
|
+
onSilence: () => void;
|
|
183
|
+
}): { reset: () => void; stop: () => void } {
|
|
184
|
+
let timer: NodeJS.Timeout | undefined;
|
|
185
|
+
let bytesReadAtArm = 0;
|
|
186
|
+
let lastResetAt = 0;
|
|
187
|
+
// Coalesce rapid reset() calls (e.g. message frames arriving thousands of times per second
|
|
188
|
+
// during a large copy) so we do not churn setTimeout/clearTimeout per frame. Granularity loss
|
|
189
|
+
// is small relative to intervalMs — at worst the watchdog fires this much earlier or later.
|
|
190
|
+
const throttleMs = Math.min(1000, Math.max(100, opts.intervalMs / 30));
|
|
191
|
+
function check() {
|
|
192
|
+
const current = opts.getBytesRead();
|
|
193
|
+
if (current === bytesReadAtArm) {
|
|
194
|
+
timer = undefined;
|
|
195
|
+
opts.onSilence();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
// Bytes advanced since the last arm — but the activity may have been swallowed by the
|
|
199
|
+
// reset() throttle, so we cannot rely on an external caller to re-arm us. Re-arm from
|
|
200
|
+
// the new baseline; otherwise a throttled-reset-then-silence sequence would leave the
|
|
201
|
+
// watchdog permanently inactive (see PR #234 review).
|
|
202
|
+
bytesReadAtArm = current;
|
|
203
|
+
lastResetAt = Date.now();
|
|
204
|
+
timer = setTimeout(check, opts.intervalMs).unref();
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
reset() {
|
|
208
|
+
const now = Date.now();
|
|
209
|
+
if (timer && now - lastResetAt < throttleMs) return;
|
|
210
|
+
lastResetAt = now;
|
|
211
|
+
if (timer) clearTimeout(timer);
|
|
212
|
+
bytesReadAtArm = opts.getBytesRead();
|
|
213
|
+
timer = setTimeout(check, opts.intervalMs).unref();
|
|
214
|
+
},
|
|
215
|
+
stop() {
|
|
216
|
+
if (timer) {
|
|
217
|
+
clearTimeout(timer);
|
|
218
|
+
timer = undefined;
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
108
223
|
let secureContexts: Map<string, tls.SecureContext>;
|
|
109
224
|
/**
|
|
110
225
|
* Handles reconnection, and requesting catch-up
|
|
@@ -161,7 +276,7 @@ export async function createWebSocket(
|
|
|
161
276
|
);
|
|
162
277
|
}
|
|
163
278
|
}
|
|
164
|
-
const headers = {};
|
|
279
|
+
const headers: Record<string, string> = {};
|
|
165
280
|
if (authorization) {
|
|
166
281
|
headers.Authorization = authorization;
|
|
167
282
|
}
|
|
@@ -222,6 +337,7 @@ export class NodeReplicationConnection extends EventEmitter {
|
|
|
222
337
|
databaseName: string;
|
|
223
338
|
nodeName?: string;
|
|
224
339
|
authorization?: string;
|
|
340
|
+
tentativeNode?: any;
|
|
225
341
|
constructor(url: string, subscription: any, databaseName: string, nodeName?: string, authorization?: string) {
|
|
226
342
|
super();
|
|
227
343
|
this.url = url;
|
|
@@ -232,6 +348,7 @@ export class NodeReplicationConnection extends EventEmitter {
|
|
|
232
348
|
}
|
|
233
349
|
|
|
234
350
|
async connect() {
|
|
351
|
+
if (this.intentionallyUnsubscribed) return;
|
|
235
352
|
if (!this.session) this.resetSession();
|
|
236
353
|
// TODO: Need to do this specifically for each node
|
|
237
354
|
this.socket = await createWebSocket(this.url, { serverName: this.nodeName, authorization: this.authorization });
|
|
@@ -357,7 +474,7 @@ export class NodeReplicationConnection extends EventEmitter {
|
|
|
357
474
|
}
|
|
358
475
|
unsubscribe() {
|
|
359
476
|
this.intentionallyUnsubscribed = true;
|
|
360
|
-
this.socket
|
|
477
|
+
this.socket?.close(1008, 'No longer subscribed');
|
|
361
478
|
}
|
|
362
479
|
|
|
363
480
|
getRecord(request) {
|
|
@@ -370,7 +487,7 @@ export class NodeReplicationConnection extends EventEmitter {
|
|
|
370
487
|
/**
|
|
371
488
|
* This handles both incoming and outgoing WS allowing either one to issue a subscription and get replication and/or handle subscription requests
|
|
372
489
|
*/
|
|
373
|
-
export function replicateOverWS(ws: WebSocket, options: any, authorization:
|
|
490
|
+
export function replicateOverWS(ws: WebSocket, options: any, authorization: any) {
|
|
374
491
|
const p = options.port || options.securePort;
|
|
375
492
|
const connectionId =
|
|
376
493
|
(process.pid % 1000) +
|
|
@@ -387,7 +504,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
387
504
|
let databaseName = options.database;
|
|
388
505
|
const dbSubscriptions = options.databaseSubscriptions || databaseSubscriptions;
|
|
389
506
|
let auditStore: any;
|
|
390
|
-
let auditLogIterable: Iterable<AuditRecord
|
|
507
|
+
let auditLogIterable: Iterable<AuditRecord> & { removeLog?: (name: string) => void; addLog?: (name: string) => void }; // reusable iterator for a subscription
|
|
391
508
|
let replicationSharedStatus: Float64Array;
|
|
392
509
|
// this is the subscription that the local table makes to this replicator, and incoming messages
|
|
393
510
|
// are sent to this subscription queue:
|
|
@@ -405,7 +522,33 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
405
522
|
remoteNodeName = authorization.name;
|
|
406
523
|
if (remoteNodeName && options.connection) options.connection.nodeName = remoteNodeName;
|
|
407
524
|
let lastSequenceIdReceived, lastSequenceIdCommitted;
|
|
408
|
-
|
|
525
|
+
// Bulk-copy resume state (receiver side). While inCopyMode, each committed batch persists a cursor
|
|
526
|
+
// (copyStartTime + last committed table/key) so an interrupted copy can resume instead of restarting.
|
|
527
|
+
let inCopyMode = false;
|
|
528
|
+
let copyModeStartTime = 0;
|
|
529
|
+
let copyFromNodeId; // local id of the node we are copying from — the key for the persisted cursor
|
|
530
|
+
let copyCompleteReceived = false;
|
|
531
|
+
// Finish the copy — leave copy mode and remove the resume cursor — only once COPY_COMPLETE has been
|
|
532
|
+
// received AND every copied batch has committed (outstandingCommits drained, which includes the final
|
|
533
|
+
// end_txn that advances the resume seqId to copyStartTime). We deliberately stay in copy mode until
|
|
534
|
+
// then so batches still committing keep advancing the cursor (onCommit). Finishing earlier — e.g.
|
|
535
|
+
// synchronously when COPY_COMPLETE is decoded while batches are still queued — would freeze the cursor
|
|
536
|
+
// and risk a crash that loses both the cursor and the not-yet-durable rows, leaving the next start to
|
|
537
|
+
// resume from seqId with gaps.
|
|
538
|
+
function maybeFinishCopy() {
|
|
539
|
+
if (copyCompleteReceived && outstandingCommits === 0) {
|
|
540
|
+
// guard only the cursor removal on a known node id; ALWAYS exit copy mode, otherwise a
|
|
541
|
+
// COPY_START whose getIdOfRemoteNode returned undefined would strand the node in copy mode
|
|
542
|
+
// (received-version watermark suppressed) and it could never reach Available.
|
|
543
|
+
if (copyFromNodeId !== undefined)
|
|
544
|
+
tableSubscriptionToReplicator?.dbisDB?.remove([Symbol.for('copyCursor'), copyFromNodeId]);
|
|
545
|
+
inCopyMode = false;
|
|
546
|
+
copyCompleteReceived = false;
|
|
547
|
+
copyFromNodeId = undefined;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
let sendPingInterval, lastPingTime, skippedMessageSequenceUpdateTimer;
|
|
551
|
+
let receiveWatchdog: { reset: () => void; stop: () => void } | undefined;
|
|
409
552
|
let blobsTimer;
|
|
410
553
|
const DELAY_CLOSE_TIME = 60000; // amount of time to wait before closing the connection if we haven't any activity and there are no subscriptions
|
|
411
554
|
let delayedClose: NodeJS.Timeout;
|
|
@@ -413,41 +556,78 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
413
556
|
// track bytes read and written so we can verify if a connection is really dead on pings
|
|
414
557
|
let bytesRead = 0;
|
|
415
558
|
let bytesWritten = 0;
|
|
559
|
+
// wall-clock time of the last observed socket activity (bytes moved in either direction); the
|
|
560
|
+
// keep-alive timeout is measured from this so a legitimately slow-but-progressing transfer stays
|
|
561
|
+
// alive while a truly idle/dead peer is still terminated.
|
|
562
|
+
let lastByteActivity = performance.now();
|
|
563
|
+
// Multiple independent conditions can ask to pause receive on this WS (commit backlog, consumer
|
|
564
|
+
// queue full, blob write backpressure). We refcount the reasons so that resuming one does not race
|
|
565
|
+
// ahead of another that still wants the WS paused. Declared before the ping setup below because the
|
|
566
|
+
// immediate sendPing() reads it.
|
|
567
|
+
let pauseReasons = 0;
|
|
416
568
|
const blobTimeout = env.get(CONFIG_PARAMS.REPLICATION_BLOBTIMEOUT) ?? 120000;
|
|
417
569
|
const blobsInFlight = new Map();
|
|
418
570
|
const outstandingBlobsToFinish: Promise<void>[] = [];
|
|
419
571
|
let outstandingBlobsBeingSent = 0;
|
|
420
572
|
let blobSentCallback: (v?: any) => void;
|
|
573
|
+
// Refresh the keep-alive liveness clock from observed socket byte movement. If the underlying
|
|
574
|
+
// _socket isn't observable (test mocks, pre-connect, or a change in the ws library internals),
|
|
575
|
+
// bytesRead/bytesWritten read as undefined; we can't measure activity, so treat the connection as
|
|
576
|
+
// live rather than let the keep-alive falsely terminate a healthy peer.
|
|
577
|
+
function noteByteActivity(): void {
|
|
578
|
+
const read = ws._socket?.bytesRead;
|
|
579
|
+
const written = ws._socket?.bytesWritten;
|
|
580
|
+
if (read === undefined || written === undefined || read !== bytesRead || written !== bytesWritten) {
|
|
581
|
+
lastByteActivity = performance.now();
|
|
582
|
+
}
|
|
583
|
+
}
|
|
421
584
|
if (options.url) {
|
|
422
585
|
const sendPing = () => {
|
|
423
|
-
//
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
bytesRead = ws._socket?.bytesRead;
|
|
430
|
-
bytesWritten = ws._socket?.bytesWritten;
|
|
586
|
+
// Note any socket activity since the last interval (incoming pong/data or our send buffer
|
|
587
|
+
// draining as the peer consumes) — either proves the peer is still alive.
|
|
588
|
+
noteByteActivity();
|
|
589
|
+
if (shouldTerminateIdlePing(performance.now() - lastByteActivity, PING_TIMEOUT, pauseReasons)) {
|
|
590
|
+
ws.terminate(); // no socket activity within the timeout — peer is gone
|
|
591
|
+
return;
|
|
431
592
|
}
|
|
593
|
+
// While paused for receiver backpressure, keep our own liveness fresh: the stall is local and
|
|
594
|
+
// self-clearing (it doesn't depend on the peer), so we must not time the peer out for it.
|
|
595
|
+
if (pauseReasons > 0) lastByteActivity = performance.now();
|
|
596
|
+
// Always send the keep-alive ping. ws.pause() only stops reads, not writes, and the accepted
|
|
597
|
+
// peer relies on our pings to keep its own receive timer alive even when it has no data to send
|
|
598
|
+
// us. Record byte counts AFTER the ping so the ping's own bytes aren't later mistaken for peer
|
|
599
|
+
// activity.
|
|
600
|
+
lastPingTime = performance.now();
|
|
601
|
+
ws.ping();
|
|
602
|
+
bytesRead = ws._socket?.bytesRead;
|
|
603
|
+
bytesWritten = ws._socket?.bytesWritten;
|
|
432
604
|
};
|
|
433
605
|
sendPingInterval = setInterval(sendPing, PING_INTERVAL).unref();
|
|
434
606
|
sendPing(); // send the first ping immediately so we can measure latency
|
|
435
|
-
} else {
|
|
436
|
-
resetPingTimer();
|
|
437
607
|
}
|
|
608
|
+
// Both client and server arm the receive watchdog. On the client this is independent of the
|
|
609
|
+
// sendPing tick above: if that tick is missed or its ws.terminate() does not propagate a
|
|
610
|
+
// 'close' event, the watchdog forces the reconnect path. See harper-pro#233 for the failure
|
|
611
|
+
// modes observed in the field.
|
|
612
|
+
receiveWatchdog = createReceiveWatchdog({
|
|
613
|
+
intervalMs: RECEIVE_SILENCE_THRESHOLD_MS,
|
|
614
|
+
getBytesRead: () => ws._socket?.bytesRead ?? 0,
|
|
615
|
+
onSilence: () => {
|
|
616
|
+
// Warn-level: if the active sendPing was healthy this watchdog should not have fired,
|
|
617
|
+
// so it is a signal that something is wrong upstream (event-loop stall, keepalive timer
|
|
618
|
+
// misbehaving, peer accepting bytes but not progressing the protocol). Surface it so
|
|
619
|
+
// operators have something to grep for.
|
|
620
|
+
const dbContext = databaseName ? ` (db: "${databaseName}")` : '';
|
|
621
|
+
const direction = options.url ? 'no activity from' : 'no ping from';
|
|
622
|
+
logger.warn?.(
|
|
623
|
+
`Receive watchdog: ${direction} ${remoteNodeName}${dbContext} for ${RECEIVE_SILENCE_THRESHOLD_MS}ms — terminating connection and reconnecting`
|
|
624
|
+
);
|
|
625
|
+
ws.terminate();
|
|
626
|
+
},
|
|
627
|
+
});
|
|
628
|
+
const resetPingTimer = receiveWatchdog.reset;
|
|
629
|
+
resetPingTimer();
|
|
438
630
|
ws._socket?.setMaxListeners(200); // we should allow a lot of drain listeners for concurrent blob streams
|
|
439
|
-
function resetPingTimer() {
|
|
440
|
-
clearTimeout(receivePingTimer);
|
|
441
|
-
bytesRead = ws._socket?.bytesRead;
|
|
442
|
-
bytesWritten = ws._socket?.bytesWritten;
|
|
443
|
-
receivePingTimer = setTimeout(() => {
|
|
444
|
-
// double check to make sure we aren't just waiting for other data to flow
|
|
445
|
-
if (bytesRead === ws._socket?.bytesRead && bytesWritten === ws._socket?.bytesWritten) {
|
|
446
|
-
logger.warn?.(`Timeout waiting for ping from ${remoteNodeName}, terminating connection and reconnecting`);
|
|
447
|
-
ws.terminate();
|
|
448
|
-
}
|
|
449
|
-
}, PING_INTERVAL * 2).unref();
|
|
450
|
-
}
|
|
451
631
|
let ratioOfBackPressureTime = 0;
|
|
452
632
|
let lastBackPressureCheck = 0;
|
|
453
633
|
let isPausedForBackPressure = false;
|
|
@@ -489,23 +669,32 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
489
669
|
const MAX_OUTSTANDING_BLOBS_BEING_SENT = env.get(CONFIG_PARAMS.REPLICATION_BLOBCONCURRENCY) ?? 5;
|
|
490
670
|
let outstandingCommits = 0;
|
|
491
671
|
let lastStructureLength = 0;
|
|
492
|
-
// Multiple independent conditions can ask to pause receive on this WS (commit backlog,
|
|
493
|
-
// consumer queue full, blob write backpressure). We refcount the reasons so that resuming
|
|
494
|
-
// one does not race ahead of another that still wants the WS paused.
|
|
495
|
-
let pauseReasons = 0;
|
|
496
672
|
let commitBacklogPaused = false;
|
|
497
673
|
function addPauseReason(): void {
|
|
498
|
-
if (pauseReasons === 0)
|
|
674
|
+
if (pauseReasons === 0) {
|
|
675
|
+
ws.pause();
|
|
676
|
+
// Suspend the receive watchdog while the socket is intentionally paused — `bytesRead`
|
|
677
|
+
// is frozen by `ws.pause()` so the byte check cannot tell legitimate backpressure
|
|
678
|
+
// from peer silence, and firing here would terminate a healthy mid-ingest connection.
|
|
679
|
+
receiveWatchdog?.stop();
|
|
680
|
+
}
|
|
499
681
|
pauseReasons++;
|
|
500
682
|
}
|
|
501
683
|
function removePauseReason(): void {
|
|
502
684
|
if (pauseReasons === 0) return;
|
|
503
685
|
pauseReasons--;
|
|
504
|
-
if (pauseReasons === 0)
|
|
686
|
+
if (pauseReasons === 0) {
|
|
687
|
+
ws.resume();
|
|
688
|
+
// Restart the silence window from the resume point — we deliberately do not penalize
|
|
689
|
+
// the connection for the time it spent paused.
|
|
690
|
+
receiveWatchdog?.reset();
|
|
691
|
+
}
|
|
505
692
|
}
|
|
506
693
|
let subscriptionRequest, auditSubscription;
|
|
507
694
|
let nodeSubscriptions;
|
|
508
695
|
let excludedNodes: string[]; // list of nodes to exclude from this subscription
|
|
696
|
+
// undefined = not yet computed; null = computed, no exclusions; Set = tables to drop on receive
|
|
697
|
+
let receiveExcludedTables: Set<string> | null | undefined;
|
|
509
698
|
let remoteShortIdToLocalId: Map<number, number>;
|
|
510
699
|
let subscribedNodeIds: Array<boolean | { startTime: number; endTime?: number }> | undefined; // map of node IDs to their subscription time ranges
|
|
511
700
|
// Serialize message handling so that async backpressure inside onWSMessage doesn't allow
|
|
@@ -515,6 +704,9 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
515
704
|
let messageProcessing: Promise<void> = Promise.resolve();
|
|
516
705
|
let wsClosed = false;
|
|
517
706
|
ws.on('message', (body: Buffer) => {
|
|
707
|
+
// Reset the receive watchdog synchronously on every frame — async processing below may
|
|
708
|
+
// take a long time and we want a single late frame to count as proof of life immediately.
|
|
709
|
+
resetPingTimer();
|
|
518
710
|
messageProcessing = messageProcessing.then(
|
|
519
711
|
() => (wsClosed ? undefined : onWSMessage(body)),
|
|
520
712
|
() => (wsClosed ? undefined : onWSMessage(body))
|
|
@@ -554,7 +746,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
554
746
|
// of an array that begins with the command code
|
|
555
747
|
lastMessageTime = performance.now();
|
|
556
748
|
try {
|
|
557
|
-
const decoder = (body.dataView = new Decoder(body.buffer, body.byteOffset, body.byteLength));
|
|
749
|
+
const decoder = ((body as any).dataView = new Decoder(body.buffer, body.byteOffset, body.byteLength));
|
|
558
750
|
if (body[0] > 127) {
|
|
559
751
|
// not a transaction, special message
|
|
560
752
|
const message = decode(body);
|
|
@@ -720,13 +912,12 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
720
912
|
// of the table id to the decoder so we can decode the binary data for each table.
|
|
721
913
|
tableDecoders[tableId] = {
|
|
722
914
|
name: tableName,
|
|
723
|
-
decoder: new
|
|
915
|
+
decoder: new StructonPackr({
|
|
724
916
|
useBigIntExtension: true,
|
|
725
|
-
randomAccessStructure: true,
|
|
726
917
|
freezeData: true,
|
|
727
918
|
typedStructs: data.typedStructs,
|
|
728
919
|
structures: data.structures,
|
|
729
|
-
}),
|
|
920
|
+
} as any),
|
|
730
921
|
getEntry(id) {
|
|
731
922
|
return table.primaryStore.getEntry(id);
|
|
732
923
|
},
|
|
@@ -765,7 +956,21 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
765
956
|
'replication',
|
|
766
957
|
'committed-update'
|
|
767
958
|
);
|
|
768
|
-
getSharedStatus().buffer.notify();
|
|
959
|
+
(getSharedStatus().buffer as any).notify();
|
|
960
|
+
break;
|
|
961
|
+
case COPY_START:
|
|
962
|
+
// the leader is (re)starting a bulk copy; track a resume cursor for it
|
|
963
|
+
inCopyMode = true;
|
|
964
|
+
copyModeStartTime = data; // copyStartTime anchor chosen by the leader
|
|
965
|
+
copyFromNodeId = getIdOfRemoteNode(remoteNodeName, auditStore);
|
|
966
|
+
logger.debug?.(connectionId, 'bulk copy starting from', remoteNodeName, new Date(copyModeStartTime));
|
|
967
|
+
break;
|
|
968
|
+
case COPY_COMPLETE:
|
|
969
|
+
// Copy signalled complete. Stay in copy mode so batches still committing keep advancing the
|
|
970
|
+
// cursor; maybeFinishCopy exits copy mode and clears the cursor once those commits drain.
|
|
971
|
+
copyCompleteReceived = true;
|
|
972
|
+
maybeFinishCopy();
|
|
973
|
+
logger.debug?.(connectionId, 'bulk copy complete from', remoteNodeName);
|
|
769
974
|
break;
|
|
770
975
|
case SEQUENCE_ID_UPDATE:
|
|
771
976
|
// we need to record the sequence number that the remote node has received
|
|
@@ -833,20 +1038,36 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
833
1038
|
);
|
|
834
1039
|
} else stream.end(blobBody);
|
|
835
1040
|
if (stream.connectedToBlob) blobsInFlight.delete(fileId);
|
|
1041
|
+
} else if (stream.destroyed || stream.writableEnded) {
|
|
1042
|
+
// The stream was already torn down before this mid-blob chunk arrived —
|
|
1043
|
+
// typically because saveBlob's pipeline failed (e.g. ENOENT on
|
|
1044
|
+
// createWriteStream) and destroyed the PassThrough source, firing 'close'.
|
|
1045
|
+
// We must NOT fall into the backpressure branch below: writing to a dead
|
|
1046
|
+
// stream returns false, and pausing to wait for a 'drain'/'close' that has
|
|
1047
|
+
// already fired strands the pause reason forever, wedging the entire
|
|
1048
|
+
// receive loop (observed in prod: receiver goes silent, sender stuck
|
|
1049
|
+
// reconnecting, replication never recovers). Drop the orphaned chunk and
|
|
1050
|
+
// forget the stream instead.
|
|
1051
|
+
blobsInFlight.delete(fileId);
|
|
836
1052
|
} else if (!stream.write(blobBody)) {
|
|
837
1053
|
// The PassThrough's internal queue is over its HWM, meaning the downstream
|
|
838
1054
|
// file write (via pipeline in saveBlob) can't keep up. Pause the WS until the
|
|
839
1055
|
// stream drains so blob chunks don't accumulate in memory faster than they
|
|
840
|
-
// can be flushed to disk.
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
1056
|
+
// can be flushed to disk.
|
|
1057
|
+
if (stream.destroyed || stream.writableEnded) {
|
|
1058
|
+
// write() itself may have torn the stream down (e.g. a late error). If so,
|
|
1059
|
+
// 'drain'/'close' won't arrive — skip pausing rather than strand the reason.
|
|
1060
|
+
blobsInFlight.delete(fileId);
|
|
1061
|
+
} else {
|
|
1062
|
+
addPauseReason();
|
|
1063
|
+
const release = () => {
|
|
1064
|
+
stream.off('drain', release);
|
|
1065
|
+
stream.off('close', release);
|
|
1066
|
+
removePauseReason();
|
|
1067
|
+
};
|
|
1068
|
+
stream.on('drain', release);
|
|
1069
|
+
stream.on('close', release);
|
|
1070
|
+
}
|
|
850
1071
|
}
|
|
851
1072
|
} catch (error) {
|
|
852
1073
|
logger.error?.(
|
|
@@ -1097,6 +1318,11 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1097
1318
|
let tableById;
|
|
1098
1319
|
let currentSequenceId = Infinity; // the last sequence number in the audit log that we have processed, set this with a finite number from the subscriptions
|
|
1099
1320
|
let sentSequenceId; // the last sequence number we have sent
|
|
1321
|
+
// Tables excluded from outgoing replication to this peer+database (from sendsTo config)
|
|
1322
|
+
const sendExcludedTables =
|
|
1323
|
+
authorization?.replicates && typeof authorization.replicates === 'object'
|
|
1324
|
+
? getExcludedTablesForRouteEntries(authorization.replicates.sendsTo, remoteNodeName, databaseName)
|
|
1325
|
+
: null;
|
|
1100
1326
|
const sendAuditRecord = (auditRecord, localTime) => {
|
|
1101
1327
|
if (auditRecord.type === 'end_txn') {
|
|
1102
1328
|
if (currentTransaction.txnTime) {
|
|
@@ -1127,6 +1353,9 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1127
1353
|
}
|
|
1128
1354
|
}
|
|
1129
1355
|
const table = tableEntry.table;
|
|
1356
|
+
if (sendExcludedTables?.has(table.tableName)) {
|
|
1357
|
+
return skipAuditRecord();
|
|
1358
|
+
}
|
|
1130
1359
|
const primaryStore = table.primaryStore;
|
|
1131
1360
|
const encoder = primaryStore.encoder;
|
|
1132
1361
|
// Force a reload the first time this connection touches each table:
|
|
@@ -1154,7 +1383,9 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1154
1383
|
const matchesSubscription =
|
|
1155
1384
|
(excludedNodes && timeRange === undefined) ||
|
|
1156
1385
|
// if it is in the list, we check the timestamps to verify it matches
|
|
1157
|
-
(timeRange &&
|
|
1386
|
+
(timeRange &&
|
|
1387
|
+
(timeRange as any).startTime < localTime &&
|
|
1388
|
+
(!(timeRange as any).endTime || (timeRange as any).endTime > localTime));
|
|
1158
1389
|
if (!matchesSubscription) {
|
|
1159
1390
|
if (DEBUG_MODE)
|
|
1160
1391
|
logger.trace?.(
|
|
@@ -1386,8 +1617,11 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1386
1617
|
subscriptionToHdbNodes?.end();
|
|
1387
1618
|
});
|
|
1388
1619
|
// find the earliest start time of the subscriptions
|
|
1389
|
-
|
|
1390
|
-
|
|
1620
|
+
let copyResume: { copyStartTime: number; currentTable: string; afterKey: any } | undefined;
|
|
1621
|
+
for (const subscription of nodeSubscriptions) {
|
|
1622
|
+
if (subscription.startTime < currentSequenceId) currentSequenceId = subscription.startTime;
|
|
1623
|
+
// a follower resuming an interrupted bulk copy sends back where it left off
|
|
1624
|
+
if (subscription.copyResume) copyResume = subscription.copyResume;
|
|
1391
1625
|
}
|
|
1392
1626
|
|
|
1393
1627
|
// wait for internal subscription, might be waiting for a table to be registered
|
|
@@ -1457,16 +1691,51 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1457
1691
|
isFirst = false;
|
|
1458
1692
|
if (currentSequenceId === 0) {
|
|
1459
1693
|
logger.info?.('Replicating all tables to', remoteNodeName);
|
|
1460
|
-
|
|
1694
|
+
// Capture the resume point BEFORE iterating. The bulk copy walks the primary store in
|
|
1695
|
+
// key order (snapshot: false), but the follower resumes replication from the audit log in
|
|
1696
|
+
// time order. Using copyStartTime — not max(localTime) of the copied records — guarantees
|
|
1697
|
+
// the post-copy audit replay re-delivers every write committed during the copy, including
|
|
1698
|
+
// ones to keys we already passed; resuming from max(localTime) would skip those (data loss).
|
|
1699
|
+
// When the follower is resuming an interrupted copy, keep the original copy start time so
|
|
1700
|
+
// the post-copy resume point stays anchored to when the copy first began (see safety note).
|
|
1701
|
+
const copyStartTime = copyResume?.copyStartTime ?? Date.now();
|
|
1461
1702
|
const nodeId = getThisNodeId(auditStore);
|
|
1703
|
+
// Tell the follower a bulk copy is starting and its anchor time, so it tracks a resume cursor.
|
|
1704
|
+
ws.send(encode([COPY_START, copyStartTime]));
|
|
1705
|
+
let recordsSinceCheckpoint = 0;
|
|
1706
|
+
// If resuming, the follower already committed every table before currentTable (records commit
|
|
1707
|
+
// in stable iteration order), so skip to currentTable and continue after its last committed key.
|
|
1708
|
+
let reachedResumeTable = !copyResume;
|
|
1709
|
+
// currentTable must be one the loop below will actually visit (present in `tables` AND passing
|
|
1710
|
+
// the same replication filter); otherwise the skip loop never reaches it and would omit every
|
|
1711
|
+
// later table. Mirror the loop's own check so a dropped/unreplicated cursor table forces a restart.
|
|
1712
|
+
if (copyResume && !tableToTableEntry(tables[copyResume.currentTable])) {
|
|
1713
|
+
// cursor table is gone or no longer replicated — the skip loop would never reach it and
|
|
1714
|
+
// would omit every later table, so recopy from scratch (idempotent puts, copyStartTime reset)
|
|
1715
|
+
logger.warn?.(
|
|
1716
|
+
'Copy-resume table missing or unreplicated, restarting full copy',
|
|
1717
|
+
copyResume.currentTable
|
|
1718
|
+
);
|
|
1719
|
+
copyResume = undefined;
|
|
1720
|
+
reachedResumeTable = true;
|
|
1721
|
+
}
|
|
1722
|
+
const resumeCurrentTable = copyResume?.currentTable;
|
|
1723
|
+
const resumeAfterKey = copyResume?.afterKey;
|
|
1462
1724
|
for (const tableName in tables) {
|
|
1463
|
-
if (!tableToTableEntry(tableName)) continue; // if we aren't replicating this table, skip it
|
|
1464
1725
|
const table = tables[tableName];
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
}
|
|
1726
|
+
if (!tableToTableEntry(table)) continue; // if we aren't replicating this table, skip it
|
|
1727
|
+
if (!reachedResumeTable) {
|
|
1728
|
+
if (tableName !== resumeCurrentTable) continue; // already committed on the follower
|
|
1729
|
+
reachedResumeTable = true;
|
|
1730
|
+
}
|
|
1731
|
+
const rangeOptions: any = { snapshot: false, versions: true };
|
|
1732
|
+
// values: false, // TODO: eventually, we don't want to decode, we want to use fast binary transfer
|
|
1733
|
+
if (tableName === resumeCurrentTable) {
|
|
1734
|
+
// resume this table after the last key the follower committed
|
|
1735
|
+
rangeOptions.start = resumeAfterKey;
|
|
1736
|
+
rangeOptions.exclusiveStart = true;
|
|
1737
|
+
}
|
|
1738
|
+
for (const entry of table.primaryStore.getRange(rangeOptions)) {
|
|
1470
1739
|
if (closed) return;
|
|
1471
1740
|
logger.trace?.(
|
|
1472
1741
|
connectionId,
|
|
@@ -1476,7 +1745,6 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1476
1745
|
entry.key,
|
|
1477
1746
|
entry.localTime
|
|
1478
1747
|
);
|
|
1479
|
-
lastSequenceId = Math.max(entry.localTime ?? 1, lastSequenceId);
|
|
1480
1748
|
getSharedStatus()[SENDING_TIME_POSITION] = 1;
|
|
1481
1749
|
const encoded = createAuditEntry({
|
|
1482
1750
|
version: entry.version,
|
|
@@ -1496,7 +1764,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1496
1764
|
residencyId: entry.residencyId,
|
|
1497
1765
|
previousResidencyId: null,
|
|
1498
1766
|
expiresAt: entry.expiresAt,
|
|
1499
|
-
});
|
|
1767
|
+
} as any);
|
|
1500
1768
|
await sendAuditRecord(
|
|
1501
1769
|
{
|
|
1502
1770
|
// make it look like an audit record
|
|
@@ -1521,26 +1789,46 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1521
1789
|
encoded.length,
|
|
1522
1790
|
encoded.slice(0, 10)
|
|
1523
1791
|
);
|
|
1792
|
+
// Periodically flush the accumulated records as a message so the follower commits this
|
|
1793
|
+
// batch and advances its resume cursor. This is a plain flush with NO sequence update:
|
|
1794
|
+
// emitting an end_txn here would advance the follower's received-version watermark to
|
|
1795
|
+
// copyStartTime mid-copy, which monitorSync could read as "caught up" and mark the clone
|
|
1796
|
+
// Available/cloned with rows still uncopied. (Records with differing versions already flush
|
|
1797
|
+
// naturally above; this also bounds same-version bulk data into committable batches.) The
|
|
1798
|
+
// watermark is only advanced to copyStartTime by the single end_txn after the whole copy.
|
|
1799
|
+
if (++recordsSinceCheckpoint >= COPY_CHECKPOINT_RECORDS && position - encodingStart > 8) {
|
|
1800
|
+
recordsSinceCheckpoint = 0;
|
|
1801
|
+
sendQueuedData();
|
|
1802
|
+
encodingStart = position;
|
|
1803
|
+
currentTransaction.txnTime = 0;
|
|
1804
|
+
}
|
|
1524
1805
|
}
|
|
1525
1806
|
logger.info?.('Finished copy table', tableName, remoteNodeName);
|
|
1526
1807
|
}
|
|
1527
|
-
currentSequenceId =
|
|
1808
|
+
currentSequenceId = copyStartTime;
|
|
1528
1809
|
if (!currentTransaction.txnTime) {
|
|
1529
|
-
//
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
// if we have any queued transactions to send, send them now
|
|
1535
|
-
sendAuditRecord(
|
|
1536
|
-
{
|
|
1537
|
-
type: 'end_txn',
|
|
1538
|
-
},
|
|
1539
|
-
currentSequenceId
|
|
1540
|
-
);
|
|
1810
|
+
// no records pending (none sent, or the last batch landed on a checkpoint flush):
|
|
1811
|
+
// force a txn so the end_txn below still carries the sequence update
|
|
1812
|
+
currentTransaction.txnTime = copyStartTime;
|
|
1813
|
+
encodingStart = position;
|
|
1814
|
+
writeFloat64(copyStartTime);
|
|
1541
1815
|
}
|
|
1816
|
+
// ALWAYS emit the final end_txn at copyStartTime. It carries the REMOTE_SEQUENCE_UPDATE
|
|
1817
|
+
// that advances the follower's seqId and received-version watermark to copyStartTime —
|
|
1818
|
+
// the sole signal that the copy is synced (per-record watermark advance is suppressed
|
|
1819
|
+
// during the copy). Skipping it when the last rows landed exactly on a checkpoint flush
|
|
1820
|
+
// would leave the clone unable to ever reach Available.
|
|
1821
|
+
sendAuditRecord(
|
|
1822
|
+
{
|
|
1823
|
+
type: 'end_txn',
|
|
1824
|
+
},
|
|
1825
|
+
currentSequenceId
|
|
1826
|
+
);
|
|
1827
|
+
// The full copy is done — tell the follower to clear its resume cursor and fall back to
|
|
1828
|
+
// normal audit-log replication from the persisted seqId (which is copyStartTime).
|
|
1829
|
+
ws.send(encode([COPY_COMPLETE]));
|
|
1542
1830
|
getSharedStatus()[SENDING_TIME_POSITION] = 0;
|
|
1543
|
-
currentSequenceId =
|
|
1831
|
+
currentSequenceId = copyStartTime;
|
|
1544
1832
|
}
|
|
1545
1833
|
}
|
|
1546
1834
|
const logName = subscribedNodeName === getThisNodeName() ? 'local' : subscribedNodeName;
|
|
@@ -1599,6 +1887,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1599
1887
|
let beginTxn = true;
|
|
1600
1888
|
let event; // could also get txnTime from decoder.getFloat64(0);
|
|
1601
1889
|
let sequenceIdReceived;
|
|
1890
|
+
let lastYieldTime = performance.now();
|
|
1602
1891
|
do {
|
|
1603
1892
|
getSharedStatus();
|
|
1604
1893
|
const eventLength = decoder.readInt();
|
|
@@ -1627,6 +1916,30 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1627
1916
|
if (!tableDecoder) {
|
|
1628
1917
|
logger.error?.(`No table found with an id of ${auditRecord.tableId}`);
|
|
1629
1918
|
}
|
|
1919
|
+
// Lazily compute receive-side exclusions once remoteNodeName is known.
|
|
1920
|
+
// Prefer routeReplicates from the subscriber-side connection; fall back to
|
|
1921
|
+
// authorization.replicates when this is the server-side handler.
|
|
1922
|
+
if (receiveExcludedTables === undefined) {
|
|
1923
|
+
const firstNode = options.connection?.nodeSubscriptions?.[0];
|
|
1924
|
+
const receivesFromEntries =
|
|
1925
|
+
firstNode?.routeReplicates?.receivesFrom ??
|
|
1926
|
+
(authorization?.replicates && typeof authorization.replicates === 'object'
|
|
1927
|
+
? authorization.replicates.receivesFrom
|
|
1928
|
+
: undefined);
|
|
1929
|
+
receiveExcludedTables =
|
|
1930
|
+
getExcludedTablesForRouteEntries(receivesFromEntries, remoteNodeName, databaseName) ?? null;
|
|
1931
|
+
}
|
|
1932
|
+
if (tableDecoder && receiveExcludedTables?.has(tableDecoder.name)) {
|
|
1933
|
+
logger.trace?.(
|
|
1934
|
+
connectionId,
|
|
1935
|
+
'dropping incoming replication for excluded table',
|
|
1936
|
+
databaseName + '.' + tableDecoder.name,
|
|
1937
|
+
'from',
|
|
1938
|
+
remoteNodeName
|
|
1939
|
+
);
|
|
1940
|
+
decoder.position = start + eventLength;
|
|
1941
|
+
continue;
|
|
1942
|
+
}
|
|
1630
1943
|
let residencyList;
|
|
1631
1944
|
if (auditRecord.residencyId) {
|
|
1632
1945
|
residencyList = receivedResidencyLists[auditRecord.residencyId];
|
|
@@ -1640,6 +1953,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1640
1953
|
}
|
|
1641
1954
|
const id = auditRecord.recordId;
|
|
1642
1955
|
event = undefined; // reset before each decode attempt
|
|
1956
|
+
let receivedBlobs: any[] | undefined;
|
|
1643
1957
|
try {
|
|
1644
1958
|
decodeBlobsWithWrites(
|
|
1645
1959
|
() => {
|
|
@@ -1658,7 +1972,11 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1658
1972
|
};
|
|
1659
1973
|
},
|
|
1660
1974
|
auditStore?.rootStore,
|
|
1661
|
-
(blob) =>
|
|
1975
|
+
(blob) => {
|
|
1976
|
+
const localBlob = receiveBlobs(blob, id);
|
|
1977
|
+
(receivedBlobs ??= []).push(localBlob);
|
|
1978
|
+
return localBlob;
|
|
1979
|
+
}
|
|
1662
1980
|
);
|
|
1663
1981
|
} catch (error) {
|
|
1664
1982
|
logger.error?.(
|
|
@@ -1671,11 +1989,23 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1671
1989
|
error
|
|
1672
1990
|
);
|
|
1673
1991
|
}
|
|
1674
|
-
|
|
1675
|
-
//
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1992
|
+
if (!event && receivedBlobs) {
|
|
1993
|
+
// decode failed mid-message; the blobs that were already accepted will never be referenced. Give in-flight reads
|
|
1994
|
+
// a window to complete, then unlink the files. (mirrors the pattern at the relocate path above.)
|
|
1995
|
+
setTimeout(() => receivedBlobs.forEach(deleteBlob), 60000).unref();
|
|
1996
|
+
}
|
|
1997
|
+
// During a bulk copy, do NOT advance the received-version watermark per copied record:
|
|
1998
|
+
// records arrive in primary-key order carrying their original (possibly newest) versions, so a
|
|
1999
|
+
// single record at the leader's latest timestamp would otherwise let checkSyncStatus mark the
|
|
2000
|
+
// clone Available with rows still uncopied. The watermark is advanced to copyStartTime by the
|
|
2001
|
+
// single end_txn after the whole copy (the REMOTE_SEQUENCE_UPDATE branch above).
|
|
2002
|
+
if (!inCopyMode) {
|
|
2003
|
+
replicationSharedStatus[RECEIVED_VERSION_POSITION] = Math.max(
|
|
2004
|
+
// ensure monotonicity
|
|
2005
|
+
auditRecord.version,
|
|
2006
|
+
replicationSharedStatus[RECEIVED_VERSION_POSITION]
|
|
2007
|
+
);
|
|
2008
|
+
}
|
|
1679
2009
|
replicationSharedStatus[RECEIVED_TIME_POSITION] = Date.now();
|
|
1680
2010
|
replicationSharedStatus[RECEIVING_STATUS_POSITION] = RECEIVING_STATUS_RECEIVING;
|
|
1681
2011
|
|
|
@@ -1706,6 +2036,13 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1706
2036
|
} finally {
|
|
1707
2037
|
removePauseReason();
|
|
1708
2038
|
}
|
|
2039
|
+
lastYieldTime = performance.now();
|
|
2040
|
+
} else if (performance.now() - lastYieldTime >= RECEIVE_YIELD_INTERVAL) {
|
|
2041
|
+
// The high-water-mark pause only fires under heap pressure. When the consumer keeps
|
|
2042
|
+
// up, yield on a time budget anyway so a large message doesn't decode in one
|
|
2043
|
+
// synchronous turn and stall ping responses (see RECEIVE_YIELD_INTERVAL).
|
|
2044
|
+
await new Promise(setImmediate);
|
|
2045
|
+
lastYieldTime = performance.now();
|
|
1709
2046
|
}
|
|
1710
2047
|
}
|
|
1711
2048
|
decoder.position = start + eventLength;
|
|
@@ -1727,6 +2064,9 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1727
2064
|
`Commit backlog causing replication back-pressure, requesting that ${remoteNodeName} pause replication`
|
|
1728
2065
|
);
|
|
1729
2066
|
}
|
|
2067
|
+
// Is this a bulk-copy frame? Only frames received before COPY_COMPLETE are part of the
|
|
2068
|
+
// primary-key copy; later audit-replay frames must not be recorded as the resume cursor.
|
|
2069
|
+
const isCopyFrame = inCopyMode && !copyCompleteReceived;
|
|
1730
2070
|
tableSubscriptionToReplicator.send({
|
|
1731
2071
|
type: 'end_txn',
|
|
1732
2072
|
localTime: lastSequenceIdReceived,
|
|
@@ -1754,6 +2094,18 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1754
2094
|
// we correctly resend the blobs)
|
|
1755
2095
|
if (outstandingBlobsToFinish.length > 0) await Promise.all(outstandingBlobsToFinish);
|
|
1756
2096
|
logger.trace?.('All blobs finished');
|
|
2097
|
+
// Persist/clear the resume cursor only AFTER this batch's blobs are durable too. The cursor
|
|
2098
|
+
// means "fully copied through this key"; advancing it before blob writes finish would let a
|
|
2099
|
+
// crash skip re-requesting an unfinished blob, leaving the record pointing at missing data.
|
|
2100
|
+
if (isCopyFrame && event && copyFromNodeId !== undefined) {
|
|
2101
|
+
tableSubscriptionToReplicator?.dbisDB?.put([Symbol.for('copyCursor'), copyFromNodeId], {
|
|
2102
|
+
copyStartTime: copyModeStartTime,
|
|
2103
|
+
currentTable: event.table,
|
|
2104
|
+
afterKey: event.id,
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
// once the last copied batch (incl. its blobs) is durable, it's safe to drop the cursor
|
|
2108
|
+
maybeFinishCopy();
|
|
1757
2109
|
if (!lastSequenceIdCommitted && sequenceIdReceived) {
|
|
1758
2110
|
logger.trace?.(connectionId, 'queuing confirmation of a commit at', sequenceIdReceived);
|
|
1759
2111
|
setTimeout(() => {
|
|
@@ -1773,6 +2125,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1773
2125
|
}
|
|
1774
2126
|
}
|
|
1775
2127
|
ws.on('ping', resetPingTimer);
|
|
2128
|
+
ws.on('pong', resetPingTimer);
|
|
1776
2129
|
ws.on('pong', () => {
|
|
1777
2130
|
if (options.connection) {
|
|
1778
2131
|
// every pong we can use to update our connection information (and latency)
|
|
@@ -1797,7 +2150,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1797
2150
|
// cleanup
|
|
1798
2151
|
wsClosed = true;
|
|
1799
2152
|
clearInterval(sendPingInterval);
|
|
1800
|
-
|
|
2153
|
+
receiveWatchdog?.stop();
|
|
1801
2154
|
clearInterval(blobsTimer);
|
|
1802
2155
|
if (auditSubscription) auditSubscription.emit('close');
|
|
1803
2156
|
if (subscriptionRequest) subscriptionRequest.end();
|
|
@@ -1855,7 +2208,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1855
2208
|
])
|
|
1856
2209
|
);
|
|
1857
2210
|
}
|
|
1858
|
-
lastBuffer = buffer;
|
|
2211
|
+
lastBuffer = buffer as Buffer;
|
|
1859
2212
|
if (ws._socket.writableNeedDrain) {
|
|
1860
2213
|
logger.debug?.('draining', id);
|
|
1861
2214
|
await new Promise((resolve) => ws._socket.once('drain', resolve));
|
|
@@ -1911,7 +2264,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1911
2264
|
stream.connectedToBlob = true;
|
|
1912
2265
|
stream.lastChunk = Date.now();
|
|
1913
2266
|
stream.recordId = id;
|
|
1914
|
-
if (remoteBlob.size === undefined && stream.expectedSize) remoteBlob.size = stream.expectedSize;
|
|
2267
|
+
if (remoteBlob.size === undefined && stream.expectedSize) (remoteBlob as any).size = stream.expectedSize;
|
|
1915
2268
|
const localBlob = stream.blob ?? createBlob(stream, remoteBlob);
|
|
1916
2269
|
stream.blob = localBlob; // record the blob so we can reuse it if another request uses the same blob
|
|
1917
2270
|
|
|
@@ -1935,7 +2288,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
1935
2288
|
const index = outstandingBlobsToFinish.indexOf(tracked);
|
|
1936
2289
|
if (index > -1) outstandingBlobsToFinish.splice(index, 1);
|
|
1937
2290
|
});
|
|
1938
|
-
tracked.blobId = blobId;
|
|
2291
|
+
(tracked as any).blobId = blobId;
|
|
1939
2292
|
outstandingBlobsToFinish.push(tracked);
|
|
1940
2293
|
}
|
|
1941
2294
|
return localBlob;
|
|
@@ -2027,14 +2380,20 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
2027
2380
|
const nodeSubscriptions = options.connection?.nodeSubscriptions.map((node: any) => {
|
|
2028
2381
|
const tableSubs = [];
|
|
2029
2382
|
let { replicateByDefault } = node;
|
|
2383
|
+
// Tables excluded by this node's receivesFrom config for this peer+database
|
|
2384
|
+
const receiverExcludedTables = getExcludedTablesForRouteEntries(
|
|
2385
|
+
node.routeReplicates?.receivesFrom,
|
|
2386
|
+
node.name,
|
|
2387
|
+
databaseName
|
|
2388
|
+
);
|
|
2030
2389
|
if (node.subscriptions) {
|
|
2031
2390
|
// if the node has explicit subscriptions, we need to use that to determine subscriptions
|
|
2032
2391
|
for (const subscription of node.subscriptions) {
|
|
2033
2392
|
// if there is an explicit subscription listed
|
|
2034
2393
|
if (subscription.subscribe && (subscription.schema || subscription.database) === databaseName) {
|
|
2035
2394
|
const tableName = subscription.table;
|
|
2036
|
-
if (tables?.[tableName]?.replicate !== false)
|
|
2037
|
-
// if replication is enabled for this table
|
|
2395
|
+
if (tables?.[tableName]?.replicate !== false && !receiverExcludedTables?.has(tableName))
|
|
2396
|
+
// if replication is enabled for this table and not excluded
|
|
2038
2397
|
tableSubs.push(tableName);
|
|
2039
2398
|
}
|
|
2040
2399
|
}
|
|
@@ -2042,15 +2401,26 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
2042
2401
|
} else {
|
|
2043
2402
|
// note that if replicateByDefault is enabled, we are listing the *excluded* tables
|
|
2044
2403
|
for (const tableName in tables) {
|
|
2045
|
-
if (
|
|
2404
|
+
if (
|
|
2405
|
+
replicateByDefault
|
|
2406
|
+
? tables[tableName].replicate === false || receiverExcludedTables?.has(tableName)
|
|
2407
|
+
: tables[tableName].replicate && !receiverExcludedTables?.has(tableName)
|
|
2408
|
+
) {
|
|
2046
2409
|
tableSubs.push(tableName);
|
|
2047
2410
|
}
|
|
2048
2411
|
}
|
|
2049
2412
|
}
|
|
2050
2413
|
|
|
2051
2414
|
const nodeId = auditStore && getIdOfRemoteNode(node.name, auditStore);
|
|
2052
|
-
auditStore
|
|
2415
|
+
auditStore?.ensureLogExists?.(node.name);
|
|
2053
2416
|
const sequenceEntry = tableSubscriptionToReplicator?.dbisDB?.get([Symbol.for('seq'), nodeId]) ?? 1;
|
|
2417
|
+
// A persisted copy cursor means a bulk copy from this node was interrupted mid-stream. We must
|
|
2418
|
+
// resume that copy (not treat the persisted seqId as a normal start point — the un-copied table
|
|
2419
|
+
// data predates copyStartTime and would never be delivered by an audit-log resume).
|
|
2420
|
+
const copyCursor =
|
|
2421
|
+
nodeId === undefined
|
|
2422
|
+
? undefined
|
|
2423
|
+
: tableSubscriptionToReplicator?.dbisDB?.get([Symbol.for('copyCursor'), nodeId]);
|
|
2054
2424
|
// if we are connected directly to the node, we start from the last sequence number we received at the top level
|
|
2055
2425
|
let startTime = Math.max(
|
|
2056
2426
|
sequenceEntry?.seqId ?? 1,
|
|
@@ -2097,6 +2467,18 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
2097
2467
|
startTime = Date.now() - 60000;
|
|
2098
2468
|
}
|
|
2099
2469
|
}
|
|
2470
|
+
let copyResume;
|
|
2471
|
+
if (copyCursor) {
|
|
2472
|
+
startTime = 0; // request a copy; the cursor tells the leader where to resume
|
|
2473
|
+
copyResume = {
|
|
2474
|
+
copyStartTime: copyCursor.copyStartTime,
|
|
2475
|
+
currentTable: copyCursor.currentTable,
|
|
2476
|
+
afterKey: copyCursor.afterKey,
|
|
2477
|
+
};
|
|
2478
|
+
logger.warn?.(
|
|
2479
|
+
`Resuming interrupted copy of database ${databaseName} from ${getNodeURL(node)} at table ${copyCursor.currentTable}`
|
|
2480
|
+
);
|
|
2481
|
+
}
|
|
2100
2482
|
logger.trace?.(connectionId, 'defining subscription request', node.name, databaseName, new Date(startTime));
|
|
2101
2483
|
return {
|
|
2102
2484
|
name: node.name,
|
|
@@ -2105,6 +2487,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
2105
2487
|
startTime,
|
|
2106
2488
|
isLeader: node.isLeader,
|
|
2107
2489
|
endTime: node.endTime,
|
|
2490
|
+
copyResume, // present only when resuming an interrupted bulk copy
|
|
2108
2491
|
};
|
|
2109
2492
|
});
|
|
2110
2493
|
let excluded: string[];
|
|
@@ -2203,7 +2586,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
|
|
|
2203
2586
|
const thisNodeName = getThisNodeName();
|
|
2204
2587
|
if (thisNodeName === remoteNodeName) {
|
|
2205
2588
|
if (!thisNodeName) throw new Error('Node name not defined');
|
|
2206
|
-
else throw new Error('Should not connect to self'
|
|
2589
|
+
else throw new Error('Should not connect to self: ' + thisNodeName);
|
|
2207
2590
|
}
|
|
2208
2591
|
sendNodeDBName(thisNodeName, databaseName);
|
|
2209
2592
|
return true;
|