@harperfast/harper 5.3.0-alpha.1 → 5.3.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cliOperations.ts +12 -0
- package/bin/restart.ts +66 -6
- package/components/Application.ts +1134 -112
- package/components/DESIGN.md +421 -0
- package/components/OptionsWatcher.ts +368 -102
- package/components/Scope.ts +20 -1
- package/components/componentLoader.ts +43 -4
- package/components/deploymentOperations.ts +4 -1
- package/components/deploymentRecorder.ts +9 -2
- package/components/mcp/DESIGN.md +109 -0
- package/components/mcp/audit.ts +21 -17
- package/components/operations.js +284 -52
- package/components/operationsValidation.js +49 -2
- package/components/packageComponent.ts +25 -1
- package/components/requestRestart.ts +11 -0
- package/config/DESIGN.md +306 -0
- package/config/RootConfigWatcher.ts +191 -37
- package/config/configReadRetry.ts +62 -0
- package/config/configUtils.ts +78 -26
- package/config/parseConfigFile.ts +34 -0
- package/config/readConfigFileSync.ts +44 -0
- package/config/watcherArming.ts +59 -0
- package/config-root.schema.json +4 -0
- package/dataLayer/DESIGN.md +179 -0
- package/dataLayer/harperBridge/ResourceBridge.ts +28 -2
- package/dataLayer/restoreMarker.ts +92 -25
- package/dist/bin/cliOperations.js +13 -0
- package/dist/bin/cliOperations.js.map +1 -1
- package/dist/bin/restart.js +42 -6
- package/dist/bin/restart.js.map +1 -1
- package/dist/components/Application.d.ts +104 -9
- package/dist/components/Application.js +954 -102
- package/dist/components/Application.js.map +1 -1
- package/dist/components/OptionsWatcher.d.ts +4 -1
- package/dist/components/OptionsWatcher.js +378 -104
- package/dist/components/OptionsWatcher.js.map +1 -1
- package/dist/components/Scope.js +15 -1
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/componentLoader.js +35 -3
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/deploymentOperations.js +4 -1
- package/dist/components/deploymentOperations.js.map +1 -1
- package/dist/components/deploymentRecorder.d.ts +4 -2
- package/dist/components/deploymentRecorder.js +1 -0
- package/dist/components/deploymentRecorder.js.map +1 -1
- package/dist/components/mcp/audit.d.ts +2 -1
- package/dist/components/mcp/audit.js +21 -17
- package/dist/components/mcp/audit.js.map +1 -1
- package/dist/components/operations.d.ts +28 -0
- package/dist/components/operations.js +263 -54
- package/dist/components/operations.js.map +1 -1
- package/dist/components/operationsValidation.js +48 -2
- package/dist/components/operationsValidation.js.map +1 -1
- package/dist/components/packageComponent.js +24 -0
- package/dist/components/packageComponent.js.map +1 -1
- package/dist/components/requestRestart.d.ts +1 -0
- package/dist/components/requestRestart.js +7 -0
- package/dist/components/requestRestart.js.map +1 -1
- package/dist/config/RootConfigWatcher.d.ts +2 -0
- package/dist/config/RootConfigWatcher.js +189 -35
- package/dist/config/RootConfigWatcher.js.map +1 -1
- package/dist/config/configReadRetry.d.ts +8 -0
- package/dist/config/configReadRetry.js +62 -0
- package/dist/config/configReadRetry.js.map +1 -0
- package/dist/config/configUtils.d.ts +10 -9
- package/dist/config/configUtils.js +63 -27
- package/dist/config/configUtils.js.map +1 -1
- package/dist/config/parseConfigFile.d.ts +4 -0
- package/dist/config/parseConfigFile.js +35 -0
- package/dist/config/parseConfigFile.js.map +1 -0
- package/dist/config/readConfigFileSync.d.ts +1 -0
- package/dist/config/readConfigFileSync.js +47 -0
- package/dist/config/readConfigFileSync.js.map +1 -0
- package/dist/config/watcherArming.d.ts +15 -0
- package/dist/config/watcherArming.js +59 -0
- package/dist/config/watcherArming.js.map +1 -0
- package/dist/dataLayer/harperBridge/ResourceBridge.js +21 -2
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/dataLayer/restoreMarker.d.ts +21 -8
- package/dist/dataLayer/restoreMarker.js +94 -27
- package/dist/dataLayer/restoreMarker.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/json/systemSchema.json +3 -0
- package/dist/resources/DatabaseTransaction.d.ts +25 -0
- package/dist/resources/DatabaseTransaction.js +224 -18
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/LMDBTransaction.d.ts +2 -1
- package/dist/resources/LMDBTransaction.js +22 -3
- package/dist/resources/LMDBTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.js +22 -6
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
- package/dist/resources/RecordEncoder.d.ts +1 -1
- package/dist/resources/RecordEncoder.js +46 -10
- package/dist/resources/RecordEncoder.js.map +1 -1
- package/dist/resources/Resource.js +97 -13
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +8 -0
- package/dist/resources/RocksIndexStore.js +2 -1
- package/dist/resources/RocksIndexStore.js.map +1 -1
- package/dist/resources/RocksTransactionLogStore.d.ts +10 -0
- package/dist/resources/RocksTransactionLogStore.js +104 -33
- package/dist/resources/RocksTransactionLogStore.js.map +1 -1
- package/dist/resources/Table.d.ts +58 -7
- package/dist/resources/Table.js +1143 -348
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/write.d.ts +3 -0
- package/dist/resources/analytics/write.js +49 -16
- package/dist/resources/analytics/write.js.map +1 -1
- package/dist/resources/auditStore.d.ts +170 -0
- package/dist/resources/auditStore.js +457 -11
- package/dist/resources/auditStore.js.map +1 -1
- package/dist/resources/crdt.d.ts +10 -0
- package/dist/resources/crdt.js +22 -0
- package/dist/resources/crdt.js.map +1 -1
- package/dist/resources/dataLoader.js +3 -4
- package/dist/resources/dataLoader.js.map +1 -1
- package/dist/resources/databases.d.ts +16 -13
- package/dist/resources/databases.js +625 -177
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/derivedIndexRegistry.d.ts +5 -0
- package/dist/resources/derivedIndexRegistry.js +68 -0
- package/dist/resources/derivedIndexRegistry.js.map +1 -0
- package/dist/resources/derivedIndexRuntime.d.ts +215 -0
- package/dist/resources/derivedIndexRuntime.js +2027 -0
- package/dist/resources/derivedIndexRuntime.js.map +1 -0
- package/dist/resources/graphql.js +3 -2
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +102 -9
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +867 -39
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/indexes/fullTextDerivedIndex.d.ts +81 -0
- package/dist/resources/indexes/fullTextDerivedIndex.js +1004 -0
- package/dist/resources/indexes/fullTextDerivedIndex.js.map +1 -0
- package/dist/resources/indexes/fullTextNativeBinding.d.ts +78 -0
- package/dist/resources/indexes/fullTextNativeBinding.js +85 -0
- package/dist/resources/indexes/fullTextNativeBinding.js.map +1 -0
- package/dist/resources/indexes/hnswDerivedIndex.d.ts +67 -0
- package/dist/resources/indexes/hnswDerivedIndex.js +464 -0
- package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -0
- package/dist/resources/indexes/hnswPlaneBinding.d.ts +65 -0
- package/dist/resources/indexes/hnswPlaneBinding.js +91 -0
- package/dist/resources/indexes/hnswPlaneBinding.js.map +1 -0
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.d.ts +24 -0
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js +149 -0
- package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js.map +1 -0
- package/dist/resources/nodeIdMapping.d.ts +5 -0
- package/dist/resources/nodeIdMapping.js +49 -0
- package/dist/resources/nodeIdMapping.js.map +1 -1
- package/dist/resources/recordLock.d.ts +47 -4
- package/dist/resources/recordLock.js +138 -7
- package/dist/resources/recordLock.js.map +1 -1
- package/dist/resources/recordLockCoordinator.d.ts +557 -0
- package/dist/resources/recordLockCoordinator.js +2597 -0
- package/dist/resources/recordLockCoordinator.js.map +1 -0
- package/dist/resources/replayLogs.js +5 -0
- package/dist/resources/replayLogs.js.map +1 -1
- package/dist/resources/replicatedApplyFailure.d.ts +16 -0
- package/dist/resources/replicatedApplyFailure.js +63 -0
- package/dist/resources/replicatedApplyFailure.js.map +1 -0
- package/dist/resources/scheduler/scheduler.js +3 -3
- package/dist/resources/scheduler/scheduler.js.map +1 -1
- package/dist/resources/search.d.ts +10 -4
- package/dist/resources/search.js +160 -40
- package/dist/resources/search.js.map +1 -1
- package/dist/resources/tracked.d.ts +5 -1
- package/dist/resources/tracked.js +74 -23
- package/dist/resources/tracked.js.map +1 -1
- package/dist/resources/transactionBroadcast.js +4 -6
- package/dist/resources/transactionBroadcast.js.map +1 -1
- package/dist/security/auth.js +59 -23
- package/dist/security/auth.js.map +1 -1
- package/dist/security/deferredAuthentication.d.ts +11 -0
- package/dist/security/deferredAuthentication.js +25 -3
- package/dist/security/deferredAuthentication.js.map +1 -1
- package/dist/security/jsLoader.js +9 -4
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/server/REST.js +39 -5
- package/dist/server/REST.js.map +1 -1
- package/dist/server/http.d.ts +5 -1
- package/dist/server/http.js +34 -2
- package/dist/server/http.js.map +1 -1
- package/dist/server/mqtt.js +5 -1
- package/dist/server/mqtt.js.map +1 -1
- package/dist/server/serverHelpers/Headers.d.ts +2 -0
- package/dist/server/serverHelpers/Headers.js +6 -0
- package/dist/server/serverHelpers/Headers.js.map +1 -1
- package/dist/server/serverHelpers/NodeAdapterResponse.d.ts +48 -0
- package/dist/server/serverHelpers/NodeAdapterResponse.js +220 -0
- package/dist/server/serverHelpers/NodeAdapterResponse.js.map +1 -0
- package/dist/server/serverHelpers/Request.d.ts +5 -10
- package/dist/server/serverHelpers/Request.js +38 -136
- package/dist/server/serverHelpers/Request.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.d.ts +2 -0
- package/dist/server/serverHelpers/contentTypes.js +189 -15
- package/dist/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/server/serverHelpers/serverUtilities.d.ts +3 -3
- package/dist/server/serverHelpers/serverUtilities.js +110 -20
- package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
- package/dist/server/serverHelpers/uwsServer.js +4 -1
- package/dist/server/serverHelpers/uwsServer.js.map +1 -1
- package/dist/server/serverHelpers/webSocketCloseReason.d.ts +2 -0
- package/dist/server/serverHelpers/webSocketCloseReason.js +29 -0
- package/dist/server/serverHelpers/webSocketCloseReason.js.map +1 -0
- package/dist/server/storageReclamation.js +1 -1
- package/dist/server/storageReclamation.js.map +1 -1
- package/dist/server/threads/isolatedApplications.d.ts +47 -0
- package/dist/server/threads/isolatedApplications.js +171 -0
- package/dist/server/threads/isolatedApplications.js.map +1 -0
- package/dist/server/threads/logRotationTransport.d.ts +1 -0
- package/dist/server/threads/logRotationTransport.js +33 -0
- package/dist/server/threads/logRotationTransport.js.map +1 -0
- package/dist/server/threads/manageThreads.d.ts +64 -6
- package/dist/server/threads/manageThreads.js +261 -12
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/socketRouter.d.ts +1 -0
- package/dist/server/threads/socketRouter.js +196 -13
- package/dist/server/threads/socketRouter.js.map +1 -1
- package/dist/server/threads/threadServer.js +30 -7
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/utility/errors/hdbError.d.ts +24 -0
- package/dist/utility/errors/hdbError.js +58 -1
- package/dist/utility/errors/hdbError.js.map +1 -1
- package/dist/utility/hdbTerms.d.ts +2 -0
- package/dist/utility/hdbTerms.js +2 -0
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/logging/harper_logger.js +218 -38
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/dist/utility/logging/logGenerationCoordinator.d.ts +35 -0
- package/dist/utility/logging/logGenerationCoordinator.js +184 -0
- package/dist/utility/logging/logGenerationCoordinator.js.map +1 -0
- package/dist/utility/logging/logRotation.d.ts +46 -0
- package/dist/utility/logging/logRotation.js +365 -0
- package/dist/utility/logging/logRotation.js.map +1 -0
- package/dist/utility/logging/logRotator.d.ts +1 -1
- package/dist/utility/logging/logRotator.js +172 -92
- package/dist/utility/logging/logRotator.js.map +1 -1
- package/dist/utility/npmUtilities.js +6 -4
- package/dist/utility/npmUtilities.js.map +1 -1
- package/dist/utility/watcherFallback.d.ts +0 -45
- package/dist/utility/watcherFallback.js +1 -125
- package/dist/utility/watcherFallback.js.map +1 -1
- package/dist/validation/configValidator.js +6 -3
- package/dist/validation/configValidator.js.map +1 -1
- package/index.ts +9 -0
- package/json/systemSchema.json +3 -0
- package/npm-shrinkwrap.json +177 -87
- package/package.json +13 -5
- package/resources/DESIGN.md +689 -19
- package/resources/DatabaseTransaction.ts +230 -17
- package/resources/LMDBTransaction.ts +21 -3
- package/resources/PrimaryRocksDatabase.ts +20 -7
- package/resources/RecordEncoder.ts +52 -10
- package/resources/Resource.ts +97 -13
- package/resources/ResourceInterface.ts +8 -0
- package/resources/RocksIndexStore.ts +2 -1
- package/resources/RocksTransactionLogStore.ts +111 -31
- package/resources/Table.ts +1273 -396
- package/resources/analytics/DESIGN.md +38 -0
- package/resources/analytics/write.ts +50 -17
- package/resources/auditStore.ts +460 -11
- package/resources/crdt.ts +22 -0
- package/resources/dataLoader.ts +3 -4
- package/resources/databases.ts +611 -147
- package/resources/derivedIndexRegistry.ts +56 -0
- package/resources/derivedIndexRuntime.ts +2292 -0
- package/resources/graphql.ts +3 -2
- package/resources/indexes/DESIGN.md +833 -0
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +924 -46
- package/resources/indexes/fullTextDerivedIndex.ts +1165 -0
- package/resources/indexes/fullTextNativeBinding.ts +146 -0
- package/resources/indexes/hnswDerivedIndex.ts +531 -0
- package/resources/indexes/hnswPlaneBinding.ts +174 -0
- package/resources/indexes/nativeFullTextDerivedIndexLifecycle.ts +181 -0
- package/resources/nodeIdMapping.ts +50 -0
- package/resources/record-locks.md +1407 -0
- package/resources/recordLock.ts +173 -7
- package/resources/recordLockCoordinator.ts +3082 -0
- package/resources/replayLogs.ts +5 -0
- package/resources/replicatedApplyFailure.ts +77 -0
- package/resources/scheduler/DESIGN.md +40 -0
- package/resources/scheduler/scheduler.ts +4 -4
- package/resources/search.ts +169 -49
- package/resources/tracked.ts +73 -22
- package/resources/transactionBroadcast.ts +4 -4
- package/security/DESIGN.md +175 -0
- package/security/auth.ts +53 -24
- package/security/deferredAuthentication.ts +24 -2
- package/security/jsLoader.ts +9 -4
- package/server/DESIGN.md +275 -0
- package/server/REST.ts +42 -6
- package/server/http.ts +34 -2
- package/server/mqtt.ts +6 -4
- package/server/serverHelpers/Headers.ts +5 -1
- package/server/serverHelpers/NodeAdapterResponse.ts +221 -0
- package/server/serverHelpers/Request.ts +33 -131
- package/server/serverHelpers/contentTypes.ts +188 -15
- package/server/serverHelpers/serverUtilities.ts +157 -27
- package/server/serverHelpers/uwsServer.ts +4 -1
- package/server/serverHelpers/webSocketCloseReason.ts +25 -0
- package/server/storageReclamation.ts +2 -2
- package/server/threads/isolatedApplications.ts +157 -0
- package/server/threads/logRotationTransport.ts +40 -0
- package/server/threads/manageThreads.js +254 -12
- package/server/threads/socketRouter.ts +217 -11
- package/server/threads/threadServer.js +30 -7
- package/studio/web/assets/{Chat-BnCBegQz.js → Chat-DADFFGe_.js} +1 -1
- package/studio/web/assets/{FloatingChat-CoDW1ySS.js → FloatingChat-D_mI-rZ7.js} +3 -3
- package/studio/web/assets/{apiToken-Bwk5BLXW.js → apiToken-c2NiSDHa.js} +1 -1
- package/studio/web/assets/{applications-DHxGi7JH.js → applications-DktUqh7G.js} +1 -1
- package/studio/web/assets/{cssMode-s0cWI-_M.js → cssMode-Cs_75Xhw.js} +1 -1
- package/studio/web/assets/{editor-DNcRHK54.js → editor-19b-Y1IN.js} +1 -1
- package/studio/web/assets/{html-Bdssedlg.js → html-DiYEQMpB.js} +1 -1
- package/studio/web/assets/{htmlMode-CoDlJ3fw.js → htmlMode-CmR0y7P_.js} +1 -1
- package/studio/web/assets/{index-D6sxmFLR.js → index-Dm0rfkJ7.js} +5 -5
- package/studio/web/assets/{index.lazy-tmU5BS8s.js → index.lazy-7vqt2CC3.js} +1 -1
- package/studio/web/assets/{javascript-B8meVSTH.js → javascript-BWtCFuOt.js} +1 -1
- package/studio/web/assets/{jsonMode-DpIPd35T.js → jsonMode-Buzzbv9y.js} +1 -1
- package/studio/web/assets/{languageServices-C_5FMJzQ.js → languageServices-SqsFWfTM.js} +1 -1
- package/studio/web/assets/{lspLanguageFeatures-BIzNBzPK.js → lspLanguageFeatures-EMV5cmjo.js} +1 -1
- package/studio/web/assets/{notifications-CvZivSbh.js → notifications-CAB-LZWT.js} +1 -1
- package/studio/web/assets/{notifications-CQf18QKb.js → notifications-DRzmSRxM.js} +1 -1
- package/studio/web/assets/{profile-DdOwtntb.js → profile-BNKAl79n.js} +1 -1
- package/studio/web/assets/{regions-n69fwagr.js → regions-CUow_Zw2.js} +1 -1
- package/studio/web/assets/{register-PfWTCXWB.js → register-Dkt3WUMp.js} +2 -2
- package/studio/web/assets/{setComponentFile-Bg6O7X0S.js → setComponentFile-BZRfMD0N.js} +1 -1
- package/studio/web/assets/{setup-CUx_aUDl.js → setup-D_yiEPO2.js} +2 -2
- package/studio/web/assets/{status-D7BVKqX9.js → status-DhHh1Ge-.js} +1 -1
- package/studio/web/assets/{toggleHighContrast-DBSyXzMr.js → toggleHighContrast-D7L1PDtV.js} +1 -1
- package/studio/web/assets/{tsMode-BByKCjBS.js → tsMode-CCwLk1YS.js} +1 -1
- package/studio/web/assets/{typescript-DDLnLpw9.js → typescript-BP1j1mjn.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-31CHGaHk.js → useEntityRestURL-D7bnYxLw.js} +1 -1
- package/studio/web/assets/{workers-pR3jRY9D.js → workers-tOuCNT17.js} +1 -1
- package/studio/web/assets/{xml-2iRnMhQO.js → xml-BSG_3mQT.js} +1 -1
- package/studio/web/assets/{yaml-Bf92gJpd.js → yaml-DxiLprBB.js} +1 -1
- package/studio/web/index.html +1 -1
- package/utility/DESIGN.md +55 -0
- package/utility/errors/hdbError.ts +54 -0
- package/utility/hdbTerms.ts +2 -0
- package/utility/logging/harper_logger.ts +210 -30
- package/utility/logging/logGenerationCoordinator.ts +196 -0
- package/utility/logging/logRotation.ts +367 -0
- package/utility/logging/logRotator.ts +196 -91
- package/utility/npmUtilities.ts +6 -4
- package/utility/watcherFallback.ts +0 -122
- package/validation/configValidator.ts +6 -3
|
@@ -14,22 +14,77 @@ export function startWorker(path: any, options?: {}): Worker;
|
|
|
14
14
|
* never reported that they started — or `{declined: true}` when the process is already shutting down;
|
|
15
15
|
* from a worker, nothing — the restart is handed to the main thread.
|
|
16
16
|
*/
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* `application` selects which workers of the type restart: omitted preserves the historic all-worker
|
|
19
|
+
* behavior, explicit undefined restarts the shared pool only, a name restarts only that application's
|
|
20
|
+
* dedicated worker, and '*' restarts all.
|
|
21
|
+
*/
|
|
22
|
+
export function restartWorkers(name?: any, maxWorkersDown?: number, startReplacementThreads?: boolean, onProgress?: any, application?: any, ...args: any[]): Promise<{
|
|
23
|
+
declined: boolean;
|
|
24
|
+
workersKeptOnOldCode?: undefined;
|
|
25
|
+
replacementsNotStarted?: undefined;
|
|
26
|
+
} | {
|
|
18
27
|
workersKeptOnOldCode: number;
|
|
19
28
|
replacementsNotStarted: number;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} | undefined>;
|
|
29
|
+
declined?: undefined;
|
|
30
|
+
}>;
|
|
23
31
|
export function shutdownWorkers(name: any): Promise<{
|
|
32
|
+
declined: boolean;
|
|
33
|
+
workersKeptOnOldCode?: undefined;
|
|
34
|
+
replacementsNotStarted?: undefined;
|
|
35
|
+
} | {
|
|
24
36
|
workersKeptOnOldCode: number;
|
|
25
37
|
replacementsNotStarted: number;
|
|
26
|
-
|
|
27
|
-
declined: true;
|
|
38
|
+
declined?: undefined;
|
|
28
39
|
}>;
|
|
29
40
|
export function shutdownWorkersNow(name: any): Promise<void>;
|
|
30
41
|
export const workers: any[];
|
|
31
42
|
export function setMonitorListener(listener: any): void;
|
|
32
43
|
export function onMessageFromWorkers(listener: any): void;
|
|
44
|
+
/** Registered by socketRouter: starts and stops dedicated workers to match the root config's isolated applications. */
|
|
45
|
+
export function setIsolatedWorkerReconciler(reconcile: any): void;
|
|
46
|
+
export function setRunningIsolatedApplicationsGetter(getter: any): void;
|
|
47
|
+
/**
|
|
48
|
+
* The worker that owns `applicationName`'s per-application singletons (its scheduled jobs, its data
|
|
49
|
+
* files): pool worker 0 for a shared application and for root-level plugins (which pass no name), the
|
|
50
|
+
* dedicated worker for an isolated one -- never index 0, so node-wide duties stay on the pool, and
|
|
51
|
+
* never a dedicated worker for anything but its own application.
|
|
52
|
+
*/
|
|
53
|
+
export function isApplicationPrimaryWorker(applicationName: any): boolean;
|
|
54
|
+
/** The worker index as the application sees it: a dedicated worker is its application's worker 0. */
|
|
55
|
+
export function applicationWorkerIndex(): any;
|
|
56
|
+
/**
|
|
57
|
+
* Whether a singleton set up by APPLICATION CODE runs here (a caching table's `sourcedFrom`
|
|
58
|
+
* subscription): pool worker 0, or a dedicated worker -- the only code that runs on one is its own
|
|
59
|
+
* application's, so whatever it sets up is that application's to run. Not for root-level plugins,
|
|
60
|
+
* which load on every thread and must use isApplicationPrimaryWorker(name).
|
|
61
|
+
*/
|
|
62
|
+
export function runsApplicationCodeSingletons(): boolean;
|
|
63
|
+
/** Whether this thread is a worker dedicated to one isolated application. */
|
|
64
|
+
export function isDedicatedWorker(): boolean;
|
|
65
|
+
export function markBranchStorePath(path: any, isBranch?: boolean): void;
|
|
66
|
+
/**
|
|
67
|
+
* Whether this thread runs the "last worker" per-store maintenance for `storePath` (TTL scans, storage
|
|
68
|
+
* reclamation, audit cleanup). Shared stores belong to the last pool worker; a dedicated worker
|
|
69
|
+
* maintains only its own branch stores, which no other thread has open.
|
|
70
|
+
*/
|
|
71
|
+
export function ownsStoreMaintenance(storePath: any): boolean;
|
|
72
|
+
/** The "worker 0" counterpart: expiration eviction for `storePath`. */
|
|
73
|
+
export function ownsStoreExpiration(storePath: any): boolean;
|
|
74
|
+
/** Every started worker dedicated to `application`, including a replacement still booting. */
|
|
75
|
+
export function workersForApplication(application: any): any[];
|
|
76
|
+
/**
|
|
77
|
+
* Stop one worker for good: shut it down, honour the drain extension it may ask for, force it after
|
|
78
|
+
* the same backstop the rolling restart uses (FORCE_EXIT on Bun, where terminate() segfaults), and
|
|
79
|
+
* resolve once it has exited. Marked as shut down first so its exit does not start a replacement.
|
|
80
|
+
*/
|
|
81
|
+
export function stopWorker(worker: any): Promise<any>;
|
|
82
|
+
/**
|
|
83
|
+
* The restart scope on the wire: a worker cannot post `undefined` and have it mean "the pool" (a
|
|
84
|
+
* message with no scope at all means "everything", as every pre-existing sender intends).
|
|
85
|
+
*/
|
|
86
|
+
export function encodeRestartScope(application: any): any;
|
|
87
|
+
export function decodeRestartScope(message: any): any;
|
|
33
88
|
export function onMessageByType(type: any, listener: any): void;
|
|
34
89
|
export function broadcast(message: any, includeSelf: any): Promise<void>;
|
|
35
90
|
export function broadcastWithAcknowledgement(message: any, timeout?: number): Promise<any>;
|
|
@@ -64,8 +119,11 @@ export function unregisterProcessGroup(processGroupId: any): void;
|
|
|
64
119
|
export function isProcessGroupAlive(processGroupId: any, options: any): boolean;
|
|
65
120
|
export function isThreadRunning(ownerThreadId: any, timeoutMs?: number): Promise<boolean>;
|
|
66
121
|
export function waitUntilConfirmedGone(attemptTermination: any, treeIsAlive: any, pollMs: any): Promise<void>;
|
|
122
|
+
export function sendToThread(threadId: any, message: any): boolean;
|
|
67
123
|
export let getThreadInfo: any;
|
|
124
|
+
export let getRunningIsolatedApplications: any;
|
|
68
125
|
export function watchDir(dir: any, beforeRestartCallback: any): Promise<void>;
|
|
69
126
|
import { Worker } from "node:worker_threads";
|
|
127
|
+
export declare let processIncarnation: any;
|
|
70
128
|
export declare let whenThreadsStarted: Promise<any>;
|
|
71
129
|
export { resolve as threadsHaveStarted };
|
|
@@ -48,10 +48,12 @@ let threadTerminationTimeout = process.env.DEV_MODE === 'true' || process.env.DE
|
|
|
48
48
|
const RESTART_TYPE = 'restart';
|
|
49
49
|
const RESTART_PROGRESS_HEARTBEAT_MS = 15000;
|
|
50
50
|
const REQUEST_THREAD_INFO = 'request_thread_info';
|
|
51
|
+
const REQUEST_RUNNING_ISOLATED_APPLICATIONS = 'request-running-isolated-applications';
|
|
51
52
|
const RESOURCE_REPORT = 'resource_report';
|
|
52
53
|
const OS_THREAD_ID = 'os-thread-id';
|
|
53
54
|
const STUCK_WORKER_REPORT = 'stuck-worker-report';
|
|
54
55
|
const THREAD_INFO = 'thread_info';
|
|
56
|
+
const RUNNING_ISOLATED_APPLICATIONS = 'running-isolated-applications';
|
|
55
57
|
const ADDED_PORT = 'added-port';
|
|
56
58
|
const ACKNOWLEDGEMENT = 'ack';
|
|
57
59
|
const REMOVE_PORT = 'remove-port';
|
|
@@ -69,6 +71,7 @@ const AWAIT_PROCESS_GROUP_TERMINATION = 'await-process-group-termination';
|
|
|
69
71
|
const PROCESS_GROUP_TERMINATION_CONFIRMED = 'process-group-termination-confirmed';
|
|
70
72
|
const THREAD_INFO_REQUEST_TIMEOUT_MS = 1000;
|
|
71
73
|
let getThreadInfo;
|
|
74
|
+
let getRunningIsolatedApplications;
|
|
72
75
|
let awaitProcessGroupTermination;
|
|
73
76
|
// Worker-side backstop that force-exits if the graceful shutdown sequence doesn't finish in time.
|
|
74
77
|
let selfExitTimer;
|
|
@@ -132,6 +135,19 @@ module.exports = {
|
|
|
132
135
|
workers,
|
|
133
136
|
setMonitorListener,
|
|
134
137
|
onMessageFromWorkers,
|
|
138
|
+
setIsolatedWorkerReconciler,
|
|
139
|
+
setRunningIsolatedApplicationsGetter,
|
|
140
|
+
isApplicationPrimaryWorker,
|
|
141
|
+
applicationWorkerIndex,
|
|
142
|
+
runsApplicationCodeSingletons,
|
|
143
|
+
isDedicatedWorker,
|
|
144
|
+
markBranchStorePath,
|
|
145
|
+
ownsStoreMaintenance,
|
|
146
|
+
ownsStoreExpiration,
|
|
147
|
+
workersForApplication,
|
|
148
|
+
stopWorker,
|
|
149
|
+
encodeRestartScope,
|
|
150
|
+
decodeRestartScope,
|
|
135
151
|
onMessageByType,
|
|
136
152
|
broadcast,
|
|
137
153
|
broadcastWithAcknowledgement,
|
|
@@ -154,6 +170,11 @@ module.exports = {
|
|
|
154
170
|
isThreadRunning,
|
|
155
171
|
waitUntilConfirmedGone,
|
|
156
172
|
restartNumber: workerData?.restartNumber || 1,
|
|
173
|
+
// Identifies this process incarnation, where the PID cannot: a container reuses PID 1. Minted once
|
|
174
|
+
// on the main thread and carried to workers, so live siblings agree on it — one derived per thread
|
|
175
|
+
// would not. `undefined` on a worker started without it; consumers must fall back, not treat that
|
|
176
|
+
// as a mismatch.
|
|
177
|
+
processIncarnation: workerData ? workerData.processIncarnation : randomBytes(8).toString('hex'),
|
|
157
178
|
};
|
|
158
179
|
connectedPorts.onMessageByType = onMessageByType;
|
|
159
180
|
connectedPorts.sendToThread = function (threadId, message) {
|
|
@@ -175,6 +196,9 @@ connectedPorts.sendToThread = function (threadId, message) {
|
|
|
175
196
|
throw err;
|
|
176
197
|
}
|
|
177
198
|
};
|
|
199
|
+
// Direct thread-to-thread send, so a worker can reach a sibling (e.g. the record lock owner worker)
|
|
200
|
+
// without a hop through main. Returns false when no port for the thread is connected.
|
|
201
|
+
module.exports.sendToThread = connectedPorts.sendToThread;
|
|
178
202
|
module.exports.whenThreadsStarted = new Promise((resolve) => {
|
|
179
203
|
module.exports.threadsHaveStarted = resolve;
|
|
180
204
|
});
|
|
@@ -189,6 +213,106 @@ function setTerminateTimeout(newTimeout) {
|
|
|
189
213
|
function getWorkerIndex() {
|
|
190
214
|
return workerData ? workerData.workerIndex : isMainWorker ? 0 : undefined;
|
|
191
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* The worker that owns `applicationName`'s per-application singletons (its scheduled jobs, its data
|
|
218
|
+
* files): pool worker 0 for a shared application and for root-level plugins (which pass no name), the
|
|
219
|
+
* dedicated worker for an isolated one -- never index 0, so node-wide duties stay on the pool, and
|
|
220
|
+
* never a dedicated worker for anything but its own application.
|
|
221
|
+
*/
|
|
222
|
+
function isApplicationPrimaryWorker(applicationName) {
|
|
223
|
+
if (workerData?.isolatedApplication !== undefined)
|
|
224
|
+
return applicationName === workerData.isolatedApplication;
|
|
225
|
+
return getWorkerIndex() === 0;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Stores that exist only on this thread: the branch stores an isolated application opened in its
|
|
229
|
+
* dedicated worker. Registered by openBranchDatabase, so the ownership predicates below can tell them
|
|
230
|
+
* from the shared stores every thread has open.
|
|
231
|
+
*/
|
|
232
|
+
const branchStorePaths = new Set();
|
|
233
|
+
function markBranchStorePath(path, isBranch = true) {
|
|
234
|
+
if (isBranch)
|
|
235
|
+
branchStorePaths.add(path);
|
|
236
|
+
else
|
|
237
|
+
branchStorePaths.delete(path);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Whether this thread runs the "last worker" per-store maintenance for `storePath` (TTL scans, storage
|
|
241
|
+
* reclamation, audit cleanup). Shared stores belong to the last pool worker; a dedicated worker
|
|
242
|
+
* maintains only its own branch stores, which no other thread has open.
|
|
243
|
+
*/
|
|
244
|
+
function ownsStoreMaintenance(storePath) {
|
|
245
|
+
if (workerData?.isolatedApplication !== undefined)
|
|
246
|
+
return branchStorePaths.has(storePath);
|
|
247
|
+
return getWorkerIndex() === getWorkerCount() - 1;
|
|
248
|
+
}
|
|
249
|
+
/** The "worker 0" counterpart: expiration eviction for `storePath`. */
|
|
250
|
+
function ownsStoreExpiration(storePath) {
|
|
251
|
+
if (workerData?.isolatedApplication !== undefined)
|
|
252
|
+
return branchStorePaths.has(storePath);
|
|
253
|
+
return getWorkerIndex() === 0;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Whether a singleton set up by APPLICATION CODE runs here (a caching table's `sourcedFrom`
|
|
257
|
+
* subscription): pool worker 0, or a dedicated worker -- the only code that runs on one is its own
|
|
258
|
+
* application's, so whatever it sets up is that application's to run. Not for root-level plugins,
|
|
259
|
+
* which load on every thread and must use isApplicationPrimaryWorker(name).
|
|
260
|
+
*/
|
|
261
|
+
function runsApplicationCodeSingletons() {
|
|
262
|
+
return getWorkerIndex() === 0 || workerData?.isolatedApplication !== undefined;
|
|
263
|
+
}
|
|
264
|
+
/** Whether this thread is a worker dedicated to one isolated application. */
|
|
265
|
+
function isDedicatedWorker() {
|
|
266
|
+
return workerData?.isolatedApplication !== undefined;
|
|
267
|
+
}
|
|
268
|
+
/** The worker index as the application sees it: a dedicated worker is its application's worker 0. */
|
|
269
|
+
function applicationWorkerIndex() {
|
|
270
|
+
return workerData?.isolatedApplication !== undefined ? 0 : getWorkerIndex();
|
|
271
|
+
}
|
|
272
|
+
/** Every started worker dedicated to `application`, including a replacement still booting. */
|
|
273
|
+
function workersForApplication(application) {
|
|
274
|
+
return workers.filter((worker) => worker.application === application);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Stop one worker for good: shut it down, honour the drain extension it may ask for, force it after
|
|
278
|
+
* the same backstop the rolling restart uses (FORCE_EXIT on Bun, where terminate() segfaults), and
|
|
279
|
+
* resolve once it has exited. Marked as shut down first so its exit does not start a replacement.
|
|
280
|
+
*/
|
|
281
|
+
function stopWorker(worker) {
|
|
282
|
+
worker.wasShutdown = true;
|
|
283
|
+
return new Promise((resolve) => {
|
|
284
|
+
const armTerminate = (delay) => setTimeout(() => {
|
|
285
|
+
harperLogger.warn('Thread did not voluntarily terminate, terminating from the outside', worker.threadId);
|
|
286
|
+
if (isBun) {
|
|
287
|
+
try {
|
|
288
|
+
worker.postMessage({ type: FORCE_EXIT });
|
|
289
|
+
}
|
|
290
|
+
catch { }
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
worker.terminate();
|
|
294
|
+
}
|
|
295
|
+
}, delay).unref();
|
|
296
|
+
let timeout = armTerminate(threadTerminationTimeout * 2);
|
|
297
|
+
worker.extendTerminateDeadline = (deadlineMs) => {
|
|
298
|
+
clearTimeout(timeout);
|
|
299
|
+
const { boundedTerminateDelay, getShutdownDrainCeilingMs } = require("../../components/shutdownDrain.js");
|
|
300
|
+
timeout = armTerminate(boundedTerminateDelay(deadlineMs, Date.now(), threadTerminationTimeout * 2, getShutdownDrainCeilingMs()));
|
|
301
|
+
};
|
|
302
|
+
worker.on('exit', () => {
|
|
303
|
+
clearTimeout(timeout);
|
|
304
|
+
worker.extendTerminateDeadline = undefined;
|
|
305
|
+
resolve();
|
|
306
|
+
});
|
|
307
|
+
try {
|
|
308
|
+
worker.postMessage({ restartNumber: module.exports.restartNumber, type: hdbTerms.ITC_EVENT_TYPES.SHUTDOWN });
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
clearTimeout(timeout);
|
|
312
|
+
resolve(); // already gone
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
192
316
|
function getWorkerCount() {
|
|
193
317
|
return workerData ? workerData.workerCount : isMainWorker ? 1 : undefined;
|
|
194
318
|
}
|
|
@@ -221,8 +345,10 @@ const RESERVED_WORKER_DATA_KEYS = [
|
|
|
221
345
|
'workerCount',
|
|
222
346
|
'name',
|
|
223
347
|
'restartNumber',
|
|
348
|
+
'processIncarnation',
|
|
224
349
|
'ticketKeys',
|
|
225
350
|
'noServerStart',
|
|
351
|
+
'isolatedApplication',
|
|
226
352
|
'__proto__', // never a legitimate payload name; spread would define it as an own property
|
|
227
353
|
];
|
|
228
354
|
const workerDataProviders = new Map();
|
|
@@ -282,14 +408,17 @@ function getTicketKeys() {
|
|
|
282
408
|
ticketKeys = isMainThread ? randomBytes(48) : workerData.ticketKeys;
|
|
283
409
|
return ticketKeys;
|
|
284
410
|
}
|
|
411
|
+
// What application code sees: a dedicated worker is its application's only worker, index 0 of 1, so
|
|
412
|
+
// the documented partitioning idiom (`hash % server.workerCount === server.workerIndex`) covers
|
|
413
|
+
// everything there. Node-wide duties keep using the raw getWorkerIndex/getWorkerCount.
|
|
285
414
|
Object.defineProperty(server, 'workerIndex', {
|
|
286
415
|
get() {
|
|
287
|
-
return
|
|
416
|
+
return applicationWorkerIndex();
|
|
288
417
|
},
|
|
289
418
|
});
|
|
290
419
|
Object.defineProperty(server, 'workerCount', {
|
|
291
420
|
get() {
|
|
292
|
-
return getWorkerCount();
|
|
421
|
+
return workerData?.isolatedApplication !== undefined ? 1 : getWorkerCount();
|
|
293
422
|
},
|
|
294
423
|
});
|
|
295
424
|
if (!parentPort) {
|
|
@@ -297,6 +426,14 @@ if (!parentPort) {
|
|
|
297
426
|
if (worker)
|
|
298
427
|
sendThreadInfo(worker);
|
|
299
428
|
});
|
|
429
|
+
onMessageByType(REQUEST_RUNNING_ISOLATED_APPLICATIONS, (message, worker) => {
|
|
430
|
+
if (worker)
|
|
431
|
+
worker.postMessage({
|
|
432
|
+
type: RUNNING_ISOLATED_APPLICATIONS,
|
|
433
|
+
requestId: message.requestId,
|
|
434
|
+
applications: runningIsolatedApplicationsGetter(),
|
|
435
|
+
});
|
|
436
|
+
});
|
|
300
437
|
onMessageByType(RESOURCE_REPORT, (message, worker) => {
|
|
301
438
|
if (worker)
|
|
302
439
|
recordResourceReport(worker, message);
|
|
@@ -335,11 +472,12 @@ listenersByType.set(hdbTerms.ITC_EVENT_TYPES.MIDDLEWARE_CHAINS_RESPONSE, null);
|
|
|
335
472
|
listenersByType.set(hdbTerms.ITC_EVENT_TYPES.OPERATION_REGISTERED, null);
|
|
336
473
|
listenersByType.set(hdbTerms.ITC_EVENT_TYPES.OPERATION_EXECUTE_REQUEST, null);
|
|
337
474
|
listenersByType.set(hdbTerms.ITC_EVENT_TYPES.OPERATION_EXECUTE_RESPONSE, null);
|
|
338
|
-
//
|
|
475
|
+
// These request/response functions register their own one-shot parentPort listener per
|
|
339
476
|
// call rather than going through onMessageByType, so without this, every reply would also reach
|
|
340
477
|
// addPort's permanent dispatcher as an "unregistered" type: notifyMessageListeners would warn and
|
|
341
478
|
// queue each one in messagesQueuedByType forever, and a lock-wait polls every 50ms.
|
|
342
479
|
listenersByType.set(THREAD_INFO, null);
|
|
480
|
+
listenersByType.set(RUNNING_ISOLATED_APPLICATIONS, null);
|
|
343
481
|
listenersByType.set(PROCESS_GROUP_TERMINATION_CONFIRMED, null);
|
|
344
482
|
function startWorker(path, options = {}) {
|
|
345
483
|
if (processShuttingDown) {
|
|
@@ -361,7 +499,7 @@ function startWorker(path, options = {}) {
|
|
|
361
499
|
// and lower than total memory
|
|
362
500
|
availableMemory = Math.min(availableMemory, totalmem(), 20000 * MB);
|
|
363
501
|
const maxOldMemory = resolveThreadHeapMemoryMb(envMgr.get(hdbTerms.CONFIG_PARAMS.THREADS_MAXHEAPMEMORY)) ??
|
|
364
|
-
Math.max(Math.floor(availableMemory / MB / (10 + (options.threadCount || 1) / 4)), 512);
|
|
502
|
+
Math.max(Math.floor(availableMemory / MB / (10 + (options.heapShareCount || options.threadCount || 1) / 4)), 512);
|
|
365
503
|
// Max young memory space (semi-space for scavenger) is 1/128 of max memory (limited to 16-64). For most of our m5
|
|
366
504
|
// machines this will be 64MB (less for t3's). This is based on recommendations from:
|
|
367
505
|
// https://www.alibabacloud.com/blog/node-js-application-troubleshooting-manual---comprehensive-gc-problems-and-optimization594965
|
|
@@ -405,6 +543,9 @@ function startWorker(path, options = {}) {
|
|
|
405
543
|
for (const requirePath of getRequireModules())
|
|
406
544
|
execArgv.push('--require', requirePath);
|
|
407
545
|
}
|
|
546
|
+
// Only a start that declares the serving topology may write it; see DESIGN.md on the two workerCounts.
|
|
547
|
+
if (typeof options.threadCount === 'number')
|
|
548
|
+
workerCount = options.threadCount;
|
|
408
549
|
const worker = new Worker(isAbsolute(path) ? path : join(PACKAGE_ROOT, path), {
|
|
409
550
|
resourceLimits: {
|
|
410
551
|
maxOldGenerationSizeMb: maxOldMemory,
|
|
@@ -419,9 +560,11 @@ function startWorker(path, options = {}) {
|
|
|
419
560
|
addThreadIds: channelsToConnect.map((channel) => channel.existingPort.threadId),
|
|
420
561
|
addPortIsJobWorkers: channelsToConnect.map((channel) => channel.existingPort.isJobWorker === true),
|
|
421
562
|
workerIndex: options.workerIndex,
|
|
422
|
-
workerCount:
|
|
563
|
+
workerCount: options.threadCount,
|
|
423
564
|
name: options.name,
|
|
565
|
+
isolatedApplication: options.application,
|
|
424
566
|
restartNumber: module.exports.restartNumber,
|
|
567
|
+
processIncarnation: module.exports.processIncarnation,
|
|
425
568
|
ticketKeys: getTicketKeys(),
|
|
426
569
|
},
|
|
427
570
|
transferList: portsToSend,
|
|
@@ -452,7 +595,10 @@ function startWorker(path, options = {}) {
|
|
|
452
595
|
});
|
|
453
596
|
worker.on('exit', (_code) => {
|
|
454
597
|
workers.splice(workers.indexOf(worker), 1);
|
|
455
|
-
if (!processShuttingDown &&
|
|
598
|
+
if (!processShuttingDown &&
|
|
599
|
+
!worker.wasShutdown &&
|
|
600
|
+
options.autoRestart !== false &&
|
|
601
|
+
options.shouldAutoRestart?.(worker) !== false) {
|
|
456
602
|
// if this wasn't an intentional shutdown, restart now (unless we have tried too many times)
|
|
457
603
|
if (worker.unexpectedRestarts < MAX_UNEXPECTED_RESTARTS) {
|
|
458
604
|
options.unexpectedRestarts = worker.unexpectedRestarts + 1;
|
|
@@ -469,6 +615,7 @@ function startWorker(path, options = {}) {
|
|
|
469
615
|
if (options.onStarted)
|
|
470
616
|
options.onStarted(worker); // notify that it is ready
|
|
471
617
|
worker.name = options.name;
|
|
618
|
+
worker.application = options.application; // the isolated application this worker is dedicated to, if any
|
|
472
619
|
return worker;
|
|
473
620
|
}
|
|
474
621
|
const OVERLAPPING_RESTART_TYPES = [hdbTerms.THREAD_TYPES.HTTP];
|
|
@@ -487,8 +634,15 @@ const OVERLAPPING_RESTART_TYPES = [hdbTerms.THREAD_TYPES.HTTP];
|
|
|
487
634
|
* never reported that they started — or `{declined: true}` when the process is already shutting down;
|
|
488
635
|
* from a worker, nothing — the restart is handed to the main thread.
|
|
489
636
|
*/
|
|
637
|
+
/**
|
|
638
|
+
* `application` selects which workers of the type restart: omitted preserves the historic all-worker
|
|
639
|
+
* behavior, explicit undefined restarts the shared pool only, a name restarts only that application's
|
|
640
|
+
* dedicated worker, and '*' restarts all.
|
|
641
|
+
*/
|
|
490
642
|
async function restartWorkers(name = null, maxWorkersDown = Math.max(Math.floor(workerCount / 8), 1), // restart 1/8 of the threads at a time, but at least 1
|
|
491
|
-
startReplacementThreads = true, onProgress = null) {
|
|
643
|
+
startReplacementThreads = true, onProgress = null, application = undefined) {
|
|
644
|
+
if (arguments.length < 5)
|
|
645
|
+
application = '*';
|
|
492
646
|
if (isMainThread) {
|
|
493
647
|
// Declining is not the same as delegating: a caller reporting on the restart must not read this
|
|
494
648
|
// as "another thread is completing it".
|
|
@@ -503,9 +657,19 @@ startReplacementThreads = true, onProgress = null) {
|
|
|
503
657
|
catch (e) {
|
|
504
658
|
harperLogger.error('Unable to reestablish current working directory', e);
|
|
505
659
|
}
|
|
660
|
+
const freshlyStarted = new Set(); // dedicated workers the reconcile just started: already on the new code
|
|
506
661
|
// problematic cyclic dependency, bind late
|
|
507
662
|
const { resetRestartNeeded } = require("../../components/requestRestart.js");
|
|
508
|
-
|
|
663
|
+
// One process-wide bit cannot say WHICH application is pending, so it may only be cleared by a
|
|
664
|
+
// restart that demonstrably covers every worker the bit could stand for. A restart scoped to one
|
|
665
|
+
// application leaves the whole pool on its old code; a pool restart replaces every pool worker and
|
|
666
|
+
// reconciles dedicated slots, but leaves an already-running dedicated worker on its old modules.
|
|
667
|
+
// Clearing from either would report restartRequired: false while a deployed component is still
|
|
668
|
+
// loaded nowhere. So: all workers always, the pool only while no dedicated worker is running --
|
|
669
|
+
// which is every node that uses no isolated application, i.e. the historic behavior unchanged.
|
|
670
|
+
const coversEveryWorker = application === '*' || (application === undefined && !workers.some((worker) => worker.application));
|
|
671
|
+
if (coversEveryWorker)
|
|
672
|
+
resetRestartNeeded();
|
|
509
673
|
// This is here to prevent circular dependencies
|
|
510
674
|
if (startReplacementThreads) {
|
|
511
675
|
const { loadRootComponents } = require('../loadRootComponents.js');
|
|
@@ -515,6 +679,15 @@ startReplacementThreads = true, onProgress = null) {
|
|
|
515
679
|
const loading = setInterval(() => onProgress?.(), RESTART_PROGRESS_HEARTBEAT_MS).unref();
|
|
516
680
|
try {
|
|
517
681
|
await loadRootComponents();
|
|
682
|
+
// isolated applications added or removed by the reload get their dedicated worker started or
|
|
683
|
+
// stopped; a crash-looping newcomer can take a while, so the heartbeat runs through this too
|
|
684
|
+
try {
|
|
685
|
+
for (const application of (await isolatedWorkerReconciler?.()) ?? [])
|
|
686
|
+
freshlyStarted.add(application);
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
harperLogger.error('Could not reconcile isolated application workers', error);
|
|
690
|
+
}
|
|
518
691
|
}
|
|
519
692
|
finally {
|
|
520
693
|
clearInterval(loading);
|
|
@@ -522,7 +695,11 @@ startReplacementThreads = true, onProgress = null) {
|
|
|
522
695
|
onProgress?.();
|
|
523
696
|
}
|
|
524
697
|
module.exports.restartNumber++;
|
|
525
|
-
|
|
698
|
+
// `Infinity` is shutdownWorkers' "all at once" sentinel and must survive.
|
|
699
|
+
if (typeof maxWorkersDown !== 'number' || Number.isNaN(maxWorkersDown)) {
|
|
700
|
+
maxWorkersDown = 1;
|
|
701
|
+
}
|
|
702
|
+
else if (maxWorkersDown < 1) {
|
|
526
703
|
// we accept a ratio of workers, and compute absolute maximum being down at a time from the total number of
|
|
527
704
|
// threads
|
|
528
705
|
maxWorkersDown = maxWorkersDown * workers.length;
|
|
@@ -549,7 +726,7 @@ startReplacementThreads = true, onProgress = null) {
|
|
|
549
726
|
// replacement — an unavoidable brief gap, but only for worker-owned listeners, since the main
|
|
550
727
|
// thread keeps serving the HTTP ports throughout. This ordering is also what lets
|
|
551
728
|
// listenOnPorts() treat a dedicated listener's EADDRINUSE as an external conflict.
|
|
552
|
-
const
|
|
729
|
+
const platformCanPreStartReplacement = process.platform !== 'win32' && process.platform !== 'darwin' && !isBun;
|
|
553
730
|
const restarting = workers.slice(0);
|
|
554
731
|
for (let index = 0; index < restarting.length; index++) {
|
|
555
732
|
const worker = restarting[index];
|
|
@@ -558,7 +735,12 @@ startReplacementThreads = true, onProgress = null) {
|
|
|
558
735
|
break;
|
|
559
736
|
if ((name && worker.name !== name) || worker.wasShutdown)
|
|
560
737
|
continue; // filter by type, if specified
|
|
738
|
+
if (application !== '*' && worker.application !== application)
|
|
739
|
+
continue; // and by isolated application
|
|
740
|
+
if (worker.application && freshlyStarted.has(worker.application))
|
|
741
|
+
continue;
|
|
561
742
|
const overlapping = OVERLAPPING_RESTART_TYPES.indexOf(worker.name) > -1;
|
|
743
|
+
const canPreStartReplacement = platformCanPreStartReplacement && !worker.application;
|
|
562
744
|
if (overlapping && startReplacementThreads && canPreStartReplacement) {
|
|
563
745
|
// Overlapping restart: start the replacement and wait until it is accepting connections
|
|
564
746
|
// *before* shutting down the worker it replaces. The replacement joins the (SO_REUSEPORT)
|
|
@@ -730,6 +912,7 @@ startReplacementThreads = true, onProgress = null) {
|
|
|
730
912
|
parentPort.postMessage({
|
|
731
913
|
type: RESTART_TYPE,
|
|
732
914
|
workerType: name,
|
|
915
|
+
scope: encodeRestartScope(application),
|
|
733
916
|
});
|
|
734
917
|
}
|
|
735
918
|
}
|
|
@@ -779,7 +962,7 @@ function whenWorkerStarted(newWorker) {
|
|
|
779
962
|
});
|
|
780
963
|
}
|
|
781
964
|
function shutdownWorkers(name) {
|
|
782
|
-
return restartWorkers(name, Infinity, false);
|
|
965
|
+
return restartWorkers(name, Infinity, false, null, '*');
|
|
783
966
|
}
|
|
784
967
|
function beginProcessShutdown() {
|
|
785
968
|
processShuttingDown = true;
|
|
@@ -801,6 +984,27 @@ async function shutdownWorkersNow(name) {
|
|
|
801
984
|
await Promise.all(workers.map((worker) => worker.terminate()));
|
|
802
985
|
}
|
|
803
986
|
}
|
|
987
|
+
/**
|
|
988
|
+
* The restart scope on the wire: a worker cannot post `undefined` and have it mean "the pool" (a
|
|
989
|
+
* message with no scope at all means "everything", as every pre-existing sender intends).
|
|
990
|
+
*/
|
|
991
|
+
function encodeRestartScope(application) {
|
|
992
|
+
return application === undefined ? '' : application; // '' is not a legal application name
|
|
993
|
+
}
|
|
994
|
+
function decodeRestartScope(message) {
|
|
995
|
+
if (message.scope === undefined)
|
|
996
|
+
return '*';
|
|
997
|
+
return message.scope === '' ? undefined : message.scope;
|
|
998
|
+
}
|
|
999
|
+
let isolatedWorkerReconciler = null;
|
|
1000
|
+
let runningIsolatedApplicationsGetter = () => [];
|
|
1001
|
+
/** Registered by socketRouter: starts and stops dedicated workers to match the root config's isolated applications. */
|
|
1002
|
+
function setIsolatedWorkerReconciler(reconcile) {
|
|
1003
|
+
isolatedWorkerReconciler = reconcile;
|
|
1004
|
+
}
|
|
1005
|
+
function setRunningIsolatedApplicationsGetter(getter) {
|
|
1006
|
+
runningIsolatedApplicationsGetter = getter;
|
|
1007
|
+
}
|
|
804
1008
|
const messageListeners = [];
|
|
805
1009
|
function onMessageFromWorkers(listener) {
|
|
806
1010
|
messageListeners.push(listener);
|
|
@@ -1062,6 +1266,7 @@ function getChildWorkerInfo() {
|
|
|
1062
1266
|
return workers.map((worker) => ({
|
|
1063
1267
|
threadId: worker.threadId,
|
|
1064
1268
|
name: worker.name,
|
|
1269
|
+
application: worker.application,
|
|
1065
1270
|
heapTotal: worker.resources?.heapTotal,
|
|
1066
1271
|
heapUsed: worker.resources?.heapUsed,
|
|
1067
1272
|
externalMemory: worker.resources?.external,
|
|
@@ -1173,6 +1378,39 @@ if (parentPort && workerData?.addPorts) {
|
|
|
1173
1378
|
}, timeoutMs);
|
|
1174
1379
|
}
|
|
1175
1380
|
});
|
|
1381
|
+
let nextRunningIsolatedApplicationsRequestId = 0;
|
|
1382
|
+
getRunningIsolatedApplications = (timeoutMs) => new Promise((resolve, reject) => {
|
|
1383
|
+
const requestId = ++nextRunningIsolatedApplicationsRequestId;
|
|
1384
|
+
let timeout;
|
|
1385
|
+
parentPort.on('message', receiveApplications);
|
|
1386
|
+
try {
|
|
1387
|
+
parentPort.postMessage({ type: REQUEST_RUNNING_ISOLATED_APPLICATIONS, requestId });
|
|
1388
|
+
}
|
|
1389
|
+
catch (error) {
|
|
1390
|
+
cleanup();
|
|
1391
|
+
reject(error);
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
function receiveApplications(message) {
|
|
1395
|
+
if (message.type === RUNNING_ISOLATED_APPLICATIONS && message.requestId === requestId) {
|
|
1396
|
+
cleanup();
|
|
1397
|
+
resolve(message.applications);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
function cleanup() {
|
|
1401
|
+
if (timeout)
|
|
1402
|
+
clearTimeout(timeout);
|
|
1403
|
+
parentPort.off('message', receiveApplications);
|
|
1404
|
+
}
|
|
1405
|
+
if (timeoutMs != null) {
|
|
1406
|
+
timeout = setTimeout(() => {
|
|
1407
|
+
cleanup();
|
|
1408
|
+
const error = new Error(`Timed out waiting for isolated application topology after ${timeoutMs}ms`);
|
|
1409
|
+
error.code = 'ERR_ISOLATED_APPLICATIONS_TIMEOUT';
|
|
1410
|
+
reject(error);
|
|
1411
|
+
}, timeoutMs);
|
|
1412
|
+
}
|
|
1413
|
+
});
|
|
1176
1414
|
let awaitTerminationRequestId = 0;
|
|
1177
1415
|
awaitProcessGroupTermination = (ownerThreadId, signal) => new Promise((resolve) => {
|
|
1178
1416
|
// Deliberately no timeout: a contender must not reclaim a dead owner's lock while its
|
|
@@ -1197,9 +1435,17 @@ if (parentPort && workerData?.addPorts) {
|
|
|
1197
1435
|
}
|
|
1198
1436
|
else {
|
|
1199
1437
|
getThreadInfo = getChildWorkerInfo;
|
|
1438
|
+
getRunningIsolatedApplications = isMainThread
|
|
1439
|
+
? () => runningIsolatedApplicationsGetter()
|
|
1440
|
+
: () => {
|
|
1441
|
+
const error = new Error('No channel to the main thread for isolated application topology');
|
|
1442
|
+
error.code = 'ERR_ISOLATED_APPLICATIONS_UNAVAILABLE';
|
|
1443
|
+
return Promise.reject(error);
|
|
1444
|
+
};
|
|
1200
1445
|
awaitProcessGroupTermination = (ownerThreadId) => pendingProcessGroupTerminations.get(ownerThreadId) ?? Promise.resolve();
|
|
1201
1446
|
}
|
|
1202
1447
|
module.exports.getThreadInfo = getThreadInfo;
|
|
1448
|
+
module.exports.getRunningIsolatedApplications = getRunningIsolatedApplications;
|
|
1203
1449
|
// Listeners notified when a connected thread's port closes (worker exit/restart), so
|
|
1204
1450
|
// modules holding per-thread state (e.g. registeredOperations' registry and in-flight
|
|
1205
1451
|
// forwards) can clean up.
|
|
@@ -1703,7 +1949,7 @@ if (isMainThread) {
|
|
|
1703
1949
|
queuedRestart = setTimeout(async () => {
|
|
1704
1950
|
if (beforeRestart)
|
|
1705
1951
|
await beforeRestart();
|
|
1706
|
-
await restartWorkers();
|
|
1952
|
+
await restartWorkers(undefined, undefined, true, null, '*');
|
|
1707
1953
|
console.log('Reloaded Harper components, changed files:', Array.from(changedFiles));
|
|
1708
1954
|
changedFiles.clear();
|
|
1709
1955
|
}, 100);
|
|
@@ -1727,4 +1973,7 @@ else {
|
|
|
1727
1973
|
realExit(0);
|
|
1728
1974
|
});
|
|
1729
1975
|
}
|
|
1976
|
+
// Required here, not from logging, which must never import this module; last in the file because it
|
|
1977
|
+
// calls onThreadExit.
|
|
1978
|
+
require("./logRotationTransport.js");
|
|
1730
1979
|
//# sourceMappingURL=manageThreads.js.map
|