kuzzle 2.14.14 → 2.15.2
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/lib/api/controllers/adminController.js +5 -0
- package/lib/api/controllers/documentController.js +1 -5
- package/lib/cluster/idCardHandler.d.ts +140 -0
- package/lib/cluster/idCardHandler.js +219 -172
- package/lib/cluster/node.js +19 -8
- package/lib/cluster/protobuf/sync.proto +4 -0
- package/lib/cluster/subscriber.js +10 -1
- package/lib/cluster/workers/IDCardRenewer.js +13 -7
- package/lib/config/httpRoutes.js +1 -0
- package/lib/core/cache/cacheEngine.js +9 -0
- package/lib/core/realtime/hotelClerk.js +6 -6
- package/lib/core/realtime/notifier.js +16 -18
- package/lib/core/storage/clientAdapter.js +11 -5
- package/lib/core/storage/indexCache.d.ts +55 -0
- package/lib/core/storage/indexCache.js +97 -130
- package/lib/kuzzle/kuzzle.js +2 -2
- package/lib/service/storage/elasticsearch.js +14 -9
- package/package-lock.json +175 -134
- package/package.json +13 -13
|
@@ -37,6 +37,7 @@ class AdminController extends NativeController {
|
|
|
37
37
|
'loadFixtures',
|
|
38
38
|
'loadMappings',
|
|
39
39
|
'loadSecurities',
|
|
40
|
+
'refreshIndexCache',
|
|
40
41
|
'resetCache',
|
|
41
42
|
'resetDatabase',
|
|
42
43
|
'resetSecurity',
|
|
@@ -46,6 +47,10 @@ class AdminController extends NativeController {
|
|
|
46
47
|
this.shuttingDown = false;
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
async refreshIndexCache () {
|
|
51
|
+
await global.kuzzle.ask('core:storage:public:cache:refresh');
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
/**
|
|
50
55
|
* Reset Redis cache
|
|
51
56
|
*/
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import '../types';
|
|
2
|
+
export declare type SerializedIdCard = {
|
|
3
|
+
id: string;
|
|
4
|
+
ip: string;
|
|
5
|
+
birthdate: number;
|
|
6
|
+
topology: string[];
|
|
7
|
+
};
|
|
8
|
+
export declare class IdCard {
|
|
9
|
+
/**
|
|
10
|
+
* Node unique identifier
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
*
|
|
14
|
+
* knode-pensive-einstein-844221
|
|
15
|
+
*/
|
|
16
|
+
private id;
|
|
17
|
+
/**
|
|
18
|
+
* Node IP address
|
|
19
|
+
*/
|
|
20
|
+
private ip;
|
|
21
|
+
/**
|
|
22
|
+
* Node creation timestamp
|
|
23
|
+
*/
|
|
24
|
+
private birthdate;
|
|
25
|
+
/**
|
|
26
|
+
* Node known topology composed of node IDs
|
|
27
|
+
*
|
|
28
|
+
* Set<node-id>
|
|
29
|
+
*/
|
|
30
|
+
topology: Set<string>;
|
|
31
|
+
constructor({ id, ip, birthdate, topology }: SerializedIdCard);
|
|
32
|
+
serialize(): SerializedIdCard;
|
|
33
|
+
static unserialize(serialized: SerializedIdCard): IdCard;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Handles the ID Key stored in Redis, holding node information
|
|
37
|
+
*/
|
|
38
|
+
export declare class ClusterIdCardHandler {
|
|
39
|
+
/**
|
|
40
|
+
* Node instance. Represents the local node.
|
|
41
|
+
*/
|
|
42
|
+
private node;
|
|
43
|
+
/**
|
|
44
|
+
* Local node ID Card
|
|
45
|
+
*/
|
|
46
|
+
private idCard;
|
|
47
|
+
/**
|
|
48
|
+
* Local node IP address
|
|
49
|
+
*/
|
|
50
|
+
private ip;
|
|
51
|
+
/**
|
|
52
|
+
* Delay for refreshing the ID Card. The heartbeat timer is run on this delay
|
|
53
|
+
* and the node ID Card should be available on Redis otherwise it will be evicted.
|
|
54
|
+
*/
|
|
55
|
+
private refreshDelay;
|
|
56
|
+
/**
|
|
57
|
+
* Multiplier used to ensure the node has enough time to refresh it's ID Card
|
|
58
|
+
* before the ID Card refresh delay
|
|
59
|
+
*/
|
|
60
|
+
private refreshMultiplier;
|
|
61
|
+
/**
|
|
62
|
+
* Worker thread in charge of refreshing the ID Card once the node has started
|
|
63
|
+
*/
|
|
64
|
+
private refreshWorker;
|
|
65
|
+
/**
|
|
66
|
+
* Hold the timer in charge of refreshing the ID Card before the worker starts
|
|
67
|
+
*/
|
|
68
|
+
private refreshTimer;
|
|
69
|
+
/**
|
|
70
|
+
* Local node ID
|
|
71
|
+
*/
|
|
72
|
+
private nodeId;
|
|
73
|
+
/**
|
|
74
|
+
* Local node Redis key
|
|
75
|
+
*/
|
|
76
|
+
private nodeIdKey;
|
|
77
|
+
/**
|
|
78
|
+
* Flag to prevent updating the id card if it has been disposed.
|
|
79
|
+
* Prevents race condition if a topology update occurs at the same time as
|
|
80
|
+
* the id card is been disposed because the node is evicting itself from the
|
|
81
|
+
* cluster
|
|
82
|
+
*/
|
|
83
|
+
private disposed;
|
|
84
|
+
constructor(node: any);
|
|
85
|
+
/**
|
|
86
|
+
* Generates and reserves a unique ID for this node instance.
|
|
87
|
+
* Makes sure that the ID is not already taken by another node instance.
|
|
88
|
+
*/
|
|
89
|
+
createIdCard(): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Helper method to mock worker instantiation in unit tests
|
|
92
|
+
*/
|
|
93
|
+
private constructWorker;
|
|
94
|
+
/**
|
|
95
|
+
* Start refreshing the ID Card before the worker starts to ensure the ID Card
|
|
96
|
+
* is refreshed.
|
|
97
|
+
*
|
|
98
|
+
* Once the worker starts, this timer will be stopped.
|
|
99
|
+
*/
|
|
100
|
+
private startTemporaryRefresh;
|
|
101
|
+
dispose(): Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Retrieves the ID cards from other nodes
|
|
104
|
+
*
|
|
105
|
+
* Each node store it's ID Card under a specific key name. When Redis database
|
|
106
|
+
* is growing, searching for those keys can be quite expensive and can slow down
|
|
107
|
+
* the handshake process.
|
|
108
|
+
*
|
|
109
|
+
* We are storing a set containing ID Card's keys under a set so when a new node
|
|
110
|
+
* is started, it can directly get the other nodes ID Cards with SMEMBERS and
|
|
111
|
+
* then MGET instead of using SCAN.
|
|
112
|
+
*
|
|
113
|
+
* When a new node retrieve the ID Card's keys from the set, it try to get them
|
|
114
|
+
* with MGET, those who cannot be retrieved are expired ID Cards so the node update
|
|
115
|
+
* the set accordingly.
|
|
116
|
+
*
|
|
117
|
+
* @return {Array.<IdCard>}
|
|
118
|
+
*/
|
|
119
|
+
getRemoteIdCards(): Promise<IdCard[]>;
|
|
120
|
+
/**
|
|
121
|
+
* Adds a remote node IdCard to the node known topology
|
|
122
|
+
*/
|
|
123
|
+
addNode(id: string): Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Removes a remote node IdCard from the node known topology
|
|
126
|
+
*/
|
|
127
|
+
removeNode(id: string): Promise<void>;
|
|
128
|
+
/**
|
|
129
|
+
* Store the key under which this node ID Card is stored inside the set.
|
|
130
|
+
*
|
|
131
|
+
* This set is an index to retrieve ID Cards faster.
|
|
132
|
+
*/
|
|
133
|
+
addIdCardToIndex(): Promise<void>;
|
|
134
|
+
/**
|
|
135
|
+
* Saves the local node IdCard into Redis
|
|
136
|
+
*
|
|
137
|
+
* @returns True if the key was set
|
|
138
|
+
*/
|
|
139
|
+
private save;
|
|
140
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/*
|
|
2
3
|
* Kuzzle, a backend software, self-hostable and ready to use
|
|
3
4
|
* to power modern apps
|
|
@@ -18,188 +19,234 @@
|
|
|
18
19
|
* See the License for the specific language governing permissions and
|
|
19
20
|
* limitations under the License.
|
|
20
21
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.ClusterIdCardHandler = exports.IdCard = void 0;
|
|
27
|
+
const name_generator_1 = require("../util/name-generator");
|
|
28
|
+
const worker_threads_1 = require("worker_threads");
|
|
29
|
+
const bluebird_1 = __importDefault(require("bluebird"));
|
|
30
|
+
require("../types");
|
|
27
31
|
const REDIS_PREFIX = '{cluster/node}/';
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @typedef {IdCard}
|
|
31
|
-
*/
|
|
32
|
+
const REDIS_ID_CARDS_INDEX = REDIS_PREFIX + 'id-cards-index';
|
|
32
33
|
class IdCard {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
id: this.id,
|
|
51
|
-
ip: this.ip,
|
|
52
|
-
topology: Array.from(this.topology),
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
static unserialize (value) {
|
|
57
|
-
return new IdCard(JSON.parse(value));
|
|
58
|
-
}
|
|
34
|
+
constructor({ id, ip, birthdate, topology }) {
|
|
35
|
+
this.id = id;
|
|
36
|
+
this.ip = ip;
|
|
37
|
+
this.birthdate = birthdate;
|
|
38
|
+
this.topology = new Set(topology);
|
|
39
|
+
}
|
|
40
|
+
serialize() {
|
|
41
|
+
return {
|
|
42
|
+
birthdate: this.birthdate,
|
|
43
|
+
id: this.id,
|
|
44
|
+
ip: this.ip,
|
|
45
|
+
topology: Array.from(this.topology),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
static unserialize(serialized) {
|
|
49
|
+
return new IdCard(serialized);
|
|
50
|
+
}
|
|
59
51
|
}
|
|
60
|
-
|
|
52
|
+
exports.IdCard = IdCard;
|
|
61
53
|
/**
|
|
62
54
|
* Handles the ID Key stored in Redis, holding node information
|
|
63
55
|
*/
|
|
64
56
|
class ClusterIdCardHandler {
|
|
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
|
-
|
|
57
|
+
constructor(node) {
|
|
58
|
+
/**
|
|
59
|
+
* Local node ID Card
|
|
60
|
+
*/
|
|
61
|
+
this.idCard = null;
|
|
62
|
+
/**
|
|
63
|
+
* Multiplier used to ensure the node has enough time to refresh it's ID Card
|
|
64
|
+
* before the ID Card refresh delay
|
|
65
|
+
*/
|
|
66
|
+
this.refreshMultiplier = 2;
|
|
67
|
+
/**
|
|
68
|
+
* Worker thread in charge of refreshing the ID Card once the node has started
|
|
69
|
+
*/
|
|
70
|
+
this.refreshWorker = null;
|
|
71
|
+
/**
|
|
72
|
+
* Hold the timer in charge of refreshing the ID Card before the worker starts
|
|
73
|
+
*/
|
|
74
|
+
this.refreshTimer = null;
|
|
75
|
+
/**
|
|
76
|
+
* Local node ID
|
|
77
|
+
*/
|
|
78
|
+
this.nodeId = null;
|
|
79
|
+
/**
|
|
80
|
+
* Local node Redis key
|
|
81
|
+
*/
|
|
82
|
+
this.nodeIdKey = null;
|
|
83
|
+
/**
|
|
84
|
+
* Flag to prevent updating the id card if it has been disposed.
|
|
85
|
+
* Prevents race condition if a topology update occurs at the same time as
|
|
86
|
+
* the id card is been disposed because the node is evicting itself from the
|
|
87
|
+
* cluster
|
|
88
|
+
*/
|
|
89
|
+
this.disposed = false;
|
|
90
|
+
this.node = node;
|
|
91
|
+
this.ip = node.ip;
|
|
92
|
+
this.refreshDelay = node.heartbeatDelay;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Generates and reserves a unique ID for this node instance.
|
|
96
|
+
* Makes sure that the ID is not already taken by another node instance.
|
|
97
|
+
*/
|
|
98
|
+
async createIdCard() {
|
|
99
|
+
let reserved = false;
|
|
100
|
+
do {
|
|
101
|
+
this.nodeId = (0, name_generator_1.generateRandomName)('knode');
|
|
102
|
+
this.nodeIdKey = `${REDIS_PREFIX}${this.nodeId}`;
|
|
103
|
+
this.idCard = new IdCard({
|
|
104
|
+
birthdate: Date.now(),
|
|
105
|
+
id: this.nodeId,
|
|
106
|
+
ip: this.ip,
|
|
107
|
+
topology: [],
|
|
108
|
+
});
|
|
109
|
+
reserved = await this.save({ creation: true });
|
|
110
|
+
} while (!reserved);
|
|
111
|
+
await this.addIdCardToIndex();
|
|
112
|
+
this.refreshWorker = this.constructWorker(`${__dirname}/workers/IDCardRenewer.js`);
|
|
113
|
+
this.refreshWorker.unref();
|
|
114
|
+
this.refreshWorker.on('message', async (message) => {
|
|
115
|
+
if (message.error) {
|
|
116
|
+
await this.node.evictSelf(message.error);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// Transfer informations to the worker
|
|
120
|
+
this.refreshWorker.postMessage({
|
|
121
|
+
action: 'start',
|
|
122
|
+
kuzzle: {
|
|
123
|
+
config: global.kuzzle.config,
|
|
124
|
+
id: global.kuzzle.id,
|
|
125
|
+
},
|
|
126
|
+
nodeIdKey: this.nodeIdKey,
|
|
127
|
+
// Used to configure a redis the same way as the Cache Engine does
|
|
128
|
+
redis: {
|
|
129
|
+
config: global.kuzzle.config.services.internalCache,
|
|
130
|
+
name: 'internal_adapter',
|
|
131
|
+
},
|
|
132
|
+
refreshDelay: this.refreshDelay,
|
|
133
|
+
refreshMultiplier: this.refreshMultiplier,
|
|
134
|
+
});
|
|
135
|
+
this.startTemporaryRefresh();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Helper method to mock worker instantiation in unit tests
|
|
139
|
+
*/
|
|
140
|
+
constructWorker(path) {
|
|
141
|
+
return new worker_threads_1.Worker(path);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Start refreshing the ID Card before the worker starts to ensure the ID Card
|
|
145
|
+
* is refreshed.
|
|
146
|
+
*
|
|
147
|
+
* Once the worker starts, this timer will be stopped.
|
|
148
|
+
*/
|
|
149
|
+
startTemporaryRefresh() {
|
|
150
|
+
this.refreshTimer = setInterval(async () => {
|
|
151
|
+
try {
|
|
152
|
+
await this.save();
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
global.kuzzle.log.error(`An error occurred while refreshing the ID card during WorkerThread startup: ${error}`);
|
|
156
|
+
}
|
|
157
|
+
}, this.refreshDelay * this.refreshMultiplier);
|
|
158
|
+
this.refreshWorker.on('message', ({ initialized }) => {
|
|
159
|
+
if (initialized) {
|
|
160
|
+
clearInterval(this.refreshTimer);
|
|
161
|
+
this.refreshTimer = null;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
async dispose() {
|
|
166
|
+
this.disposed = true;
|
|
167
|
+
if (this.refreshWorker) {
|
|
168
|
+
this.refreshWorker.postMessage({ action: 'dispose' });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Retrieves the ID cards from other nodes
|
|
173
|
+
*
|
|
174
|
+
* Each node store it's ID Card under a specific key name. When Redis database
|
|
175
|
+
* is growing, searching for those keys can be quite expensive and can slow down
|
|
176
|
+
* the handshake process.
|
|
177
|
+
*
|
|
178
|
+
* We are storing a set containing ID Card's keys under a set so when a new node
|
|
179
|
+
* is started, it can directly get the other nodes ID Cards with SMEMBERS and
|
|
180
|
+
* then MGET instead of using SCAN.
|
|
181
|
+
*
|
|
182
|
+
* When a new node retrieve the ID Card's keys from the set, it try to get them
|
|
183
|
+
* with MGET, those who cannot be retrieved are expired ID Cards so the node update
|
|
184
|
+
* the set accordingly.
|
|
185
|
+
*
|
|
186
|
+
* @return {Array.<IdCard>}
|
|
187
|
+
*/
|
|
188
|
+
async getRemoteIdCards() {
|
|
189
|
+
const idCards = [];
|
|
190
|
+
let keys = await global.kuzzle.ask('core:cache:internal:execute', 'smembers', REDIS_ID_CARDS_INDEX);
|
|
191
|
+
keys = keys.filter(nodeIdKey => nodeIdKey !== this.nodeIdKey);
|
|
192
|
+
if (keys.length === 0) {
|
|
193
|
+
return idCards;
|
|
194
|
+
}
|
|
195
|
+
const rawIdCards = await global.kuzzle.ask('core:cache:internal:mget', keys);
|
|
196
|
+
const expiredIdCards = [];
|
|
197
|
+
for (let i = 0; i < keys.length; i++) {
|
|
198
|
+
// filter keys that might have expired between the key search and their
|
|
199
|
+
// values retrieval
|
|
200
|
+
if (rawIdCards[i] !== null) {
|
|
201
|
+
idCards.push(IdCard.unserialize(JSON.parse(rawIdCards[i])));
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
expiredIdCards.push(keys[i]);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Clean expired ID Card's keys in the index
|
|
208
|
+
await bluebird_1.default.map(expiredIdCards, idCardKey => {
|
|
209
|
+
return global.kuzzle.ask('core:cache:internal:execute', 'srem', REDIS_ID_CARDS_INDEX, idCardKey);
|
|
210
|
+
});
|
|
211
|
+
return idCards;
|
|
138
212
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
let keys = await global.kuzzle.ask(
|
|
149
|
-
'core:cache:internal:searchKeys',
|
|
150
|
-
`${REDIS_PREFIX}*`);
|
|
151
|
-
|
|
152
|
-
keys = keys.filter(nodeIdKey => nodeIdKey !== this.nodeIdKey);
|
|
153
|
-
|
|
154
|
-
if (keys.length === 0) {
|
|
155
|
-
return result;
|
|
213
|
+
/**
|
|
214
|
+
* Adds a remote node IdCard to the node known topology
|
|
215
|
+
*/
|
|
216
|
+
async addNode(id) {
|
|
217
|
+
if (this.disposed || this.idCard.topology.has(id)) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
this.idCard.topology.add(id);
|
|
221
|
+
await this.save();
|
|
156
222
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
result.push(IdCard.unserialize(values[i]));
|
|
165
|
-
}
|
|
223
|
+
/**
|
|
224
|
+
* Removes a remote node IdCard from the node known topology
|
|
225
|
+
*/
|
|
226
|
+
async removeNode(id) {
|
|
227
|
+
if (!this.disposed && this.idCard.topology.delete(id)) {
|
|
228
|
+
await this.save();
|
|
229
|
+
}
|
|
166
230
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
async addNode (id) {
|
|
175
|
-
if (this.disposed || this.idCard.topology.has(id)) {
|
|
176
|
-
return;
|
|
231
|
+
/**
|
|
232
|
+
* Store the key under which this node ID Card is stored inside the set.
|
|
233
|
+
*
|
|
234
|
+
* This set is an index to retrieve ID Cards faster.
|
|
235
|
+
*/
|
|
236
|
+
async addIdCardToIndex() {
|
|
237
|
+
await global.kuzzle.ask('core:cache:internal:execute', 'sadd', REDIS_ID_CARDS_INDEX, this.nodeIdKey);
|
|
177
238
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (!this.disposed && this.idCard.topology.delete(id)) {
|
|
189
|
-
await this._save();
|
|
239
|
+
/**
|
|
240
|
+
* Saves the local node IdCard into Redis
|
|
241
|
+
*
|
|
242
|
+
* @returns True if the key was set
|
|
243
|
+
*/
|
|
244
|
+
async save({ creation } = { creation: false }) {
|
|
245
|
+
if (!this.idCard) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
return await global.kuzzle.ask('core:cache:internal:store', this.nodeIdKey, JSON.stringify(this.idCard.serialize()), { onlyIfNew: creation, ttl: this.refreshDelay * this.refreshMultiplier });
|
|
190
249
|
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Saves the local node IdCard into Redis
|
|
195
|
-
*/
|
|
196
|
-
_save ({ creation } = { creation: false }) {
|
|
197
|
-
return global.kuzzle.ask(
|
|
198
|
-
'core:cache:internal:store',
|
|
199
|
-
this.nodeIdKey,
|
|
200
|
-
this.idCard.serialize(),
|
|
201
|
-
{ onlyIfNew: creation, ttl: this.refreshDelay * 3 });
|
|
202
|
-
}
|
|
203
250
|
}
|
|
204
|
-
|
|
205
|
-
|
|
251
|
+
exports.ClusterIdCardHandler = ClusterIdCardHandler;
|
|
252
|
+
//# sourceMappingURL=idCardHandler.js.map
|
package/lib/cluster/node.js
CHANGED
|
@@ -474,21 +474,21 @@ class ClusterNode {
|
|
|
474
474
|
// to prevent race conditions (other nodes attempting to connect to this
|
|
475
475
|
// node while it's still initializing)
|
|
476
476
|
await this.idCardHandler.createIdCard();
|
|
477
|
-
|
|
477
|
+
debug('[CLUSTER] ID Card created');
|
|
478
478
|
|
|
479
479
|
this.nodeId = this.idCardHandler.nodeId;
|
|
480
480
|
|
|
481
481
|
await this.startHeartbeat();
|
|
482
|
-
|
|
482
|
+
debug('[CLUSTER] Start heartbeat');
|
|
483
483
|
|
|
484
484
|
let retried = false;
|
|
485
485
|
let fullState = null;
|
|
486
486
|
let nodes;
|
|
487
487
|
|
|
488
|
-
|
|
488
|
+
debug('[CLUSTER] Start retrieving full state..');
|
|
489
489
|
do {
|
|
490
490
|
nodes = await this.idCardHandler.getRemoteIdCards();
|
|
491
|
-
|
|
491
|
+
debug('[CLUSTER] %s remote nodes discovered', nodes.length);
|
|
492
492
|
|
|
493
493
|
// No other nodes detected = no handshake required
|
|
494
494
|
if (nodes.length === 0) {
|
|
@@ -511,7 +511,7 @@ class ClusterNode {
|
|
|
511
511
|
this.remoteNodes.set(id, subscriber);
|
|
512
512
|
return subscriber.init();
|
|
513
513
|
});
|
|
514
|
-
|
|
514
|
+
debug('[CLUSTER] Successfully subscribed to nodes');
|
|
515
515
|
|
|
516
516
|
fullState = await this.command.getFullState(nodes);
|
|
517
517
|
|
|
@@ -541,18 +541,18 @@ class ClusterNode {
|
|
|
541
541
|
}
|
|
542
542
|
}
|
|
543
543
|
while (fullState === null);
|
|
544
|
-
|
|
544
|
+
debug('[CLUSTER] Fullstate retrieved, loading into node..');
|
|
545
545
|
|
|
546
546
|
await this.fullState.loadFullState(fullState);
|
|
547
547
|
this.activity = fullState.activity
|
|
548
548
|
? fullState.activity
|
|
549
549
|
: this.activity;
|
|
550
550
|
|
|
551
|
-
|
|
551
|
+
debug('[CLUSTER] Fullstate loaded.');
|
|
552
552
|
|
|
553
553
|
const handshakeResponses = await this.command.broadcastHandshake(nodes);
|
|
554
554
|
|
|
555
|
-
|
|
555
|
+
debug('[CLUSTER] Successful handshakes with other nodes.');
|
|
556
556
|
|
|
557
557
|
// Update subscribers: start synchronizing, or unsubscribes from nodes who
|
|
558
558
|
// didn't respond
|
|
@@ -753,6 +753,10 @@ class ClusterNode {
|
|
|
753
753
|
* @return {void}
|
|
754
754
|
*/
|
|
755
755
|
registerEvents () {
|
|
756
|
+
global.kuzzle.on(
|
|
757
|
+
'admin:afterRefreshIndexCache',
|
|
758
|
+
() => this.onIndexCacheRefreshed());
|
|
759
|
+
|
|
756
760
|
global.kuzzle.on(
|
|
757
761
|
'core:realtime:room:create:after',
|
|
758
762
|
payload => this.onNewRealtimeRoom(payload));
|
|
@@ -1123,6 +1127,13 @@ class ClusterNode {
|
|
|
1123
1127
|
this.publisher.send('Shutdown', {});
|
|
1124
1128
|
}
|
|
1125
1129
|
|
|
1130
|
+
/**
|
|
1131
|
+
* Triggered when the index cache has been manually refreshed
|
|
1132
|
+
*/
|
|
1133
|
+
onIndexCacheRefreshed () {
|
|
1134
|
+
this.publisher.send('RefreshIndexCache', {});
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1126
1137
|
/**
|
|
1127
1138
|
* Returns the total number of subscribers on the cluster for the provided
|
|
1128
1139
|
* room
|