@superhero/core 4.0.0-beta.7 → 4.0.0-beta.8
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/README.md +4 -5
- package/index.js +54 -50
- package/index.test.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,10 +44,9 @@ await core.bootstrap()
|
|
|
44
44
|
|
|
45
45
|
// Locate a service
|
|
46
46
|
const myService = core.locate('myService')
|
|
47
|
-
console.log(myService)
|
|
48
47
|
|
|
49
48
|
// Graceful shutdown
|
|
50
|
-
await core.
|
|
49
|
+
await core.destroy()
|
|
51
50
|
```
|
|
52
51
|
|
|
53
52
|
### Clustering Example
|
|
@@ -66,8 +65,8 @@ await core.add('./path/to/config.js')
|
|
|
66
65
|
// Bootstrap core
|
|
67
66
|
await core.bootstrap()
|
|
68
67
|
|
|
69
|
-
//
|
|
70
|
-
await core.
|
|
68
|
+
// Destroy core when done
|
|
69
|
+
await core.destroy()
|
|
71
70
|
```
|
|
72
71
|
|
|
73
72
|
---
|
|
@@ -157,7 +156,7 @@ new Core(branch: string = undefined)
|
|
|
157
156
|
- **`cluster(forks: number, branch?: number, version?: number): Promise<number>`**
|
|
158
157
|
Start clustering with the specified number of workers.
|
|
159
158
|
|
|
160
|
-
- **`
|
|
159
|
+
- **`destroy(): Promise<void>`**
|
|
161
160
|
Gracefully shutdown the core, its workers, and services.
|
|
162
161
|
|
|
163
162
|
#### Properties
|
package/index.js
CHANGED
|
@@ -20,7 +20,7 @@ export default class Core
|
|
|
20
20
|
this.branch = branch
|
|
21
21
|
this.basePath = this.locate.pathResolver.basePath // synchronize the base path.
|
|
22
22
|
this.locate.set('@superhero/config', this.config)
|
|
23
|
-
Core.#
|
|
23
|
+
Core.#setupDestroyer(this)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
#branch
|
|
@@ -56,69 +56,71 @@ export default class Core
|
|
|
56
56
|
static #cores = new Map
|
|
57
57
|
|
|
58
58
|
// Used to prevent multiple observers on the process events.
|
|
59
|
-
static #
|
|
59
|
+
static #destroyerIsSetup = false
|
|
60
60
|
|
|
61
|
-
// Used to prevent multiple
|
|
62
|
-
static #
|
|
61
|
+
// Used to prevent multiple destroy calls.
|
|
62
|
+
static #destroyerIsTriggered = false
|
|
63
63
|
|
|
64
|
-
static #
|
|
64
|
+
static #setupDestroyer(core)
|
|
65
65
|
{
|
|
66
66
|
Core.#cores.set(core, core)
|
|
67
67
|
|
|
68
|
-
if(false === Core.#
|
|
68
|
+
if(false === Core.#destroyerIsSetup)
|
|
69
69
|
{
|
|
70
|
-
Core.#
|
|
70
|
+
Core.#destroyerIsSetup = true
|
|
71
71
|
|
|
72
|
-
process.on('SIGINT', (signal) => Core.#
|
|
73
|
-
process.on('SIGTERM', (signal) => Core.#
|
|
72
|
+
process.on('SIGINT', (signal) => Core.#destroy(signal))
|
|
73
|
+
process.on('SIGTERM', (signal) => Core.#destroy(signal))
|
|
74
74
|
|
|
75
|
-
process.on('unhandledRejection', (reason) => Core.#
|
|
76
|
-
process.on('uncaughtException', (error) => Core.#
|
|
75
|
+
process.on('unhandledRejection', (reason) => Core.#destroy(false, reason))
|
|
76
|
+
process.on('uncaughtException', (error) => Core.#destroy(false, error))
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
* Attempts to
|
|
81
|
+
* Attempts to destroy all core instances gracefully.
|
|
82
82
|
*/
|
|
83
|
-
static async #
|
|
83
|
+
static async #destroy(signal, reason)
|
|
84
84
|
{
|
|
85
|
-
if(Core.#
|
|
85
|
+
if(Core.#destroyerIsTriggered)
|
|
86
86
|
{
|
|
87
87
|
Core.log.info`redundant shutdown signal ${signal} waiting for previous shutdown process to finalize…`
|
|
88
88
|
reason && Core.log.fail`${reason}`
|
|
89
89
|
return
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
Core.#
|
|
92
|
+
Core.#destroyerIsTriggered = true
|
|
93
93
|
|
|
94
|
-
signal
|
|
94
|
+
signal
|
|
95
|
+
? Core.log.info`graceful shutdown initiated ${signal}`
|
|
96
|
+
: Core.log.info`graceful shutdown initiated`
|
|
95
97
|
|
|
96
98
|
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
destroyedCores = [],
|
|
100
|
+
destroyRejects = []
|
|
99
101
|
|
|
100
102
|
for(const core of Core.#cores.values())
|
|
101
103
|
{
|
|
102
|
-
|
|
104
|
+
destroyedCores.push((async () =>
|
|
103
105
|
{
|
|
104
106
|
try
|
|
105
107
|
{
|
|
106
|
-
await core.
|
|
108
|
+
await core.destroy()
|
|
107
109
|
}
|
|
108
110
|
catch(error)
|
|
109
111
|
{
|
|
110
|
-
|
|
112
|
+
destroyRejects.push(error)
|
|
111
113
|
}
|
|
112
114
|
})())
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
await Promise.all(
|
|
117
|
+
await Promise.all(destroyedCores)
|
|
116
118
|
|
|
117
|
-
if(
|
|
119
|
+
if(destroyRejects.length)
|
|
118
120
|
{
|
|
119
121
|
const error = new Error('Failed to shutdown gracefully')
|
|
120
|
-
error.code = '
|
|
121
|
-
error.cause =
|
|
122
|
+
error.code = 'E_CORE_DESTROY_GRACEFUL'
|
|
123
|
+
error.cause = destroyRejects
|
|
122
124
|
Core.log.fail`${error}`
|
|
123
125
|
setImmediate(() => process.exit(1))
|
|
124
126
|
}
|
|
@@ -133,25 +135,25 @@ export default class Core
|
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
137
|
|
|
136
|
-
async
|
|
138
|
+
async destroy()
|
|
137
139
|
{
|
|
138
140
|
// First remove the core instance from the static core registry
|
|
139
|
-
// to prevent multiple
|
|
141
|
+
// to prevent multiple destroy calls.
|
|
140
142
|
Core.#cores.delete(this)
|
|
141
143
|
|
|
142
|
-
// Then
|
|
144
|
+
// Then destroy the core workers, if clustered, using a timeout.
|
|
143
145
|
const
|
|
144
|
-
timeout
|
|
145
|
-
timeoutError
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
timeout = this.config.find('core/destroy/timeout', 15e3),
|
|
147
|
+
timeoutError = new Error(`Failed to destroy core within ${(timeout/1e3).toFixed(1)}s`),
|
|
148
|
+
destroyTimeout = (ctx) => new Promise((_, reject) => ctx.id = setTimeout(() => reject(timeoutError), timeout)),
|
|
149
|
+
destroyedWorkers = [],
|
|
150
|
+
destroyRejects = []
|
|
149
151
|
|
|
150
|
-
timeoutError.code = '
|
|
152
|
+
timeoutError.code = 'E_CORE_DESTROY_TIMEOUT'
|
|
151
153
|
|
|
152
154
|
for(const id in this.#workers)
|
|
153
155
|
{
|
|
154
|
-
|
|
156
|
+
destroyedWorkers.push((async () =>
|
|
155
157
|
{
|
|
156
158
|
// Attempt to kill the worker.
|
|
157
159
|
this.#workers[id].kill()
|
|
@@ -162,42 +164,42 @@ export default class Core
|
|
|
162
164
|
|
|
163
165
|
try
|
|
164
166
|
{
|
|
165
|
-
if(
|
|
167
|
+
if(destroyedWorkers.length)
|
|
166
168
|
{
|
|
167
169
|
const timeout = {}
|
|
168
|
-
await Promise.race([
|
|
170
|
+
await Promise.race([ destroyTimeout(timeout), Promise.all(destroyedWorkers) ])
|
|
169
171
|
clearTimeout(timeout.id)
|
|
170
172
|
}
|
|
171
173
|
}
|
|
172
174
|
catch(reason)
|
|
173
175
|
{
|
|
174
|
-
const error = new Error(`Failed to
|
|
175
|
-
error.code = '
|
|
176
|
+
const error = new Error(`Failed to destroy workers`)
|
|
177
|
+
error.code = 'E_CORE_DESTROY_WORKERS'
|
|
176
178
|
error.cause = reason
|
|
177
|
-
|
|
179
|
+
destroyRejects.push(error)
|
|
178
180
|
}
|
|
179
181
|
|
|
180
|
-
// Then
|
|
182
|
+
// Then destroy the core locator and all loaded services, restricted by a
|
|
181
183
|
// timeout if it takes to long.
|
|
182
184
|
try
|
|
183
185
|
{
|
|
184
186
|
const timeout = {}
|
|
185
|
-
await Promise.race([
|
|
187
|
+
await Promise.race([ destroyTimeout(timeout), this.locate.destroy() ])
|
|
186
188
|
clearTimeout(timeout.id)
|
|
187
189
|
}
|
|
188
190
|
catch(reason)
|
|
189
191
|
{
|
|
190
|
-
const error = new Error(`Failed to
|
|
191
|
-
error.code = '
|
|
192
|
+
const error = new Error(`Failed to destroy locator`)
|
|
193
|
+
error.code = 'E_CORE_DESTROY_LOCATOR'
|
|
192
194
|
error.cause = reason
|
|
193
|
-
|
|
195
|
+
destroyRejects.push(error)
|
|
194
196
|
}
|
|
195
197
|
|
|
196
|
-
if(
|
|
198
|
+
if(destroyRejects.length)
|
|
197
199
|
{
|
|
198
|
-
const error = new Error(`Failed to
|
|
199
|
-
error.code = '
|
|
200
|
-
error.cause =
|
|
200
|
+
const error = new Error(`Failed to destroy core gracefully`)
|
|
201
|
+
error.code = 'E_CORE_DESTROY'
|
|
202
|
+
error.cause = destroyRejects
|
|
201
203
|
throw error
|
|
202
204
|
}
|
|
203
205
|
}
|
|
@@ -333,7 +335,7 @@ export default class Core
|
|
|
333
335
|
this.#workers[forkId].once('exit', this.#reloadWorker.bind(this, forkId, forkBranch, forkVersion))
|
|
334
336
|
this.#workers[forkId].sync = this.#createSynchoronizer(forkId)
|
|
335
337
|
|
|
336
|
-
Core.log.info
|
|
338
|
+
Core.log.info`${'CORE:' + forkId} ⇡ clustered`
|
|
337
339
|
|
|
338
340
|
try
|
|
339
341
|
{
|
|
@@ -346,6 +348,8 @@ export default class Core
|
|
|
346
348
|
error.cause = reason
|
|
347
349
|
throw error
|
|
348
350
|
}
|
|
351
|
+
|
|
352
|
+
Core.log.info`synchronized ${'CORE:' + forkId}`
|
|
349
353
|
}
|
|
350
354
|
|
|
351
355
|
return branch + forks
|
package/index.test.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superhero/core",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.8",
|
|
4
4
|
"description": "Core functionalities for the superhero framework.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@superhero/bootstrap": "^4.1.0",
|
|
13
13
|
"@superhero/config": "^4.1.2",
|
|
14
|
-
"@superhero/locator": "^4.
|
|
14
|
+
"@superhero/locator": "^4.2.0",
|
|
15
15
|
"@superhero/log": "^4.0.0"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|