@platformatic/watt-extra 1.8.1-alpha.0 → 1.8.1-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/watt-extra",
3
- "version": "1.8.1-alpha.0",
3
+ "version": "1.8.1-alpha.1",
4
4
  "description": "The Platformatic runtime manager",
5
5
  "type": "module",
6
6
  "scripts": {
package/plugins/alerts.js CHANGED
@@ -10,6 +10,10 @@ async function alerts (app, _opts) {
10
10
  const lastServicesAlertTime = {}
11
11
  const workerStartTimes = new Map() // Track per-worker start times for grace period
12
12
 
13
+ // Store listener references for cleanup
14
+ let workerStartedListener = null
15
+ let healthListener = null
16
+
13
17
  async function setupAlerts () {
14
18
  const scalerAlgorithmVersion = app.instanceConfig?.scaler?.version ?? 'v1'
15
19
  if (scalerAlgorithmVersion !== 'v1') {
@@ -37,17 +41,30 @@ async function alerts (app, _opts) {
37
41
  return
38
42
  }
39
43
 
44
+ const healthEventName = app.watt.runtimeSupportsNewHealthMetrics()
45
+ ? 'application:worker:health:metrics'
46
+ : 'application:worker:health'
47
+
48
+ // Remove old listeners if they exist (for ICC recovery scenario)
49
+ if (workerStartedListener) {
50
+ runtime.removeListener('application:worker:started', workerStartedListener)
51
+ }
52
+ if (healthListener) {
53
+ runtime.removeListener(healthEventName, healthListener)
54
+ }
55
+
40
56
  // Default start time for workers that started before the listener was registered
41
57
  const pluginStartTime = Date.now()
42
58
 
43
59
  // Listen for worker start events to track start times
44
- runtime.on('application:worker:started', (workerInfo) => {
60
+ workerStartedListener = (workerInfo) => {
45
61
  const workerId = workerInfo?.id
46
62
  if (workerId) {
47
63
  workerStartTimes.set(workerId, Date.now())
48
64
  app.log.debug({ workerId }, 'Worker started, tracking for grace period')
49
65
  }
50
- })
66
+ }
67
+ runtime.on('application:worker:started', workerStartedListener)
51
68
 
52
69
  const processHealthInfo = async (healthInfo) => {
53
70
  if (!healthInfo) {
@@ -147,7 +164,7 @@ async function alerts (app, _opts) {
147
164
 
148
165
  if (app.watt.runtimeSupportsNewHealthMetrics()) {
149
166
  // Runtime >= 3.18.0: Listen to health:metrics
150
- runtime.on('application:worker:health:metrics', async (health) => {
167
+ healthListener = async (health) => {
151
168
  if (!health) {
152
169
  app.log.error('No health info received')
153
170
  return
@@ -177,11 +194,12 @@ async function alerts (app, _opts) {
177
194
  }
178
195
 
179
196
  await processHealthInfo(healthInfo)
180
- })
197
+ }
181
198
  } else {
182
199
  // Runtime < 3.18.0:
183
- runtime.on('application:worker:health', processHealthInfo)
200
+ healthListener = processHealthInfo
184
201
  }
202
+ runtime.on(healthEventName, healthListener)
185
203
  }
186
204
  app.setupAlerts = setupAlerts
187
205
  }
@@ -73,6 +73,11 @@ async function flamegraphs (app, _opts) {
73
73
  }
74
74
  }
75
75
 
76
+ // Remove old listener if it exists (for ICC recovery scenario)
77
+ if (workerStartedListener) {
78
+ runtime.removeListener('application:worker:started', workerStartedListener)
79
+ }
80
+
76
81
  // Listen for new workers starting and start profiling on them
77
82
  workerStartedListener = ({ application, worker }) => {
78
83
  if (isFlamegraphsDisabled) {
@@ -34,6 +34,9 @@ async function healthSignals (app, _opts) {
34
34
  // remove after depricating the Scaler v1 UI
35
35
  const servicesMetrics = {}
36
36
 
37
+ // Store listener reference for cleanup
38
+ let healthMetricsListener = null
39
+
37
40
  async function setupHealthSignals () {
38
41
  const scalerAlgorithmVersion = app.instanceConfig?.scaler?.version ?? 'v1'
39
42
  if (scalerAlgorithmVersion !== 'v2') {
@@ -74,7 +77,12 @@ async function healthSignals (app, _opts) {
74
77
  return
75
78
  }
76
79
 
77
- runtime.on('application:worker:health:metrics', async (healthInfo) => {
80
+ // Remove old listener if it exists (for ICC recovery scenario)
81
+ if (healthMetricsListener) {
82
+ runtime.removeListener('application:worker:health:metrics', healthMetricsListener)
83
+ }
84
+
85
+ healthMetricsListener = async (healthInfo) => {
78
86
  if (!healthInfo) {
79
87
  app.log.error('No health metrics info received')
80
88
  }
@@ -128,7 +136,8 @@ async function healthSignals (app, _opts) {
128
136
  if (healthSignals.length > 0) {
129
137
  await sendHealthSignalsWithTimeout(serviceId, workerId, healthSignals)
130
138
  }
131
- })
139
+ }
140
+ runtime.on('application:worker:health:metrics', healthMetricsListener)
132
141
  }
133
142
  app.setupHealthSignals = setupHealthSignals
134
143
 
package/plugins/init.js CHANGED
@@ -55,6 +55,11 @@ async function initPlugin (app) {
55
55
  // If runtime already started (ICC recovery after startup), update its instance config
56
56
  if (app.watt?.runtime) {
57
57
  await app.watt.updateInstanceConfig(instanceConfig)
58
+ // Run setup methods that were skipped due to missing ICC config.
59
+ // Note that these calls are idempotent
60
+ await app.setupAlerts?.()
61
+ await app.setupHealthSignals?.()
62
+ await app.setupFlamegraphs?.()
58
63
  }
59
64
  }
60
65
  try {
package/test/init.test.js CHANGED
@@ -296,6 +296,14 @@ test('init plugin calls updateInstanceConfig on watt when ICC becomes available
296
296
  receivedInstanceConfig = config
297
297
  }
298
298
 
299
+ // Track if setup methods were called
300
+ let setupAlertsCalled = false
301
+ let setupHealthSignalsCalled = false
302
+ let setupFlamegraphsCalled = false
303
+ app.setupAlerts = async () => { setupAlertsCalled = true }
304
+ app.setupHealthSignals = async () => { setupHealthSignalsCalled = true }
305
+ app.setupFlamegraphs = async () => { setupFlamegraphsCalled = true }
306
+
299
307
  // Mock runtime to simulate that runtime has started
300
308
  app.watt.runtime = {}
301
309
 
@@ -310,6 +318,11 @@ test('init plugin calls updateInstanceConfig on watt when ICC becomes available
310
318
  equal(receivedInstanceConfig.applicationId, applicationId)
311
319
  equal(app.instanceConfig.applicationId, applicationId)
312
320
  equal(app.instanceId, instanceId)
321
+
322
+ // Verify setup methods were called for ICC recovery
323
+ equal(setupAlertsCalled, true)
324
+ equal(setupHealthSignalsCalled, true)
325
+ equal(setupFlamegraphsCalled, true)
313
326
  })
314
327
 
315
328
  test('updateInstanceConfig calls runtime.updateMetricsConfig with merged config', async (t) => {