kuzzle 2.60.0-beta.1 → 2.60.0-beta.3
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/dist/bin/copy-binaries.js +17 -7
- package/dist/bin/start-kuzzle-server +19 -8
- package/dist/index.d.ts +2 -1
- package/dist/index.js +6 -2
- package/dist/lib/api/controllers/adminController.d.ts +9 -3
- package/dist/lib/api/controllers/adminController.js +42 -26
- package/dist/lib/api/controllers/authController.d.ts +15 -5
- package/dist/lib/api/controllers/authController.js +35 -20
- package/dist/lib/api/controllers/baseController.js +17 -7
- package/dist/lib/api/controllers/bulkController.js +17 -7
- package/dist/lib/api/controllers/collectionController.js +17 -7
- package/dist/lib/api/controllers/debugController.js +17 -7
- package/dist/lib/api/controllers/documentController.js +17 -7
- package/dist/lib/api/controllers/indexController.d.ts +1 -1
- package/dist/lib/api/controllers/memoryStorageController.js +17 -7
- package/dist/lib/api/controllers/securityController.d.ts +30 -28
- package/dist/lib/api/controllers/securityController.js +18 -8
- package/dist/lib/api/controllers/serverController.d.ts +0 -1
- package/dist/lib/api/controllers/serverController.js +17 -7
- package/dist/lib/api/documentExtractor.js +17 -7
- package/dist/lib/api/funnel.js +19 -9
- package/dist/lib/api/httpRoutes.d.ts +2 -1
- package/dist/lib/api/openapi/OpenApiManager.d.ts +2 -1
- package/dist/lib/api/openapi/openApiGenerator.js +1 -2
- package/dist/lib/api/request/kuzzleRequest.d.ts +9 -2
- package/dist/lib/api/request/kuzzleRequest.js +21 -7
- package/dist/lib/api/request/requestContext.d.ts +7 -2
- package/dist/lib/api/request/requestContext.js +17 -7
- package/dist/lib/api/request/requestInput.d.ts +1 -1
- package/dist/lib/api/request/requestInput.js +17 -7
- package/dist/lib/api/request/requestResponse.d.ts +2 -2
- package/dist/lib/api/request/requestResponse.js +21 -10
- package/dist/lib/cluster/command.d.ts +0 -2
- package/dist/lib/cluster/command.js +17 -7
- package/dist/lib/cluster/idCardHandler.d.ts +7 -2
- package/dist/lib/cluster/idCardHandler.js +24 -3
- package/dist/lib/cluster/node.d.ts +15 -3
- package/dist/lib/cluster/node.js +112 -40
- package/dist/lib/cluster/protobuf/command.proto +23 -22
- package/dist/lib/cluster/protobuf/sync.proto +91 -85
- package/dist/lib/cluster/publisher.js +17 -7
- package/dist/lib/cluster/state.js +17 -7
- package/dist/lib/cluster/subscriber.d.ts +0 -2
- package/dist/lib/cluster/subscriber.js +17 -7
- package/dist/lib/cluster/workers/IDCardRenewer.d.ts +6 -1
- package/dist/lib/cluster/workers/IDCardRenewer.js +14 -8
- package/dist/lib/config/index.js +20 -14
- package/dist/lib/core/auth/formatProcessing.d.ts +1 -1
- package/dist/lib/core/auth/formatProcessing.js +3 -3
- package/dist/lib/core/auth/passportResponse.d.ts +0 -1
- package/dist/lib/core/auth/passportWrapper.js +24 -8
- package/dist/lib/core/backend/backend.js +17 -7
- package/dist/lib/core/backend/backendConfig.d.ts +1 -1
- package/dist/lib/core/backend/backendConfig.js +17 -7
- package/dist/lib/core/backend/backendController.js +17 -7
- package/dist/lib/core/backend/backendErrors.js +17 -7
- package/dist/lib/core/backend/backendHook.js +17 -7
- package/dist/lib/core/backend/backendImport.js +17 -7
- package/dist/lib/core/backend/backendPipe.d.ts +4 -1
- package/dist/lib/core/backend/backendPipe.js +17 -7
- package/dist/lib/core/backend/backendPlugin.js +17 -7
- package/dist/lib/core/backend/backendStorage.js +8 -8
- package/dist/lib/core/backend/backendSubscription.js +17 -7
- package/dist/lib/core/backend/backendVault.js +17 -7
- package/dist/lib/core/backend/internalLogger.d.ts +7 -7
- package/dist/lib/core/debug/kuzzleDebugger.js +17 -7
- package/dist/lib/core/network/accessLogger.d.ts +0 -1
- package/dist/lib/core/network/accessLogger.js +17 -7
- package/dist/lib/core/network/clientConnection.js +17 -7
- package/dist/lib/core/network/context.d.ts +0 -1
- package/dist/lib/core/network/entryPoint.js +17 -7
- package/dist/lib/core/network/httpRouter/routePart.js +17 -7
- package/dist/lib/core/network/protocols/httpMessage.d.ts +0 -2
- package/dist/lib/core/network/protocols/httpwsProtocol.d.ts +0 -3
- package/dist/lib/core/network/protocols/httpwsProtocol.js +17 -7
- package/dist/lib/core/network/protocols/mqttProtocol.d.ts +1 -2
- package/dist/lib/core/network/protocols/mqttProtocol.js +31 -10
- package/dist/lib/core/network/router.js +17 -7
- package/dist/lib/core/plugin/plugin.js +17 -7
- package/dist/lib/core/plugin/pluginContext.d.ts +20 -9
- package/dist/lib/core/plugin/pluginContext.js +39 -24
- package/dist/lib/core/plugin/pluginsManager.js +17 -7
- package/dist/lib/core/realtime/channel.js +17 -7
- package/dist/lib/core/realtime/hotelClerk.d.ts +3 -2
- package/dist/lib/core/realtime/hotelClerk.js +17 -7
- package/dist/lib/core/realtime/notifier.js +17 -7
- package/dist/lib/core/security/profileRepository.js +17 -7
- package/dist/lib/core/security/roleRepository.js +17 -7
- package/dist/lib/core/security/securityLoader.js +17 -7
- package/dist/lib/core/security/tokenRepository.js +18 -8
- package/dist/lib/core/security/userRepository.js +17 -7
- package/dist/lib/core/shared/ObjectRepository.js +20 -7
- package/dist/lib/core/shared/abstractManifest.js +17 -7
- package/dist/lib/core/shared/sdk/embeddedSdk.js +20 -8
- package/dist/lib/core/shared/sdk/funnelProtocol.js +17 -7
- package/dist/lib/core/shared/store.d.ts +11 -1
- package/dist/lib/core/shared/store.js +26 -30
- package/dist/lib/core/statistics/statistics.d.ts +0 -1
- package/dist/lib/core/statistics/statistics.js +17 -7
- package/dist/lib/core/storage/clientAdapter.d.ts +4 -6
- package/dist/lib/core/storage/clientAdapter.js +24 -18
- package/dist/lib/core/storage/indexCache.js +17 -7
- package/dist/lib/core/validation/specification.js +1 -2
- package/dist/lib/core/validation/types/date.js +17 -7
- package/dist/lib/core/validation/types/enum.js +17 -7
- package/dist/lib/core/validation/types/geoShape.js +17 -7
- package/dist/lib/core/validation/types/geoShapeUtils.js +6 -7
- package/dist/lib/core/validation/validation.js +17 -7
- package/dist/lib/core/validation/validationUtils.js +24 -15
- package/dist/lib/kerror/codes/8-cluster.json +23 -0
- package/dist/lib/kerror/codes/index.js +20 -10
- package/dist/lib/kerror/errors/kuzzleError.js +18 -8
- package/dist/lib/kerror/index.js +31 -18
- package/dist/lib/kuzzle/Logger.js +4 -3
- package/dist/lib/kuzzle/dumpGenerator.js +17 -7
- package/dist/lib/kuzzle/event/KuzzleEventEmitter.d.ts +3 -3
- package/dist/lib/kuzzle/event/KuzzleEventEmitter.js +17 -7
- package/dist/lib/kuzzle/event/pipeRunner.js +17 -7
- package/dist/lib/kuzzle/exitOnFailure.d.ts +13 -0
- package/dist/lib/kuzzle/exitOnFailure.js +60 -0
- package/dist/lib/kuzzle/internalIndexHandler.js +37 -32
- package/dist/lib/kuzzle/kuzzle.d.ts +20 -8
- package/dist/lib/kuzzle/kuzzle.js +148 -130
- package/dist/lib/model/security/profile.js +17 -7
- package/dist/lib/model/security/role.js +17 -7
- package/dist/lib/model/security/user.js +17 -7
- package/dist/lib/model/storage/apiKey.d.ts +1 -1
- package/dist/lib/model/storage/apiKey.js +17 -7
- package/dist/lib/service/cache/redis.js +40 -9
- package/dist/lib/service/service.js +17 -7
- package/dist/lib/service/storage/7/elasticsearch.d.ts +36 -25
- package/dist/lib/service/storage/7/elasticsearch.js +73 -55
- package/dist/lib/service/storage/8/elasticsearch.d.ts +47 -36
- package/dist/lib/service/storage/8/elasticsearch.js +73 -55
- package/dist/lib/service/storage/Elasticsearch.d.ts +10 -4
- package/dist/lib/types/HttpStream.d.ts +0 -1
- package/dist/lib/types/Plugin.js +17 -7
- package/dist/lib/types/config/SecurityConfiguration.d.ts +0 -2
- package/dist/lib/types/config/storageEngine/StorageEngineElasticsearchConfiguration.d.ts +5 -0
- package/dist/lib/types/controllers/GetCurrentUserResponse.d.ts +5 -0
- package/dist/lib/types/controllers/GetCurrentUserResponse.js +3 -0
- package/dist/lib/types/controllers/ResetSecurityResult.d.ts +5 -0
- package/dist/lib/types/controllers/ResetSecurityResult.js +3 -0
- package/dist/lib/types/controllers/adminController.type.d.ts +7 -5
- package/dist/lib/types/controllers/authController.type.d.ts +7 -5
- package/dist/lib/types/core/auth/Serialized.d.ts +14 -0
- package/dist/lib/types/core/auth/Serialized.js +3 -0
- package/dist/lib/types/core/auth/formatProcessing.type.d.ts +5 -11
- package/dist/lib/types/events/EventProtocol.d.ts +0 -2
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/index.js +1 -0
- package/dist/lib/types/storage/8/Elasticsearch.d.ts +4 -3
- package/dist/lib/util/array.d.ts +1 -1
- package/dist/lib/util/array.js +7 -6
- package/dist/lib/util/assertType.js +5 -6
- package/dist/lib/util/async.js +1 -2
- package/dist/lib/util/asyncStore.d.ts +0 -1
- package/dist/lib/util/bufferedPassThrough.d.ts +0 -3
- package/dist/lib/util/crypto.js +1 -2
- package/dist/lib/util/debug.d.ts +0 -1
- package/dist/lib/util/debug.js +17 -7
- package/dist/lib/util/distributedLock.d.ts +27 -4
- package/dist/lib/util/distributedLock.js +69 -16
- package/dist/lib/util/dump-collection.d.ts +2 -2
- package/dist/lib/util/dump-collection.js +22 -13
- package/dist/lib/util/esRequest.js +1 -2
- package/dist/lib/util/extractFields.d.ts +16 -7
- package/dist/lib/util/extractFields.js +1 -10
- package/dist/lib/util/koncordeCompat.js +22 -13
- package/dist/lib/util/mutex.d.ts +4 -4
- package/dist/lib/util/mutex.js +21 -11
- package/dist/lib/util/name-generator.js +2 -2
- package/dist/lib/util/requestAssertions.js +27 -18
- package/dist/lib/util/safeObject.js +3 -4
- package/dist/lib/util/stackTrace.js +2 -3
- package/dist/lib/util/thrown.js +1 -2
- package/dist/lib/util/time.js +1 -2
- package/dist/package.json +22 -15
- package/package.json +22 -15
|
@@ -35,13 +35,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
35
35
|
}) : function(o, v) {
|
|
36
36
|
o["default"] = v;
|
|
37
37
|
});
|
|
38
|
-
var __importStar = (this && this.__importStar) || function (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
};
|
|
38
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
39
|
+
var ownKeys = function(o) {
|
|
40
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
41
|
+
var ar = [];
|
|
42
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
43
|
+
return ar;
|
|
44
|
+
};
|
|
45
|
+
return ownKeys(o);
|
|
46
|
+
};
|
|
47
|
+
return function (mod) {
|
|
48
|
+
if (mod && mod.__esModule) return mod;
|
|
49
|
+
var result = {};
|
|
50
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
51
|
+
__setModuleDefault(result, mod);
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
})();
|
|
45
55
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
56
|
exports.RequestResponse = exports.Headers = void 0;
|
|
47
57
|
const assert = __importStar(require("../../util/assertType"));
|
|
@@ -72,11 +82,11 @@ class Headers {
|
|
|
72
82
|
if (typeof name === "symbol") {
|
|
73
83
|
return this.headers[name];
|
|
74
84
|
}
|
|
75
|
-
assert.assertString("header name", name);
|
|
76
|
-
if (!
|
|
85
|
+
const header = assert.assertString("header name", name);
|
|
86
|
+
if (!header) {
|
|
77
87
|
return;
|
|
78
88
|
}
|
|
79
|
-
const storedName = this.namesMap.get(
|
|
89
|
+
const storedName = this.namesMap.get(header.toLowerCase());
|
|
80
90
|
return storedName === undefined ? undefined : this.headers[storedName];
|
|
81
91
|
}
|
|
82
92
|
removeHeader(name) {
|
|
@@ -239,6 +249,7 @@ class RequestResponse {
|
|
|
239
249
|
/**
|
|
240
250
|
* Request result
|
|
241
251
|
*/
|
|
252
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- kept for v2 compatibility, see docs/v3-breaking-changes.md
|
|
242
253
|
get result() {
|
|
243
254
|
return this[_request].result;
|
|
244
255
|
}
|
|
@@ -35,13 +35,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
35
35
|
}) : function(o, v) {
|
|
36
36
|
o["default"] = v;
|
|
37
37
|
});
|
|
38
|
-
var __importStar = (this && this.__importStar) || function (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
};
|
|
38
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
39
|
+
var ownKeys = function(o) {
|
|
40
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
41
|
+
var ar = [];
|
|
42
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
43
|
+
return ar;
|
|
44
|
+
};
|
|
45
|
+
return ownKeys(o);
|
|
46
|
+
};
|
|
47
|
+
return function (mod) {
|
|
48
|
+
if (mod && mod.__esModule) return mod;
|
|
49
|
+
var result = {};
|
|
50
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
51
|
+
__setModuleDefault(result, mod);
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
})();
|
|
45
55
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
46
56
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
47
57
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import "../types";
|
|
2
|
+
import type ClusterNode from "./node";
|
|
2
3
|
export type SerializedIdCard = {
|
|
3
4
|
id: string;
|
|
4
5
|
ip: string;
|
|
@@ -41,7 +42,7 @@ export declare class ClusterIdCardHandler {
|
|
|
41
42
|
/**
|
|
42
43
|
* Node instance. Represents the local node.
|
|
43
44
|
*/
|
|
44
|
-
private node;
|
|
45
|
+
private readonly node;
|
|
45
46
|
/**
|
|
46
47
|
* Local node ID Card
|
|
47
48
|
*/
|
|
@@ -85,7 +86,11 @@ export declare class ClusterIdCardHandler {
|
|
|
85
86
|
*/
|
|
86
87
|
private disposed;
|
|
87
88
|
private readonly logger;
|
|
88
|
-
constructor(node:
|
|
89
|
+
constructor(node: ClusterNode);
|
|
90
|
+
/**
|
|
91
|
+
* How long an ID card outlives the last renewal of its node, in ms
|
|
92
|
+
*/
|
|
93
|
+
get ttl(): number;
|
|
89
94
|
/**
|
|
90
95
|
* Generates and reserves a unique ID for this node instance.
|
|
91
96
|
* Makes sure that the ID is not already taken by another node instance.
|
|
@@ -94,6 +94,12 @@ class ClusterIdCardHandler {
|
|
|
94
94
|
this.ip = node.ip;
|
|
95
95
|
this.refreshDelay = node.heartbeatDelay;
|
|
96
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* How long an ID card outlives the last renewal of its node, in ms
|
|
99
|
+
*/
|
|
100
|
+
get ttl() {
|
|
101
|
+
return this.refreshDelay * this.refreshMultiplier;
|
|
102
|
+
}
|
|
97
103
|
/**
|
|
98
104
|
* Generates and reserves a unique ID for this node instance.
|
|
99
105
|
* Makes sure that the ID is not already taken by another node instance.
|
|
@@ -139,7 +145,8 @@ class ClusterIdCardHandler {
|
|
|
139
145
|
const refreshWorker = this.constructWorker(`${__dirname}/workers/IDCardRenewer${(0, node_path_1.extname)(__filename)}`);
|
|
140
146
|
this.refreshWorker = refreshWorker;
|
|
141
147
|
refreshWorker.on("message", async (message) => {
|
|
142
|
-
|
|
148
|
+
// A renewal racing the disposal finds the card deleted: not a failure
|
|
149
|
+
if (message.error && !this.disposed) {
|
|
143
150
|
await this.node.evictSelf(message.error);
|
|
144
151
|
}
|
|
145
152
|
});
|
|
@@ -207,7 +214,7 @@ class ClusterIdCardHandler {
|
|
|
207
214
|
// this method runs on the line after it is filled.
|
|
208
215
|
refreshWorker.on("message", ({ initialized }) => {
|
|
209
216
|
if (initialized) {
|
|
210
|
-
clearInterval(this.refreshTimer);
|
|
217
|
+
clearInterval(this.refreshTimer ?? undefined);
|
|
211
218
|
this.refreshTimer = null;
|
|
212
219
|
}
|
|
213
220
|
});
|
|
@@ -228,6 +235,20 @@ class ClusterIdCardHandler {
|
|
|
228
235
|
// It could happens that the worker has been killed before the dispose causing send to fail
|
|
229
236
|
}
|
|
230
237
|
}
|
|
238
|
+
clearInterval(this.refreshTimer ?? undefined);
|
|
239
|
+
this.refreshTimer = null;
|
|
240
|
+
// The worker deletes the card too, but nothing waits for it: the process
|
|
241
|
+
// usually exits first, and the card then outlives it by a TTL, long
|
|
242
|
+
// enough for a node restarted on this IP to take it for a live duplicate
|
|
243
|
+
if (this.nodeIdKey !== null) {
|
|
244
|
+
try {
|
|
245
|
+
await global.kuzzle.ask("core:cache:internal:del", this.nodeIdKey);
|
|
246
|
+
await global.kuzzle.ask("core:cache:internal:execute", "srem", REDIS_ID_CARDS_INDEX, this.nodeIdKey);
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
this.logger.warn(`Could not delete the ID card ${this.nodeIdKey}: it expires in ${this.ttl}ms (${error instanceof Error ? error.message : String(error)})`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
231
252
|
}
|
|
232
253
|
/**
|
|
233
254
|
* Retrieves the ID cards from other nodes
|
|
@@ -309,7 +330,7 @@ class ClusterIdCardHandler {
|
|
|
309
330
|
if (!this.idCard) {
|
|
310
331
|
return false;
|
|
311
332
|
}
|
|
312
|
-
return global.kuzzle.ask("core:cache:internal:store", this.nodeIdKey, JSON.stringify(this.idCard.serialize()), { onlyIfNew: creation, ttl: this.
|
|
333
|
+
return global.kuzzle.ask("core:cache:internal:store", this.nodeIdKey, JSON.stringify(this.idCard.serialize()), { onlyIfNew: creation, ttl: this.ttl });
|
|
313
334
|
}
|
|
314
335
|
}
|
|
315
336
|
exports.ClusterIdCardHandler = ClusterIdCardHandler;
|
|
@@ -143,16 +143,28 @@ declare class ClusterNode {
|
|
|
143
143
|
*
|
|
144
144
|
* Extracted from `handshake()` unchanged.
|
|
145
145
|
*
|
|
146
|
-
* @
|
|
147
|
-
*
|
|
146
|
+
* @throws {cluster.join.full_state_unavailable} on the second failure: the
|
|
147
|
+
* cluster is split
|
|
148
148
|
*/
|
|
149
149
|
private retryFullState;
|
|
150
|
+
/**
|
|
151
|
+
* The ID cards of the other nodes, none of which has this node's IP.
|
|
152
|
+
*
|
|
153
|
+
* A card with this IP may be the stale one of a previous run of this node
|
|
154
|
+
* (a process killed, a container restarted at once): it expires within a
|
|
155
|
+
* TTL, so it is only a real duplicate if it is still there after that.
|
|
156
|
+
*
|
|
157
|
+
* @throws {cluster.join.duplicate_ip} if it is
|
|
158
|
+
*/
|
|
159
|
+
private discoverNodes;
|
|
150
160
|
/**
|
|
151
161
|
* Discovers other active nodes from the cluster and, if other nodes exist,
|
|
152
162
|
* starts a handshake procedure to sync this node and to make it able to
|
|
153
163
|
* handle new client requests
|
|
154
164
|
*
|
|
155
|
-
*
|
|
165
|
+
* Rejects with a `cluster.join.*` error, and shuts the node down, when it
|
|
166
|
+
* cannot join: the node must not go on starting as if it had (a duplicate
|
|
167
|
+
* IP, a split cluster, `joinTimeout` reached).
|
|
156
168
|
*/
|
|
157
169
|
handshake(): Promise<void>;
|
|
158
170
|
/**
|
package/dist/lib/cluster/node.js
CHANGED
|
@@ -19,6 +19,39 @@
|
|
|
19
19
|
* See the License for the specific language governing permissions and
|
|
20
20
|
* limitations under the License.
|
|
21
21
|
*/
|
|
22
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
25
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
26
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
27
|
+
}
|
|
28
|
+
Object.defineProperty(o, k2, desc);
|
|
29
|
+
}) : (function(o, m, k, k2) {
|
|
30
|
+
if (k2 === undefined) k2 = k;
|
|
31
|
+
o[k2] = m[k];
|
|
32
|
+
}));
|
|
33
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
34
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
35
|
+
}) : function(o, v) {
|
|
36
|
+
o["default"] = v;
|
|
37
|
+
});
|
|
38
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
39
|
+
var ownKeys = function(o) {
|
|
40
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
41
|
+
var ar = [];
|
|
42
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
43
|
+
return ar;
|
|
44
|
+
};
|
|
45
|
+
return ownKeys(o);
|
|
46
|
+
};
|
|
47
|
+
return function (mod) {
|
|
48
|
+
if (mod && mod.__esModule) return mod;
|
|
49
|
+
var result = {};
|
|
50
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
51
|
+
__setModuleDefault(result, mod);
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
})();
|
|
22
55
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
56
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
57
|
};
|
|
@@ -37,19 +70,19 @@ const eventemitter3_1 = __importDefault(require("eventemitter3"));
|
|
|
37
70
|
const intersection_1 = __importDefault(require("lodash/intersection"));
|
|
38
71
|
const xor_1 = __importDefault(require("lodash/xor"));
|
|
39
72
|
const long_1 = __importDefault(require("long"));
|
|
73
|
+
const kerror = __importStar(require("../kerror"));
|
|
74
|
+
const kuzzleError_1 = require("../kerror/errors/kuzzleError");
|
|
40
75
|
const kuzzleStateEnum_1 = __importDefault(require("../kuzzle/kuzzleStateEnum"));
|
|
41
76
|
const debug_1 = __importDefault(require("../util/debug"));
|
|
42
77
|
const koncordeCompat_1 = require("../util/koncordeCompat");
|
|
43
|
-
|
|
44
|
-
// incompatible acquisition/TTL formats and must not contend on the same key —
|
|
45
|
-
// every node takes "clusterHandshake" with `Mutex`. Deferred to #2894.
|
|
46
|
-
const mutex_1 = require("../util/mutex"); // NOSONAR
|
|
78
|
+
const distributedLock_1 = require("../util/distributedLock");
|
|
47
79
|
const command_1 = __importDefault(require("./command"));
|
|
48
80
|
const idCardHandler_1 = require("./idCardHandler");
|
|
49
81
|
const publisher_1 = __importDefault(require("./publisher"));
|
|
50
82
|
const state_1 = __importDefault(require("./state"));
|
|
51
83
|
const subscriber_1 = __importDefault(require("./subscriber"));
|
|
52
84
|
const debug = (0, debug_1.default)("kuzzle:cluster:sync");
|
|
85
|
+
const joinError = kerror.wrap("cluster", "join");
|
|
53
86
|
/** Cluster-wide events carrying the collection write locks (see registerEvents) */
|
|
54
87
|
const COLLECTION_LOCK_EVENT = "core:storage:collection:lock";
|
|
55
88
|
const COLLECTION_UNLOCK_EVENT = "core:storage:collection:unlock";
|
|
@@ -335,7 +368,7 @@ class ClusterNode {
|
|
|
335
368
|
//
|
|
336
369
|
// Exit code 1: this is a failure, and a node that exits 0 is not restarted
|
|
337
370
|
// by an `on-failure` policy (#2785).
|
|
338
|
-
global.kuzzle.shutdown(1);
|
|
371
|
+
void global.kuzzle.shutdown(1);
|
|
339
372
|
}
|
|
340
373
|
/**
|
|
341
374
|
* Evicts a remote from the list
|
|
@@ -391,14 +424,15 @@ class ClusterNode {
|
|
|
391
424
|
const candidates = this.electShutdownCandidates(splits, idCards);
|
|
392
425
|
if (candidates.includes(this.nodeId)) {
|
|
393
426
|
this.logger.error("[CLUSTER] Network split detected. This node is outside the cluster: shutting down.");
|
|
394
|
-
|
|
427
|
+
// A failure, like `evictSelf`'s: exit non-zero (#2785)
|
|
428
|
+
void global.kuzzle.shutdown(1);
|
|
395
429
|
return;
|
|
396
430
|
}
|
|
397
431
|
}
|
|
398
432
|
catch (err) {
|
|
399
433
|
this.logger.error("[CLUSTER] Unexpected exception caught during a cluster consistency check. Shutting down...");
|
|
400
434
|
this.logger.error(err instanceof Error ? err.stack : (0, util_1.inspect)(err));
|
|
401
|
-
global.kuzzle.shutdown();
|
|
435
|
+
void global.kuzzle.shutdown(1);
|
|
402
436
|
}
|
|
403
437
|
}
|
|
404
438
|
/**
|
|
@@ -526,8 +560,8 @@ class ClusterNode {
|
|
|
526
560
|
*
|
|
527
561
|
* Extracted from `handshake()` unchanged.
|
|
528
562
|
*
|
|
529
|
-
* @
|
|
530
|
-
*
|
|
563
|
+
* @throws {cluster.join.full_state_unavailable} on the second failure: the
|
|
564
|
+
* cluster is split
|
|
531
565
|
*/
|
|
532
566
|
async retryFullState(retried) {
|
|
533
567
|
// Uh oh... no node was able to give us the full state.
|
|
@@ -536,9 +570,7 @@ class ClusterNode {
|
|
|
536
570
|
// means we're probably facing a network split, and we must then shut
|
|
537
571
|
// down.
|
|
538
572
|
if (retried) {
|
|
539
|
-
|
|
540
|
-
global.kuzzle.shutdown();
|
|
541
|
-
return false;
|
|
573
|
+
throw joinError.get("full_state_unavailable");
|
|
542
574
|
}
|
|
543
575
|
// Disposes all subscribers
|
|
544
576
|
for (const subscriber of this.remoteNodes.values()) {
|
|
@@ -549,28 +581,59 @@ class ClusterNode {
|
|
|
549
581
|
const retryDelay = this.heartbeatDelay * 1.5;
|
|
550
582
|
this.logger.warn(`[CLUSTER] Unable to connect to discovered cluster nodes. Retrying in ${retryDelay}ms...`);
|
|
551
583
|
await bluebird_1.default.delay(retryDelay);
|
|
552
|
-
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* The ID cards of the other nodes, none of which has this node's IP.
|
|
587
|
+
*
|
|
588
|
+
* A card with this IP may be the stale one of a previous run of this node
|
|
589
|
+
* (a process killed, a container restarted at once): it expires within a
|
|
590
|
+
* TTL, so it is only a real duplicate if it is still there after that.
|
|
591
|
+
*
|
|
592
|
+
* @throws {cluster.join.duplicate_ip} if it is
|
|
593
|
+
*/
|
|
594
|
+
async discoverNodes() {
|
|
595
|
+
const nodes = await this.idCardHandler.getRemoteIdCards();
|
|
596
|
+
if (!nodes.some((node) => node.ip === this.ip)) {
|
|
597
|
+
return nodes;
|
|
598
|
+
}
|
|
599
|
+
// A heartbeat more than the TTL: the card's last renewal may be in flight
|
|
600
|
+
const delay = this.idCardHandler.ttl + this.heartbeatDelay;
|
|
601
|
+
this.logger.warn(`[CLUSTER] Another node has the same IP address as this one (${this.ip}). Waiting ${delay}ms for it to expire, in case it is a stale ID card...`);
|
|
602
|
+
await bluebird_1.default.delay(delay);
|
|
603
|
+
const remaining = await this.idCardHandler.getRemoteIdCards();
|
|
604
|
+
const duplicate = remaining.find((node) => node.ip === this.ip);
|
|
605
|
+
if (duplicate) {
|
|
606
|
+
throw joinError.get("duplicate_ip", this.ip, duplicate.id);
|
|
607
|
+
}
|
|
608
|
+
return remaining;
|
|
553
609
|
}
|
|
554
610
|
/**
|
|
555
611
|
* Discovers other active nodes from the cluster and, if other nodes exist,
|
|
556
612
|
* starts a handshake procedure to sync this node and to make it able to
|
|
557
613
|
* handle new client requests
|
|
558
614
|
*
|
|
559
|
-
*
|
|
615
|
+
* Rejects with a `cluster.join.*` error, and shuts the node down, when it
|
|
616
|
+
* cannot join: the node must not go on starting as if it had (a duplicate
|
|
617
|
+
* IP, a split cluster, `joinTimeout` reached).
|
|
560
618
|
*/
|
|
561
619
|
async handshake() {
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
620
|
+
let timedOut = false;
|
|
621
|
+
let handshakeTimeout;
|
|
622
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
623
|
+
handshakeTimeout = setTimeout(() => {
|
|
624
|
+
timedOut = true;
|
|
625
|
+
reject(joinError.get("timeout", this.config.joinTimeout));
|
|
626
|
+
}, this.config.joinTimeout);
|
|
627
|
+
});
|
|
628
|
+
// No retry limit: the handshakes are taken one at a time, and the
|
|
629
|
+
// `joinTimeout` above is what gives up on a cluster that never lets this
|
|
630
|
+
// node in.
|
|
631
|
+
const join = (0, distributedLock_1.withLock)("clusterHandshake", async () => {
|
|
632
|
+
// Given up on while waiting for the lock: do not join after all
|
|
633
|
+
if (timedOut) {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
// Create the ID Key AFTER the handshake lock is actually taken,
|
|
574
637
|
// to prevent race conditions (other nodes attempting to connect to this
|
|
575
638
|
// node while it's still initializing)
|
|
576
639
|
await this.idCardHandler.createIdCard();
|
|
@@ -581,37 +644,29 @@ class ClusterNode {
|
|
|
581
644
|
await this.startHeartbeat(); // NOSONAR: TD-26
|
|
582
645
|
debug("[CLUSTER] Start heartbeat");
|
|
583
646
|
let retried = false;
|
|
584
|
-
let fullState
|
|
647
|
+
let fullState;
|
|
585
648
|
let nodes;
|
|
586
649
|
debug("[CLUSTER] Start retrieving full state..");
|
|
587
650
|
do {
|
|
588
|
-
nodes = await this.
|
|
651
|
+
nodes = await this.discoverNodes();
|
|
589
652
|
debug("[CLUSTER] %s remote nodes discovered", nodes.length);
|
|
590
653
|
// No other nodes detected = no handshake required
|
|
591
654
|
if (nodes.length === 0) {
|
|
592
655
|
this.trackActivity(this.nodeId, this.ip, nodeActivityEnum.ADDED);
|
|
593
656
|
return;
|
|
594
657
|
}
|
|
595
|
-
// Verify that no other node share the same IP address as this one
|
|
596
|
-
const duplicate = nodes.filter((node) => node.ip === this.ip);
|
|
597
|
-
if (duplicate.length > 0) {
|
|
598
|
-
this.logger.error(`[CLUSTER] Another node share the same IP address as this one (${this.ip}): ${duplicate[0]?.id}. Shutting down.`);
|
|
599
|
-
global.kuzzle.shutdown();
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
658
|
await this.subscribeToNodes(nodes);
|
|
603
659
|
debug("[CLUSTER] Successfully subscribed to nodes");
|
|
604
660
|
fullState = await this.command.getFullState(nodes);
|
|
605
661
|
if (fullState === null) {
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
}
|
|
662
|
+
// Sequential by design: the retry waits for a heartbeat round
|
|
663
|
+
await this.retryFullState(retried); // NOSONAR
|
|
609
664
|
retried = true;
|
|
610
665
|
}
|
|
611
666
|
} while (fullState === null);
|
|
612
667
|
debug("[CLUSTER] Fullstate retrieved, loading into node..");
|
|
613
668
|
await this.fullState.loadFullState(fullState); // NOSONAR: TD-26
|
|
614
|
-
this.activity = fullState.activity
|
|
669
|
+
this.activity = fullState.activity ?? this.activity;
|
|
615
670
|
debug("[CLUSTER] Fullstate loaded.");
|
|
616
671
|
const handshakeResponses = await this.command.broadcastHandshake(nodes);
|
|
617
672
|
debug("[CLUSTER] Successful handshakes with other nodes.");
|
|
@@ -625,10 +680,27 @@ class ClusterNode {
|
|
|
625
680
|
? `[CLUSTER] Successfully joined the cluster as "${this.nodeId}" (process node id: ${global.nodeId}).`
|
|
626
681
|
: `[CLUSTER] Successfully joined the cluster as "${this.nodeId}".`);
|
|
627
682
|
this.trackActivity(this.nodeId, this.ip, nodeActivityEnum.ADDED);
|
|
683
|
+
}, { retryAttempts: Number.POSITIVE_INFINITY });
|
|
684
|
+
try {
|
|
685
|
+
// A timeout does not stop `join`: the node it settles later is shutting
|
|
686
|
+
// down already
|
|
687
|
+
await Promise.race([join, timeout]);
|
|
688
|
+
}
|
|
689
|
+
catch (error) {
|
|
690
|
+
// The handshake cannot be stopped where it is: it may still introduce
|
|
691
|
+
// this node to the others. Leave the cluster rather than stay in it
|
|
692
|
+
// half-joined, whatever the application does with the failed startup.
|
|
693
|
+
if (error instanceof distributedLock_1.MutexLockLostError) {
|
|
694
|
+
this.logger.error("[CLUSTER] Lost the handshake lock while joining the cluster. Shutting down.");
|
|
695
|
+
}
|
|
696
|
+
if (error instanceof distributedLock_1.MutexLockLostError ||
|
|
697
|
+
(error instanceof kuzzleError_1.KuzzleError && error.id.startsWith("cluster.join."))) {
|
|
698
|
+
void global.kuzzle.shutdown(1);
|
|
699
|
+
}
|
|
700
|
+
throw error;
|
|
628
701
|
}
|
|
629
702
|
finally {
|
|
630
703
|
clearTimeout(handshakeTimeout);
|
|
631
|
-
await mutex.unlock();
|
|
632
704
|
}
|
|
633
705
|
}
|
|
634
706
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// messages definition for issuing node commands and receiving the
|
|
2
2
|
// corresponding responses
|
|
3
|
+
// /!\ Every singular scalar field is "optional": see sync.proto's header.
|
|
3
4
|
package command;
|
|
4
5
|
|
|
5
6
|
syntax="proto3";
|
|
@@ -10,36 +11,36 @@ import "sync.proto";
|
|
|
10
11
|
message FullStateResponse {
|
|
11
12
|
// Describes the nodes that have subscribed to a given realtime room
|
|
12
13
|
message NodeSubscribers {
|
|
13
|
-
string nodeId = 1;
|
|
14
|
-
uint32 subscribers = 2;
|
|
15
|
-
uint64 messageId = 3;
|
|
14
|
+
optional string nodeId = 1;
|
|
15
|
+
optional uint32 subscribers = 2;
|
|
16
|
+
optional uint64 messageId = 3;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
// Describes a realtime room
|
|
19
20
|
message RealtimeRoom {
|
|
20
|
-
string roomId = 1;
|
|
21
|
-
string index = 2;
|
|
22
|
-
string collection = 3;
|
|
21
|
+
optional string roomId = 1;
|
|
22
|
+
optional string index = 2;
|
|
23
|
+
optional string collection = 3;
|
|
23
24
|
|
|
24
25
|
// array of polymorphic objects, needs to be stringified beforehand until
|
|
25
26
|
// this PR is merged: https://github.com/protobufjs/protobuf.js/pull/1495
|
|
26
|
-
string filters = 4;
|
|
27
|
+
optional string filters = 4;
|
|
27
28
|
|
|
28
29
|
repeated NodeSubscribers nodes = 5;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
// Describes a cluster activity event
|
|
32
33
|
message Activity {
|
|
33
|
-
string id = 1;
|
|
34
|
-
string date = 2;
|
|
35
|
-
string address = 3;
|
|
36
|
-
uint32 event = 4;
|
|
34
|
+
optional string id = 1;
|
|
35
|
+
optional string date = 2;
|
|
36
|
+
optional string address = 3;
|
|
37
|
+
optional uint32 event = 4;
|
|
37
38
|
optional string reason = 5;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
message NodeState {
|
|
41
|
-
string id = 1;
|
|
42
|
-
uint64 lastMessageId = 2;
|
|
42
|
+
optional string id = 1;
|
|
43
|
+
optional uint64 lastMessageId = 2;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
repeated RealtimeRoom rooms = 1;
|
|
@@ -50,30 +51,30 @@ message FullStateResponse {
|
|
|
50
51
|
|
|
51
52
|
// Handshake Request
|
|
52
53
|
message HandshakeRequest {
|
|
53
|
-
string nodeId = 1;
|
|
54
|
-
uint64 lastMessageId = 2;
|
|
55
|
-
string ip = 3;
|
|
54
|
+
optional string nodeId = 1;
|
|
55
|
+
optional uint64 lastMessageId = 2;
|
|
56
|
+
optional string ip = 3;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
// Handshake response
|
|
59
60
|
message HandshakeResponse {
|
|
60
|
-
bool added = 1;
|
|
61
|
-
uint64 lastMessageId = 2;
|
|
61
|
+
optional bool added = 1;
|
|
62
|
+
optional uint64 lastMessageId = 2;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
// Retransmit request: the messages a subscriber missed from this node's sync
|
|
65
66
|
// stream, by message id, both ends included (#2785)
|
|
66
67
|
message RetransmitRequest {
|
|
67
|
-
uint64 from = 1;
|
|
68
|
-
uint64 to = 2;
|
|
68
|
+
optional uint64 from = 1;
|
|
69
|
+
optional uint64 to = 2;
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
// Retransmit response: the missed messages, in order, exactly as they were
|
|
72
73
|
// published (topic + encoded sync message)
|
|
73
74
|
message RetransmitResponse {
|
|
74
75
|
message Frame {
|
|
75
|
-
string topic = 1;
|
|
76
|
-
bytes data = 2;
|
|
76
|
+
optional string topic = 1;
|
|
77
|
+
optional bytes data = 2;
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
repeated Frame frames = 1;
|