@platformatic/runtime 3.14.0 → 3.16.0
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/config.d.ts +2 -0
- package/index.js +13 -2
- package/lib/logger.js +141 -10
- package/lib/management-api.js +87 -4
- package/lib/runtime.js +50 -16
- package/package.json +15 -15
- package/schema.json +21 -1
package/config.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export type PlatformaticRuntimeConfig = {
|
|
|
20
20
|
id: string;
|
|
21
21
|
config?: string;
|
|
22
22
|
useHttp?: boolean;
|
|
23
|
+
reuseTcpPorts?: boolean;
|
|
23
24
|
workers?:
|
|
24
25
|
| number
|
|
25
26
|
| string
|
|
@@ -182,6 +183,7 @@ export type PlatformaticRuntimeConfig = {
|
|
|
182
183
|
rejectUnauthorized?: boolean;
|
|
183
184
|
};
|
|
184
185
|
};
|
|
186
|
+
reuseTcpPorts?: boolean;
|
|
185
187
|
startTimeout?: number;
|
|
186
188
|
restartOnError?: boolean | number;
|
|
187
189
|
exitOnUnhandledErrors?: boolean;
|
package/index.js
CHANGED
|
@@ -127,6 +127,7 @@ export async function loadApplicationsCommands () {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
export async function create (configOrRoot, sourceOrConfig, context) {
|
|
130
|
+
const setupSignals = context?.setupSignals ?? true
|
|
130
131
|
const config = await loadConfiguration(configOrRoot, sourceOrConfig, context)
|
|
131
132
|
|
|
132
133
|
if (inspector.url() && !config[kMetadata].env.VSCODE_INSPECTOR_OPTIONS) {
|
|
@@ -134,12 +135,20 @@ export async function create (configOrRoot, sourceOrConfig, context) {
|
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
let runtime = new Runtime(config, context)
|
|
137
|
-
|
|
138
|
+
if (setupSignals) {
|
|
139
|
+
handleSignal(runtime, config)
|
|
140
|
+
}
|
|
138
141
|
|
|
139
142
|
// Handle port handling
|
|
140
143
|
if (context?.start) {
|
|
141
144
|
let port = config.server?.port
|
|
142
145
|
|
|
146
|
+
await runtime.init()
|
|
147
|
+
|
|
148
|
+
if (context.reloaded) {
|
|
149
|
+
runtime.logger.info('The application has been successfully reloaded.')
|
|
150
|
+
}
|
|
151
|
+
|
|
143
152
|
while (true) {
|
|
144
153
|
try {
|
|
145
154
|
await runtime.start()
|
|
@@ -160,7 +169,9 @@ export async function create (configOrRoot, sourceOrConfig, context) {
|
|
|
160
169
|
|
|
161
170
|
config.server.port = ++port
|
|
162
171
|
runtime = new Runtime(config, context)
|
|
163
|
-
|
|
172
|
+
if (setupSignals) {
|
|
173
|
+
handleSignal(runtime, config)
|
|
174
|
+
}
|
|
164
175
|
}
|
|
165
176
|
}
|
|
166
177
|
}
|
package/lib/logger.js
CHANGED
|
@@ -5,19 +5,150 @@ import pretty from 'pino-pretty'
|
|
|
5
5
|
|
|
6
6
|
export { abstractLogger } from '@platformatic/foundation'
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
// A valid color in the ANSI 256 color palette - Adiacent colors are purposely different
|
|
9
|
+
const colors = [
|
|
10
|
+
196, // bright red
|
|
11
|
+
46, // bright green
|
|
12
|
+
33, // light blue
|
|
13
|
+
226, // bright yellow
|
|
14
|
+
201, // bright magenta
|
|
15
|
+
51, // cyan
|
|
16
|
+
208, // orange
|
|
17
|
+
118, // lime
|
|
18
|
+
39, // deep sky blue
|
|
19
|
+
220, // gold
|
|
20
|
+
129, // violet
|
|
21
|
+
82, // spring green
|
|
22
|
+
33, // blue
|
|
23
|
+
214, // amber
|
|
24
|
+
99, // orchid
|
|
25
|
+
190, // light yellow-green
|
|
26
|
+
45, // turquoise
|
|
27
|
+
197, // rose
|
|
28
|
+
50, // aqua
|
|
29
|
+
202, // orange-red
|
|
30
|
+
141, // lavender
|
|
31
|
+
154, // pale green
|
|
32
|
+
93, // pink
|
|
33
|
+
33, // light blue again (for spacing)
|
|
34
|
+
220, // gold
|
|
35
|
+
201, // magenta
|
|
36
|
+
46, // green
|
|
37
|
+
27, // navy blue
|
|
38
|
+
214, // amber
|
|
39
|
+
99, // orchid
|
|
40
|
+
190, // light yellow-green
|
|
41
|
+
39, // cyan-blue
|
|
42
|
+
200, // violet
|
|
43
|
+
82, // neon green
|
|
44
|
+
208, // orange
|
|
45
|
+
135, // purple
|
|
46
|
+
118, // lime
|
|
47
|
+
33, // bright blue
|
|
48
|
+
220, // gold
|
|
49
|
+
201, // bright magenta
|
|
50
|
+
46, // bright green
|
|
51
|
+
21, // bright blue
|
|
52
|
+
202, // orange-red
|
|
53
|
+
141, // purple
|
|
54
|
+
118, // spring green
|
|
55
|
+
208, // orange
|
|
56
|
+
93, // pink
|
|
57
|
+
190, // yellow-green
|
|
58
|
+
39, // cyan
|
|
59
|
+
196, // bright red
|
|
60
|
+
226 // bright yellow
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
function createLoggerContext () {
|
|
64
|
+
const context = {
|
|
65
|
+
colors: {},
|
|
66
|
+
maxLength: 0,
|
|
67
|
+
updatePrefixes (ids) {
|
|
68
|
+
context.colors = {}
|
|
69
|
+
context.maxLength = 0
|
|
70
|
+
|
|
71
|
+
for (const id of ids) {
|
|
72
|
+
context.maxLength = Math.max(context.maxLength, id.length)
|
|
73
|
+
let hash = 0
|
|
74
|
+
|
|
75
|
+
if (!pretty.isColorSupported && process.env.FORCE_COLOR !== 'true') {
|
|
76
|
+
context.colors[id] = ''
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Calculate the hash of the id to pick a color
|
|
81
|
+
for (const char of id) {
|
|
82
|
+
for (let i = 0; i < char.length; i++) {
|
|
83
|
+
hash = ((hash << 5) - hash) ^ char.charCodeAt(i)
|
|
84
|
+
hash = Math.abs(hash) % Number.MAX_SAFE_INTEGER
|
|
85
|
+
}
|
|
86
|
+
}
|
|
14
87
|
|
|
15
|
-
|
|
88
|
+
context.colors[id] = `\u001B[38;5;${colors[hash % colors.length]}m`
|
|
89
|
+
}
|
|
90
|
+
}
|
|
16
91
|
}
|
|
92
|
+
|
|
93
|
+
return context
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function createPrettifier (context) {
|
|
97
|
+
return pretty({
|
|
98
|
+
messageFormat (log, key) {
|
|
99
|
+
const { name, pid, hostname, caller, worker } = log
|
|
100
|
+
|
|
101
|
+
context.current = {
|
|
102
|
+
name,
|
|
103
|
+
pid,
|
|
104
|
+
hostname,
|
|
105
|
+
caller,
|
|
106
|
+
worker
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let prefix = ''
|
|
110
|
+
let color = ''
|
|
111
|
+
|
|
112
|
+
if (name) {
|
|
113
|
+
prefix = name.match(/:\d+$/) ? name : `${name}:${worker}`
|
|
114
|
+
color = context.colors[prefix] ?? ''
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
context.current.prefix = `(${pid}) ` + prefix.padStart(context.maxLength, ' ')
|
|
118
|
+
context.current.color = color
|
|
119
|
+
|
|
120
|
+
// We need to nullify all these so that prettifierMetadata in pino-pretty returns an empty string
|
|
121
|
+
log.name = undefined
|
|
122
|
+
log.pid = undefined
|
|
123
|
+
log.hostname = undefined
|
|
124
|
+
log.caller = undefined
|
|
125
|
+
log.worker = undefined
|
|
126
|
+
|
|
127
|
+
return log[key]
|
|
128
|
+
},
|
|
129
|
+
customPrettifiers: {
|
|
130
|
+
time (time) {
|
|
131
|
+
return `${context.current.color}[${time}]`
|
|
132
|
+
},
|
|
133
|
+
level (_u1, _u2, _u3, { label, labelColorized }) {
|
|
134
|
+
// No applications registered yet, no need to pad
|
|
135
|
+
if (context.maxLength === 0) {
|
|
136
|
+
return context.current.prefix + labelColorized
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const current = context.current
|
|
140
|
+
const level = current.caller ? current.caller : labelColorized.replace(label, label.padStart(6, ' '))
|
|
141
|
+
|
|
142
|
+
return `${current.prefix} | \u001B[0m ${level}`
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
})
|
|
17
146
|
}
|
|
18
147
|
|
|
19
148
|
// Create the runtime logger
|
|
20
149
|
export async function createLogger (config) {
|
|
150
|
+
const context = createLoggerContext()
|
|
151
|
+
|
|
21
152
|
const loggerConfig = { ...config.logger, transport: undefined }
|
|
22
153
|
if (config.logger.base === null) {
|
|
23
154
|
loggerConfig.base = undefined
|
|
@@ -28,7 +159,7 @@ export async function createLogger (config) {
|
|
|
28
159
|
if (config.logger.transport) {
|
|
29
160
|
cliStream = pino.transport(config.logger.transport)
|
|
30
161
|
} else if ((process.env.FORCE_TTY || isatty(1)) && usePrettyPrint()) {
|
|
31
|
-
cliStream =
|
|
162
|
+
cliStream = createPrettifier(context)
|
|
32
163
|
} else {
|
|
33
164
|
cliStream = pino.destination(1)
|
|
34
165
|
}
|
|
@@ -42,7 +173,7 @@ export async function createLogger (config) {
|
|
|
42
173
|
}
|
|
43
174
|
|
|
44
175
|
if (!config.managementApi) {
|
|
45
|
-
return [pino(loggerConfig, cliStream), cliStream]
|
|
176
|
+
return [pino(loggerConfig, cliStream), cliStream, context]
|
|
46
177
|
}
|
|
47
178
|
|
|
48
179
|
const multiStream = pino.multistream([{ stream: cliStream, level: loggerConfig.level }])
|
|
@@ -55,5 +186,5 @@ export async function createLogger (config) {
|
|
|
55
186
|
logsLimitCount = 1
|
|
56
187
|
}
|
|
57
188
|
|
|
58
|
-
return [pino(loggerConfig, multiStream), multiStream]
|
|
189
|
+
return [pino(loggerConfig, multiStream), multiStream, context]
|
|
59
190
|
}
|
package/lib/management-api.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import fastifyAccepts from '@fastify/accepts'
|
|
2
2
|
import fastifyWebsocket from '@fastify/websocket'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
applications as applicationSchema,
|
|
5
|
+
createDirectory,
|
|
6
|
+
kMetadata,
|
|
7
|
+
safeRemove,
|
|
8
|
+
validate
|
|
9
|
+
} from '@platformatic/foundation'
|
|
4
10
|
import fastify from 'fastify'
|
|
5
11
|
import { platform, tmpdir } from 'node:os'
|
|
6
12
|
import { join } from 'node:path'
|
|
7
13
|
import { setTimeout as sleep } from 'node:timers/promises'
|
|
8
14
|
import { createWebSocketStream } from 'ws'
|
|
15
|
+
import { prepareApplication } from './config.js'
|
|
9
16
|
|
|
10
17
|
const PLATFORMATIC_TMP_DIR = join(tmpdir(), 'platformatic', 'runtimes')
|
|
11
18
|
|
|
@@ -14,6 +21,26 @@ export async function managementApiPlugin (app, opts) {
|
|
|
14
21
|
|
|
15
22
|
const runtime = opts.runtime
|
|
16
23
|
|
|
24
|
+
async function deleteApplications (ids, reply) {
|
|
25
|
+
const validIds = runtime.getApplicationsIds()
|
|
26
|
+
|
|
27
|
+
for (const id of ids) {
|
|
28
|
+
if (!validIds.includes(id)) {
|
|
29
|
+
reply.code(404)
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
error: 'Not Found',
|
|
33
|
+
message: `Application with id "${id}" not found.`,
|
|
34
|
+
statusCode: 404
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const removed = await runtime.removeApplications(ids)
|
|
40
|
+
reply.code(202)
|
|
41
|
+
return removed
|
|
42
|
+
}
|
|
43
|
+
|
|
17
44
|
app.get('/status', async () => {
|
|
18
45
|
const status = runtime.getRuntimeStatus()
|
|
19
46
|
return { status }
|
|
@@ -23,8 +50,16 @@ export async function managementApiPlugin (app, opts) {
|
|
|
23
50
|
return runtime.getRuntimeMetadata()
|
|
24
51
|
})
|
|
25
52
|
|
|
26
|
-
app.get('/config', async
|
|
27
|
-
|
|
53
|
+
app.get('/config', async request => {
|
|
54
|
+
const metadata = request.query.metadata === 'true'
|
|
55
|
+
const rawConfig = await runtime.getRuntimeConfig(metadata)
|
|
56
|
+
|
|
57
|
+
if (metadata) {
|
|
58
|
+
const { [kMetadata]: __metadata, ...config } = rawConfig
|
|
59
|
+
return { ...config, __metadata }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return rawConfig
|
|
28
63
|
})
|
|
29
64
|
|
|
30
65
|
app.get('/env', async () => {
|
|
@@ -46,12 +81,60 @@ export async function managementApiPlugin (app, opts) {
|
|
|
46
81
|
return runtime.getApplications()
|
|
47
82
|
})
|
|
48
83
|
|
|
84
|
+
app.post('/applications', async (request, reply) => {
|
|
85
|
+
let applications = request.body
|
|
86
|
+
|
|
87
|
+
if (!Array.isArray(applications)) {
|
|
88
|
+
applications = [applications]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const config = runtime.getRuntimeConfig(true)
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
validate(applicationSchema, applications, {}, true, config[kMetadata].root)
|
|
95
|
+
} catch (err) {
|
|
96
|
+
reply.code(400)
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
statusCode: 400,
|
|
100
|
+
error: 'Bad Request',
|
|
101
|
+
message: 'Invalid applications configuration.',
|
|
102
|
+
validationErrors: err.validationErrors
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
for (let i = 0; i < applications.length; i++) {
|
|
107
|
+
applications[i] = await prepareApplication(config, applications[i])
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const created = await runtime.addApplications(applications, request.query.start !== 'false')
|
|
111
|
+
reply.code(201)
|
|
112
|
+
return created
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
app.delete('/applications', async (request, reply) => {
|
|
116
|
+
if (!Array.isArray(request.body)) {
|
|
117
|
+
reply.code(404)
|
|
118
|
+
return {
|
|
119
|
+
statusCode: 404,
|
|
120
|
+
error: 'Bad Request',
|
|
121
|
+
message: 'Invalid applications IDs.'
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return deleteApplications(request.body, reply)
|
|
126
|
+
})
|
|
127
|
+
|
|
49
128
|
app.get('/applications/:id', async request => {
|
|
50
129
|
const { id } = request.params
|
|
51
130
|
app.log.debug('get application details', { id })
|
|
52
131
|
return runtime.getApplicationDetails(id)
|
|
53
132
|
})
|
|
54
133
|
|
|
134
|
+
app.delete('/applications/:id', async (request, reply) => {
|
|
135
|
+
return deleteApplications([request.params.id], reply)
|
|
136
|
+
})
|
|
137
|
+
|
|
55
138
|
app.get('/applications/:id/config', async request => {
|
|
56
139
|
const { id } = request.params
|
|
57
140
|
app.log.debug('get application config', { id })
|
|
@@ -195,7 +278,7 @@ export async function managementApiPlugin (app, opts) {
|
|
|
195
278
|
})
|
|
196
279
|
})
|
|
197
280
|
|
|
198
|
-
app.get('/logs/live', { websocket: true }, async
|
|
281
|
+
app.get('/logs/live', { websocket: true }, async socket => {
|
|
199
282
|
runtime.addLoggerDestination(createWebSocketStream(socket))
|
|
200
283
|
})
|
|
201
284
|
}
|
package/lib/runtime.js
CHANGED
|
@@ -47,9 +47,9 @@ import { startPrometheusServer } from './prom-server.js'
|
|
|
47
47
|
import { startScheduler } from './scheduler.js'
|
|
48
48
|
import { createSharedStore } from './shared-http-cache.js'
|
|
49
49
|
import { version } from './version.js'
|
|
50
|
+
import { HealthSignalsQueue } from './worker/health-signals.js'
|
|
50
51
|
import { sendViaITC, waitEventFromITC } from './worker/itc.js'
|
|
51
52
|
import { RoundRobinMap } from './worker/round-robin-map.js'
|
|
52
|
-
import { HealthSignalsQueue } from './worker/health-signals.js'
|
|
53
53
|
import {
|
|
54
54
|
kApplicationId,
|
|
55
55
|
kConfig,
|
|
@@ -60,11 +60,11 @@ import {
|
|
|
60
60
|
kITC,
|
|
61
61
|
kLastHealthCheckELU,
|
|
62
62
|
kStderrMarker,
|
|
63
|
+
kWorkerHealthSignals,
|
|
63
64
|
kWorkerId,
|
|
64
65
|
kWorkersBroadcast,
|
|
65
66
|
kWorkerStartTime,
|
|
66
|
-
kWorkerStatus
|
|
67
|
-
kWorkerHealthSignals
|
|
67
|
+
kWorkerStatus
|
|
68
68
|
} from './worker/symbols.js'
|
|
69
69
|
|
|
70
70
|
const kWorkerFile = join(import.meta.dirname, 'worker/main.js')
|
|
@@ -84,6 +84,7 @@ export class Runtime extends EventEmitter {
|
|
|
84
84
|
error
|
|
85
85
|
|
|
86
86
|
#loggerDestination
|
|
87
|
+
#loggerContext
|
|
87
88
|
#stdio
|
|
88
89
|
|
|
89
90
|
#status // starting, started, stopping, stopped, closed
|
|
@@ -201,9 +202,10 @@ export class Runtime extends EventEmitter {
|
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
// Create the logger
|
|
204
|
-
const [logger, destination] = await createLogger(config)
|
|
205
|
+
const [logger, destination, context] = await createLogger(config)
|
|
205
206
|
this.logger = logger
|
|
206
207
|
this.#loggerDestination = destination
|
|
208
|
+
this.#loggerContext = context
|
|
207
209
|
|
|
208
210
|
this.#createWorkersBroadcastChannel()
|
|
209
211
|
|
|
@@ -358,6 +360,7 @@ export class Runtime extends EventEmitter {
|
|
|
358
360
|
|
|
359
361
|
this.logger = abstractLogger
|
|
360
362
|
this.#loggerDestination = null
|
|
363
|
+
this.#loggerContext = null
|
|
361
364
|
}
|
|
362
365
|
|
|
363
366
|
this.#updateStatus('closed')
|
|
@@ -466,12 +469,21 @@ export class Runtime extends EventEmitter {
|
|
|
466
469
|
await executeInParallel(this.#setupApplication.bind(this), setupInvocations, this.#concurrency)
|
|
467
470
|
|
|
468
471
|
for (const application of applications) {
|
|
472
|
+
this.logger.info(`Added application "${application.id}"${application.entrypoint ? ' (entrypoint)' : ''}.`)
|
|
469
473
|
this.emitAndNotify('application:added', application)
|
|
470
474
|
}
|
|
471
475
|
|
|
472
476
|
if (start) {
|
|
473
477
|
await this.startApplications(toStart)
|
|
474
478
|
}
|
|
479
|
+
|
|
480
|
+
const created = []
|
|
481
|
+
for (const { id } of applications) {
|
|
482
|
+
created.push(await this.getApplicationDetails(id))
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
this.#updateLoggingPrefixes()
|
|
486
|
+
return created
|
|
475
487
|
}
|
|
476
488
|
|
|
477
489
|
async removeApplications (applications, silent = false) {
|
|
@@ -479,6 +491,13 @@ export class Runtime extends EventEmitter {
|
|
|
479
491
|
throw new CannotRemoveEntrypointError()
|
|
480
492
|
}
|
|
481
493
|
|
|
494
|
+
const removed = []
|
|
495
|
+
for (const application of applications) {
|
|
496
|
+
const details = await this.getApplicationDetails(application)
|
|
497
|
+
details.status = 'removed'
|
|
498
|
+
removed.push(details)
|
|
499
|
+
}
|
|
500
|
+
|
|
482
501
|
await this.stopApplications(applications, silent, true)
|
|
483
502
|
|
|
484
503
|
for (const application of applications) {
|
|
@@ -487,8 +506,12 @@ export class Runtime extends EventEmitter {
|
|
|
487
506
|
}
|
|
488
507
|
|
|
489
508
|
for (const application of applications) {
|
|
509
|
+
this.logger.info(`Removed application "${application}".`)
|
|
490
510
|
this.emitAndNotify('application:removed', application)
|
|
491
511
|
}
|
|
512
|
+
|
|
513
|
+
this.#updateLoggingPrefixes()
|
|
514
|
+
return removed
|
|
492
515
|
}
|
|
493
516
|
|
|
494
517
|
async startApplications (applicationsToStart, silent = false) {
|
|
@@ -1182,7 +1205,7 @@ export class Runtime extends EventEmitter {
|
|
|
1182
1205
|
throw e
|
|
1183
1206
|
}
|
|
1184
1207
|
|
|
1185
|
-
const { entrypoint, localUrl } = application[kConfig]
|
|
1208
|
+
const { entrypoint, localUrl, config, path } = application[kConfig]
|
|
1186
1209
|
|
|
1187
1210
|
const status = await sendViaITC(application, 'getStatus')
|
|
1188
1211
|
const { type, version, dependencies } = await sendViaITC(application, 'getApplicationInfo')
|
|
@@ -1190,6 +1213,8 @@ export class Runtime extends EventEmitter {
|
|
|
1190
1213
|
const applicationDetails = {
|
|
1191
1214
|
id,
|
|
1192
1215
|
type,
|
|
1216
|
+
config,
|
|
1217
|
+
path,
|
|
1193
1218
|
status,
|
|
1194
1219
|
dependencies,
|
|
1195
1220
|
version,
|
|
@@ -1587,7 +1612,7 @@ export class Runtime extends EventEmitter {
|
|
|
1587
1612
|
await this.startApplication(applicationId)
|
|
1588
1613
|
}
|
|
1589
1614
|
|
|
1590
|
-
this.logger.info(`The application "${applicationId}" has been successfully reloaded
|
|
1615
|
+
this.logger.info(`The application "${applicationId}" has been successfully reloaded.`)
|
|
1591
1616
|
this.emitAndNotify('application:worker:reloaded', eventPayload)
|
|
1592
1617
|
|
|
1593
1618
|
if (applicationConfig.entrypoint) {
|
|
@@ -1666,13 +1691,7 @@ export class Runtime extends EventEmitter {
|
|
|
1666
1691
|
|
|
1667
1692
|
const healthConfig = worker[kConfig].health
|
|
1668
1693
|
|
|
1669
|
-
let {
|
|
1670
|
-
maxELU,
|
|
1671
|
-
maxHeapUsed,
|
|
1672
|
-
maxHeapTotal,
|
|
1673
|
-
maxUnhealthyChecks,
|
|
1674
|
-
interval
|
|
1675
|
-
} = worker[kConfig].health
|
|
1694
|
+
let { maxELU, maxHeapUsed, maxHeapTotal, maxUnhealthyChecks, interval } = worker[kConfig].health
|
|
1676
1695
|
|
|
1677
1696
|
if (typeof maxHeapTotal === 'string') {
|
|
1678
1697
|
maxHeapTotal = parseMemorySize(maxHeapTotal)
|
|
@@ -1682,7 +1701,7 @@ export class Runtime extends EventEmitter {
|
|
|
1682
1701
|
interval = 1000
|
|
1683
1702
|
this.logger.warn(
|
|
1684
1703
|
`The health check interval for the "${errorLabel}" is set to ${healthConfig.interval}ms. ` +
|
|
1685
|
-
|
|
1704
|
+
'The minimum health check interval is 1s. It will be set to 1000ms.'
|
|
1686
1705
|
)
|
|
1687
1706
|
}
|
|
1688
1707
|
|
|
@@ -1741,14 +1760,14 @@ export class Runtime extends EventEmitter {
|
|
|
1741
1760
|
|
|
1742
1761
|
this.logger.error(
|
|
1743
1762
|
{ elu: health.elu, maxELU, memoryUsage: health.heapUsed, maxMemoryUsage: maxHeapUsed },
|
|
1744
|
-
|
|
1763
|
+
`The ${errorLabel} is unhealthy. Replacing it ...`
|
|
1745
1764
|
)
|
|
1746
1765
|
|
|
1747
1766
|
await this.#replaceWorker(config, applicationConfig, workersCount, id, index, worker)
|
|
1748
1767
|
} catch (e) {
|
|
1749
1768
|
this.logger.error(
|
|
1750
1769
|
{ elu: health.elu, maxELU, memoryUsage: health.heapUsed, maxMemoryUsage: maxHeapUsed },
|
|
1751
|
-
|
|
1770
|
+
`Cannot replace the ${errorLabel}. Forcefully terminating it ...`
|
|
1752
1771
|
)
|
|
1753
1772
|
|
|
1754
1773
|
worker.terminate()
|
|
@@ -2320,6 +2339,8 @@ export class Runtime extends EventEmitter {
|
|
|
2320
2339
|
throw result.reason
|
|
2321
2340
|
}
|
|
2322
2341
|
}
|
|
2342
|
+
|
|
2343
|
+
this.#updateLoggingPrefixes()
|
|
2323
2344
|
}
|
|
2324
2345
|
|
|
2325
2346
|
async #updateApplicationConfigHealth (applicationId, health) {
|
|
@@ -2694,4 +2715,17 @@ export class Runtime extends EventEmitter {
|
|
|
2694
2715
|
worker[kWorkerHealthSignals] ??= new HealthSignalsQueue()
|
|
2695
2716
|
worker[kWorkerHealthSignals].add(signals)
|
|
2696
2717
|
}
|
|
2718
|
+
|
|
2719
|
+
#updateLoggingPrefixes () {
|
|
2720
|
+
if (!this.#loggerContext) {
|
|
2721
|
+
return
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
const ids = []
|
|
2725
|
+
for (const worker of this.#workers.values()) {
|
|
2726
|
+
ids.push(`${worker[kFullId]}`)
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
this.#loggerContext.updatePrefixes(ids)
|
|
2730
|
+
}
|
|
2697
2731
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"typescript": "^5.5.4",
|
|
36
36
|
"undici-oidc-interceptor": "^0.5.0",
|
|
37
37
|
"why-is-node-running": "^2.2.2",
|
|
38
|
-
"@platformatic/
|
|
39
|
-
"@platformatic/
|
|
40
|
-
"@platformatic/
|
|
41
|
-
"@platformatic/node": "3.
|
|
42
|
-
"@platformatic/
|
|
43
|
-
"@platformatic/sql-graphql": "3.
|
|
44
|
-
"@platformatic/
|
|
45
|
-
"@platformatic/
|
|
38
|
+
"@platformatic/composer": "3.16.0",
|
|
39
|
+
"@platformatic/db": "3.16.0",
|
|
40
|
+
"@platformatic/gateway": "3.16.0",
|
|
41
|
+
"@platformatic/node": "3.16.0",
|
|
42
|
+
"@platformatic/sql-mapper": "3.16.0",
|
|
43
|
+
"@platformatic/sql-graphql": "3.16.0",
|
|
44
|
+
"@platformatic/wattpm-pprof-capture": "3.16.0",
|
|
45
|
+
"@platformatic/service": "3.16.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@fastify/accepts": "^5.0.0",
|
|
@@ -73,12 +73,12 @@
|
|
|
73
73
|
"undici": "^7.0.0",
|
|
74
74
|
"undici-thread-interceptor": "^0.15.0",
|
|
75
75
|
"ws": "^8.16.0",
|
|
76
|
-
"@platformatic/basic": "3.
|
|
77
|
-
"@platformatic/
|
|
78
|
-
"@platformatic/
|
|
79
|
-
"@platformatic/
|
|
80
|
-
"@platformatic/
|
|
81
|
-
"@platformatic/
|
|
76
|
+
"@platformatic/basic": "3.16.0",
|
|
77
|
+
"@platformatic/generators": "3.16.0",
|
|
78
|
+
"@platformatic/itc": "3.16.0",
|
|
79
|
+
"@platformatic/foundation": "3.16.0",
|
|
80
|
+
"@platformatic/metrics": "3.16.0",
|
|
81
|
+
"@platformatic/telemetry": "3.16.0"
|
|
82
82
|
},
|
|
83
83
|
"engines": {
|
|
84
84
|
"node": ">=22.19.0"
|
package/schema.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.
|
|
2
|
+
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.16.0.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"title": "Platformatic Runtime Config",
|
|
5
5
|
"type": "object",
|
|
@@ -64,6 +64,10 @@
|
|
|
64
64
|
"useHttp": {
|
|
65
65
|
"type": "boolean"
|
|
66
66
|
},
|
|
67
|
+
"reuseTcpPorts": {
|
|
68
|
+
"type": "boolean",
|
|
69
|
+
"default": true
|
|
70
|
+
},
|
|
67
71
|
"workers": {
|
|
68
72
|
"anyOf": [
|
|
69
73
|
{
|
|
@@ -342,6 +346,10 @@
|
|
|
342
346
|
"useHttp": {
|
|
343
347
|
"type": "boolean"
|
|
344
348
|
},
|
|
349
|
+
"reuseTcpPorts": {
|
|
350
|
+
"type": "boolean",
|
|
351
|
+
"default": true
|
|
352
|
+
},
|
|
345
353
|
"workers": {
|
|
346
354
|
"anyOf": [
|
|
347
355
|
{
|
|
@@ -618,6 +626,10 @@
|
|
|
618
626
|
"useHttp": {
|
|
619
627
|
"type": "boolean"
|
|
620
628
|
},
|
|
629
|
+
"reuseTcpPorts": {
|
|
630
|
+
"type": "boolean",
|
|
631
|
+
"default": true
|
|
632
|
+
},
|
|
621
633
|
"workers": {
|
|
622
634
|
"anyOf": [
|
|
623
635
|
{
|
|
@@ -894,6 +906,10 @@
|
|
|
894
906
|
"useHttp": {
|
|
895
907
|
"type": "boolean"
|
|
896
908
|
},
|
|
909
|
+
"reuseTcpPorts": {
|
|
910
|
+
"type": "boolean",
|
|
911
|
+
"default": true
|
|
912
|
+
},
|
|
897
913
|
"workers": {
|
|
898
914
|
"anyOf": [
|
|
899
915
|
{
|
|
@@ -1464,6 +1480,10 @@
|
|
|
1464
1480
|
},
|
|
1465
1481
|
"additionalProperties": false
|
|
1466
1482
|
},
|
|
1483
|
+
"reuseTcpPorts": {
|
|
1484
|
+
"type": "boolean",
|
|
1485
|
+
"default": true
|
|
1486
|
+
},
|
|
1467
1487
|
"startTimeout": {
|
|
1468
1488
|
"default": 30000,
|
|
1469
1489
|
"type": "number",
|