@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 +1 -1
- package/plugins/alerts.js +23 -5
- package/plugins/flamegraphs.js +5 -0
- package/plugins/health-signals.js +11 -2
- package/plugins/init.js +5 -0
- package/test/init.test.js +13 -0
package/package.json
CHANGED
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
200
|
+
healthListener = processHealthInfo
|
|
184
201
|
}
|
|
202
|
+
runtime.on(healthEventName, healthListener)
|
|
185
203
|
}
|
|
186
204
|
app.setupAlerts = setupAlerts
|
|
187
205
|
}
|
package/plugins/flamegraphs.js
CHANGED
|
@@ -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
|
-
|
|
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) => {
|