-
Notifications
You must be signed in to change notification settings - Fork 128
/
Copy pathIDCardRenewer.js
138 lines (120 loc) · 3.75 KB
/
IDCardRenewer.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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
"use strict";
const Redis = require("../../service/cache/redis");
class IDCardRenewer {
constructor() {
this.redis = null;
this.refreshTimer = null;
this.nodeIdKey = null;
this.refreshDelay = 2000;
this.disposed = true; // Disposed until initialized
}
async init(config) {
if (!this.disposed) {
return; // Already initialized
}
this.disposed = false;
this.nodeIdKey = config.nodeIdKey;
this.refreshDelay = config.refreshDelay || 2000;
this.refreshMultiplier = config.refreshMultiplier;
/**
* Since we do not have access to the Kuzzle Context we can't use kuzzle.ask('core:cache:internal:*',...),
* so we need to have an instance of Redis similar to the one used in the Cache Engine
*/
try {
const redisConf = config.redis || {};
await this.initRedis(redisConf.config, redisConf.name);
} catch (error) {
// eslint-disable-next-line no-console
console.error(
`Failed to connect to redis, could not refresh ID card: ${error.message}`
);
this.parentPort.postMessage({
error: `Failed to connect to redis, could not refresh ID card: ${error.message}`,
});
return;
}
/**
* Early renew, otherwise we need to wait the refreshDelay before the first ID Card renewal
* which could be enough to make the ID Card expire.
*/
await this.renewIDCard();
if (!this.disposed) {
this.refreshTimer = setInterval(
this.renewIDCard.bind(this),
this.refreshDelay
);
}
// Notify that the worker is running and updating the ID Card
process.send({ initialized: true });
}
async initRedis(config, name) {
const redis = new Redis(config, name);
await redis.init();
this.redis = redis;
}
async renewIDCard() {
if (this.disposed) {
return; // Do not refresh ID Card when worker has been disposed
}
try {
const refreshed = await this.redis.commands.pexpire(
this.nodeIdKey,
this.refreshDelay * this.refreshMultiplier
);
// Unable to refresh the key in time before it expires
// => this node is too slow, we need to remove it from the cluster
if (refreshed === 0) {
await this.dispose();
process.send({
error: "Node too slow: ID card expired",
});
}
} catch (error) {
await this.dispose();
process.send({
error: `Failed to refresh ID Card: ${error.message}`,
});
}
}
async dispose() {
if (this.disposed) {
return; // Already disposed
}
this.disposed = true;
clearInterval(this.refreshTimer);
this.refreshTimer = null;
// If the worker is disposed before it had time to starts, redis service
// may not have been initialized
if (!this.redis) {
return;
}
try {
await this.redis.commands.del(this.nodeIdKey);
} catch (error) {
// eslint-disable-next-line no-console
console.error(
`Could not delete key '${this.nodeIdKey}' from redis: ${error.message}`
);
}
}
}
const idCardRenewer = new IDCardRenewer();
process.on("message", async (message) => {
if (message.action === "start") {
// Simulate basic global Kuzzle Context
global.kuzzle = { ...message.kuzzle };
global.kuzzle.log = {
debug: console.debug, // eslint-disable-line no-console
error: console.error, // eslint-disable-line no-console
info: console.info, // eslint-disable-line no-console
warn: console.warn, // eslint-disable-line no-console
};
// Should never throw
await idCardRenewer.init(message);
} else if (message.action === "dispose") {
// Should never throw
await idCardRenewer.dispose();
process.exit(0);
}
});
module.exports = { IDCardRenewer };