@harperfast/harper 5.3.0-alpha.1 → 5.3.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.
Files changed (291) hide show
  1. package/bin/cliOperations.ts +12 -0
  2. package/bin/restart.ts +66 -6
  3. package/components/Application.ts +1134 -112
  4. package/components/OptionsWatcher.ts +368 -102
  5. package/components/Scope.ts +20 -1
  6. package/components/componentLoader.ts +43 -4
  7. package/components/deploymentOperations.ts +4 -1
  8. package/components/deploymentRecorder.ts +9 -2
  9. package/components/operations.js +284 -52
  10. package/components/operationsValidation.js +49 -2
  11. package/components/packageComponent.ts +25 -1
  12. package/components/requestRestart.ts +11 -0
  13. package/config/RootConfigWatcher.ts +191 -37
  14. package/config/configReadRetry.ts +62 -0
  15. package/config/configUtils.ts +78 -26
  16. package/config/parseConfigFile.ts +34 -0
  17. package/config/readConfigFileSync.ts +44 -0
  18. package/config/watcherArming.ts +59 -0
  19. package/config-root.schema.json +4 -0
  20. package/dataLayer/harperBridge/ResourceBridge.ts +28 -2
  21. package/dist/bin/cliOperations.js +13 -0
  22. package/dist/bin/cliOperations.js.map +1 -1
  23. package/dist/bin/restart.js +42 -6
  24. package/dist/bin/restart.js.map +1 -1
  25. package/dist/components/Application.d.ts +104 -9
  26. package/dist/components/Application.js +954 -102
  27. package/dist/components/Application.js.map +1 -1
  28. package/dist/components/OptionsWatcher.d.ts +4 -1
  29. package/dist/components/OptionsWatcher.js +378 -104
  30. package/dist/components/OptionsWatcher.js.map +1 -1
  31. package/dist/components/Scope.js +15 -1
  32. package/dist/components/Scope.js.map +1 -1
  33. package/dist/components/componentLoader.js +35 -3
  34. package/dist/components/componentLoader.js.map +1 -1
  35. package/dist/components/deploymentOperations.js +4 -1
  36. package/dist/components/deploymentOperations.js.map +1 -1
  37. package/dist/components/deploymentRecorder.d.ts +4 -2
  38. package/dist/components/deploymentRecorder.js +1 -0
  39. package/dist/components/deploymentRecorder.js.map +1 -1
  40. package/dist/components/operations.d.ts +28 -0
  41. package/dist/components/operations.js +263 -54
  42. package/dist/components/operations.js.map +1 -1
  43. package/dist/components/operationsValidation.js +48 -2
  44. package/dist/components/operationsValidation.js.map +1 -1
  45. package/dist/components/packageComponent.js +24 -0
  46. package/dist/components/packageComponent.js.map +1 -1
  47. package/dist/components/requestRestart.d.ts +1 -0
  48. package/dist/components/requestRestart.js +7 -0
  49. package/dist/components/requestRestart.js.map +1 -1
  50. package/dist/config/RootConfigWatcher.d.ts +2 -0
  51. package/dist/config/RootConfigWatcher.js +189 -35
  52. package/dist/config/RootConfigWatcher.js.map +1 -1
  53. package/dist/config/configReadRetry.d.ts +8 -0
  54. package/dist/config/configReadRetry.js +62 -0
  55. package/dist/config/configReadRetry.js.map +1 -0
  56. package/dist/config/configUtils.d.ts +10 -9
  57. package/dist/config/configUtils.js +63 -27
  58. package/dist/config/configUtils.js.map +1 -1
  59. package/dist/config/parseConfigFile.d.ts +4 -0
  60. package/dist/config/parseConfigFile.js +35 -0
  61. package/dist/config/parseConfigFile.js.map +1 -0
  62. package/dist/config/readConfigFileSync.d.ts +1 -0
  63. package/dist/config/readConfigFileSync.js +47 -0
  64. package/dist/config/readConfigFileSync.js.map +1 -0
  65. package/dist/config/watcherArming.d.ts +15 -0
  66. package/dist/config/watcherArming.js +59 -0
  67. package/dist/config/watcherArming.js.map +1 -0
  68. package/dist/dataLayer/harperBridge/ResourceBridge.js +21 -2
  69. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  70. package/dist/index.d.ts +1 -0
  71. package/dist/index.js +4 -1
  72. package/dist/index.js.map +1 -1
  73. package/dist/json/systemSchema.json +3 -0
  74. package/dist/resources/DatabaseTransaction.d.ts +25 -0
  75. package/dist/resources/DatabaseTransaction.js +224 -18
  76. package/dist/resources/DatabaseTransaction.js.map +1 -1
  77. package/dist/resources/LMDBTransaction.d.ts +2 -1
  78. package/dist/resources/LMDBTransaction.js +22 -3
  79. package/dist/resources/LMDBTransaction.js.map +1 -1
  80. package/dist/resources/PrimaryRocksDatabase.js +22 -6
  81. package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
  82. package/dist/resources/RecordEncoder.d.ts +1 -1
  83. package/dist/resources/RecordEncoder.js +21 -5
  84. package/dist/resources/RecordEncoder.js.map +1 -1
  85. package/dist/resources/Resource.js +97 -13
  86. package/dist/resources/Resource.js.map +1 -1
  87. package/dist/resources/ResourceInterface.d.ts +8 -0
  88. package/dist/resources/RocksIndexStore.js +2 -1
  89. package/dist/resources/RocksIndexStore.js.map +1 -1
  90. package/dist/resources/RocksTransactionLogStore.d.ts +10 -0
  91. package/dist/resources/RocksTransactionLogStore.js +104 -33
  92. package/dist/resources/RocksTransactionLogStore.js.map +1 -1
  93. package/dist/resources/Table.d.ts +58 -7
  94. package/dist/resources/Table.js +1095 -346
  95. package/dist/resources/Table.js.map +1 -1
  96. package/dist/resources/analytics/write.js +10 -3
  97. package/dist/resources/analytics/write.js.map +1 -1
  98. package/dist/resources/auditStore.d.ts +170 -0
  99. package/dist/resources/auditStore.js +457 -11
  100. package/dist/resources/auditStore.js.map +1 -1
  101. package/dist/resources/dataLoader.js +3 -4
  102. package/dist/resources/dataLoader.js.map +1 -1
  103. package/dist/resources/databases.d.ts +16 -13
  104. package/dist/resources/databases.js +624 -176
  105. package/dist/resources/databases.js.map +1 -1
  106. package/dist/resources/derivedIndexRegistry.d.ts +5 -0
  107. package/dist/resources/derivedIndexRegistry.js +68 -0
  108. package/dist/resources/derivedIndexRegistry.js.map +1 -0
  109. package/dist/resources/derivedIndexRuntime.d.ts +215 -0
  110. package/dist/resources/derivedIndexRuntime.js +2027 -0
  111. package/dist/resources/derivedIndexRuntime.js.map +1 -0
  112. package/dist/resources/graphql.js +3 -2
  113. package/dist/resources/graphql.js.map +1 -1
  114. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +102 -9
  115. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +848 -39
  116. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  117. package/dist/resources/indexes/hnswDerivedIndex.d.ts +67 -0
  118. package/dist/resources/indexes/hnswDerivedIndex.js +464 -0
  119. package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -0
  120. package/dist/resources/indexes/hnswPlaneBinding.d.ts +65 -0
  121. package/dist/resources/indexes/hnswPlaneBinding.js +91 -0
  122. package/dist/resources/indexes/hnswPlaneBinding.js.map +1 -0
  123. package/dist/resources/nodeIdMapping.d.ts +5 -0
  124. package/dist/resources/nodeIdMapping.js +49 -0
  125. package/dist/resources/nodeIdMapping.js.map +1 -1
  126. package/dist/resources/recordLock.d.ts +47 -4
  127. package/dist/resources/recordLock.js +138 -7
  128. package/dist/resources/recordLock.js.map +1 -1
  129. package/dist/resources/recordLockCoordinator.d.ts +557 -0
  130. package/dist/resources/recordLockCoordinator.js +2565 -0
  131. package/dist/resources/recordLockCoordinator.js.map +1 -0
  132. package/dist/resources/replayLogs.js +5 -0
  133. package/dist/resources/replayLogs.js.map +1 -1
  134. package/dist/resources/replicatedApplyFailure.d.ts +16 -0
  135. package/dist/resources/replicatedApplyFailure.js +63 -0
  136. package/dist/resources/replicatedApplyFailure.js.map +1 -0
  137. package/dist/resources/scheduler/scheduler.js +3 -3
  138. package/dist/resources/scheduler/scheduler.js.map +1 -1
  139. package/dist/resources/search.d.ts +10 -4
  140. package/dist/resources/search.js +160 -40
  141. package/dist/resources/search.js.map +1 -1
  142. package/dist/resources/tracked.d.ts +5 -1
  143. package/dist/resources/tracked.js +74 -23
  144. package/dist/resources/tracked.js.map +1 -1
  145. package/dist/security/jsLoader.js +6 -4
  146. package/dist/security/jsLoader.js.map +1 -1
  147. package/dist/server/REST.js +33 -2
  148. package/dist/server/REST.js.map +1 -1
  149. package/dist/server/http.d.ts +5 -1
  150. package/dist/server/http.js +34 -2
  151. package/dist/server/http.js.map +1 -1
  152. package/dist/server/serverHelpers/Headers.d.ts +2 -0
  153. package/dist/server/serverHelpers/Headers.js +6 -0
  154. package/dist/server/serverHelpers/Headers.js.map +1 -1
  155. package/dist/server/serverHelpers/NodeAdapterResponse.d.ts +48 -0
  156. package/dist/server/serverHelpers/NodeAdapterResponse.js +220 -0
  157. package/dist/server/serverHelpers/NodeAdapterResponse.js.map +1 -0
  158. package/dist/server/serverHelpers/Request.d.ts +5 -10
  159. package/dist/server/serverHelpers/Request.js +38 -136
  160. package/dist/server/serverHelpers/Request.js.map +1 -1
  161. package/dist/server/serverHelpers/contentTypes.d.ts +2 -0
  162. package/dist/server/serverHelpers/contentTypes.js +189 -15
  163. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  164. package/dist/server/serverHelpers/serverUtilities.js +96 -17
  165. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  166. package/dist/server/storageReclamation.js +1 -1
  167. package/dist/server/storageReclamation.js.map +1 -1
  168. package/dist/server/threads/isolatedApplications.d.ts +47 -0
  169. package/dist/server/threads/isolatedApplications.js +171 -0
  170. package/dist/server/threads/isolatedApplications.js.map +1 -0
  171. package/dist/server/threads/logRotationTransport.d.ts +1 -0
  172. package/dist/server/threads/logRotationTransport.js +33 -0
  173. package/dist/server/threads/logRotationTransport.js.map +1 -0
  174. package/dist/server/threads/manageThreads.d.ts +64 -6
  175. package/dist/server/threads/manageThreads.js +261 -12
  176. package/dist/server/threads/manageThreads.js.map +1 -1
  177. package/dist/server/threads/socketRouter.d.ts +1 -0
  178. package/dist/server/threads/socketRouter.js +196 -13
  179. package/dist/server/threads/socketRouter.js.map +1 -1
  180. package/dist/server/threads/threadServer.js +30 -7
  181. package/dist/server/threads/threadServer.js.map +1 -1
  182. package/dist/utility/errors/hdbError.d.ts +24 -0
  183. package/dist/utility/errors/hdbError.js +58 -1
  184. package/dist/utility/errors/hdbError.js.map +1 -1
  185. package/dist/utility/hdbTerms.d.ts +2 -0
  186. package/dist/utility/hdbTerms.js +2 -0
  187. package/dist/utility/hdbTerms.js.map +1 -1
  188. package/dist/utility/logging/harper_logger.js +217 -38
  189. package/dist/utility/logging/harper_logger.js.map +1 -1
  190. package/dist/utility/logging/logGenerationCoordinator.d.ts +35 -0
  191. package/dist/utility/logging/logGenerationCoordinator.js +184 -0
  192. package/dist/utility/logging/logGenerationCoordinator.js.map +1 -0
  193. package/dist/utility/logging/logRotation.d.ts +46 -0
  194. package/dist/utility/logging/logRotation.js +365 -0
  195. package/dist/utility/logging/logRotation.js.map +1 -0
  196. package/dist/utility/logging/logRotator.d.ts +1 -1
  197. package/dist/utility/logging/logRotator.js +172 -92
  198. package/dist/utility/logging/logRotator.js.map +1 -1
  199. package/dist/utility/npmUtilities.js +6 -4
  200. package/dist/utility/npmUtilities.js.map +1 -1
  201. package/dist/utility/watcherFallback.d.ts +0 -45
  202. package/dist/utility/watcherFallback.js +1 -125
  203. package/dist/utility/watcherFallback.js.map +1 -1
  204. package/dist/validation/configValidator.js +6 -3
  205. package/dist/validation/configValidator.js.map +1 -1
  206. package/index.ts +6 -0
  207. package/json/systemSchema.json +3 -0
  208. package/npm-shrinkwrap.json +131 -41
  209. package/package.json +10 -3
  210. package/resources/DESIGN.md +124 -19
  211. package/resources/DatabaseTransaction.ts +230 -17
  212. package/resources/LMDBTransaction.ts +21 -3
  213. package/resources/PrimaryRocksDatabase.ts +20 -7
  214. package/resources/RecordEncoder.ts +25 -5
  215. package/resources/Resource.ts +97 -13
  216. package/resources/ResourceInterface.ts +8 -0
  217. package/resources/RocksIndexStore.ts +2 -1
  218. package/resources/RocksTransactionLogStore.ts +111 -31
  219. package/resources/Table.ts +1224 -393
  220. package/resources/analytics/write.ts +10 -3
  221. package/resources/auditStore.ts +460 -11
  222. package/resources/dataLoader.ts +3 -4
  223. package/resources/databases.ts +610 -146
  224. package/resources/derivedIndexRegistry.ts +56 -0
  225. package/resources/derivedIndexRuntime.ts +2292 -0
  226. package/resources/graphql.ts +3 -2
  227. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +905 -46
  228. package/resources/indexes/hnswDerivedIndex.ts +531 -0
  229. package/resources/indexes/hnswPlaneBinding.ts +174 -0
  230. package/resources/nodeIdMapping.ts +50 -0
  231. package/resources/recordLock.ts +173 -7
  232. package/resources/recordLockCoordinator.ts +3043 -0
  233. package/resources/replayLogs.ts +5 -0
  234. package/resources/replicatedApplyFailure.ts +77 -0
  235. package/resources/scheduler/scheduler.ts +4 -4
  236. package/resources/search.ts +169 -49
  237. package/resources/tracked.ts +73 -22
  238. package/security/jsLoader.ts +6 -4
  239. package/server/DESIGN.md +11 -0
  240. package/server/REST.ts +36 -3
  241. package/server/http.ts +34 -2
  242. package/server/serverHelpers/Headers.ts +5 -1
  243. package/server/serverHelpers/NodeAdapterResponse.ts +221 -0
  244. package/server/serverHelpers/Request.ts +33 -131
  245. package/server/serverHelpers/contentTypes.ts +188 -15
  246. package/server/serverHelpers/serverUtilities.ts +143 -24
  247. package/server/storageReclamation.ts +2 -2
  248. package/server/threads/isolatedApplications.ts +157 -0
  249. package/server/threads/logRotationTransport.ts +40 -0
  250. package/server/threads/manageThreads.js +254 -12
  251. package/server/threads/socketRouter.ts +217 -11
  252. package/server/threads/threadServer.js +30 -7
  253. package/studio/web/assets/{Chat-BnCBegQz.js → Chat-D3j-1yY1.js} +1 -1
  254. package/studio/web/assets/{FloatingChat-CoDW1ySS.js → FloatingChat-BxJGYcfB.js} +3 -3
  255. package/studio/web/assets/{apiToken-Bwk5BLXW.js → apiToken-CT55oWOe.js} +1 -1
  256. package/studio/web/assets/{applications-DHxGi7JH.js → applications-D9Ct9_vm.js} +1 -1
  257. package/studio/web/assets/{cssMode-s0cWI-_M.js → cssMode-DV8H7VwA.js} +1 -1
  258. package/studio/web/assets/{editor-DNcRHK54.js → editor-uatc0unt.js} +1 -1
  259. package/studio/web/assets/{html-Bdssedlg.js → html-Bm6D6paN.js} +1 -1
  260. package/studio/web/assets/{htmlMode-CoDlJ3fw.js → htmlMode-CEn7tpLG.js} +1 -1
  261. package/studio/web/assets/{index-D6sxmFLR.js → index-BIXW6Pu4.js} +5 -5
  262. package/studio/web/assets/{index.lazy-tmU5BS8s.js → index.lazy-UI7L-Vrk.js} +1 -1
  263. package/studio/web/assets/{javascript-B8meVSTH.js → javascript-CJ0G3AFZ.js} +1 -1
  264. package/studio/web/assets/{jsonMode-DpIPd35T.js → jsonMode-DQADAYEa.js} +1 -1
  265. package/studio/web/assets/{languageServices-C_5FMJzQ.js → languageServices-CAQJXWcI.js} +1 -1
  266. package/studio/web/assets/{lspLanguageFeatures-BIzNBzPK.js → lspLanguageFeatures-CCQ8P5sY.js} +1 -1
  267. package/studio/web/assets/{notifications-CQf18QKb.js → notifications-BbxTU6Aw.js} +1 -1
  268. package/studio/web/assets/{notifications-CvZivSbh.js → notifications-Cvb3P1lB.js} +1 -1
  269. package/studio/web/assets/{profile-DdOwtntb.js → profile-Yyb7gsvL.js} +1 -1
  270. package/studio/web/assets/{regions-n69fwagr.js → regions-OgjGHlU5.js} +1 -1
  271. package/studio/web/assets/{register-PfWTCXWB.js → register-6qwNEOY3.js} +2 -2
  272. package/studio/web/assets/{setComponentFile-Bg6O7X0S.js → setComponentFile-BilDMtgB.js} +1 -1
  273. package/studio/web/assets/{setup-CUx_aUDl.js → setup-J6qJ7OIU.js} +2 -2
  274. package/studio/web/assets/{status-D7BVKqX9.js → status-0RWGcfyD.js} +1 -1
  275. package/studio/web/assets/{toggleHighContrast-DBSyXzMr.js → toggleHighContrast-BIn-vErT.js} +1 -1
  276. package/studio/web/assets/{tsMode-BByKCjBS.js → tsMode-DgUXku4d.js} +1 -1
  277. package/studio/web/assets/{typescript-DDLnLpw9.js → typescript-C9orXcsM.js} +1 -1
  278. package/studio/web/assets/{useEntityRestURL-31CHGaHk.js → useEntityRestURL-BEoXXbUB.js} +1 -1
  279. package/studio/web/assets/{workers-pR3jRY9D.js → workers-JVzSDmgx.js} +1 -1
  280. package/studio/web/assets/{xml-2iRnMhQO.js → xml-Cq-S8S4X.js} +1 -1
  281. package/studio/web/assets/{yaml-Bf92gJpd.js → yaml-sfoRdh1M.js} +1 -1
  282. package/studio/web/index.html +1 -1
  283. package/utility/errors/hdbError.ts +54 -0
  284. package/utility/hdbTerms.ts +2 -0
  285. package/utility/logging/harper_logger.ts +209 -30
  286. package/utility/logging/logGenerationCoordinator.ts +196 -0
  287. package/utility/logging/logRotation.ts +367 -0
  288. package/utility/logging/logRotator.ts +196 -91
  289. package/utility/npmUtilities.ts +6 -4
  290. package/utility/watcherFallback.ts +0 -122
  291. package/validation/configValidator.ts +6 -3
@@ -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 getWorkerIndex();
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
- // getThreadInfo/awaitProcessGroupTermination register their own one-shot parentPort listener per
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: (workerCount = options.threadCount),
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 && !worker.wasShutdown && options.autoRestart !== false) {
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
- resetRestartNeeded();
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
- if (maxWorkersDown < 1) {
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 canPreStartReplacement = process.platform !== 'win32' && process.platform !== 'darwin' && !isBun;
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