forked from taskcluster/taskcluster
-
Notifications
You must be signed in to change notification settings - Fork 0
/
shutdown_manager.js
91 lines (77 loc) · 2.55 KB
/
shutdown_manager.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
const { EventEmitter } = require('events');
/**
* Manage worker shutdown, either from a graceful-termination message from
* the worker-runner, or due to idleness.
*
* This takes signals from other components in its `onXxx` methods, and
* produces a value from `shouldExit` that directs the TaskListener's behavior.
*
* Note that the worker is assumed to be working on startup, to avoid premature
* shutdown due to a very-short afterIdleSeconds configuration.
*/
class ShutdownManager extends EventEmitter {
constructor(host, config) {
super();
this.idleTimeout = null;
this.host = host;
this.config = config;
this.monitor = config.monitor;
this.exit = false;
this.shutdownCfg = config.shutdown || {};
this.nodeTerminationPoll = this.shutdownCfg.nodeTerminationPoll || 5000;
process.on('SIGTERM', () => {
this.config.log('Terminating worker due to SIGTERM');
this._setExit('immediate');
});
}
// Should we exit the process? Returns false, "graceful", or "immediate".
shouldExit() {
return this.exit;
}
/**
* The worker is idle at the moment. If it has just become idle,
* then this will start an idle timer. It's safe to call this repeatedly.
*/
onIdle() {
if (!this.shutdownCfg.enabled || this.idleTimeout) {
return;
}
let afterIdleSeconds = this.shutdownCfg.afterIdleSeconds;
this.config.log('uptime', {
uptime: this.host.billingCycleUptime(),
idleInterval: afterIdleSeconds,
});
this.config.log('worker idle', { afterIdleSeconds });
if (afterIdleSeconds) {
this.idleTimeout = setTimeout(() => {
// use a graceful timeout as a failsafe: if somehow a task
// gets started after this, let's let it finish.
this._setExit('graceful');
}, afterIdleSeconds * 1000);
}
}
/**
* The worker is working at the moment. This cancels any running idle
* timers. It's safe to call this repeatedly.
*/
onWorking() {
if (!this.shutdownCfg.enabled || !this.idleTimeout || this.shouldExit()) {
return;
}
this.config.log('worker working');
clearTimeout(this.idleTimeout);
this.idleTimeout = null;
}
// Call this on a graceful-termination message from worker-runner
onGracefulTermination(graceful) {
this._setExit(graceful ? 'graceful' : 'immediate');
}
// Set this.exit, but never downgrade (e.g., immediate -> graceful)
_setExit(exit) {
if (this.exit === 'immediate' && exit === 'graceful') {
return;
}
this.exit = exit;
}
}
module.exports = ShutdownManager;