kuzzle 2.59.0 → 2.60.0-beta.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.
Files changed (174) hide show
  1. package/dist/bin/copy-binaries.js +17 -7
  2. package/dist/bin/start-kuzzle-server +17 -7
  3. package/dist/index.d.ts +2 -1
  4. package/dist/index.js +6 -2
  5. package/dist/lib/api/controllers/adminController.d.ts +9 -3
  6. package/dist/lib/api/controllers/adminController.js +42 -26
  7. package/dist/lib/api/controllers/authController.d.ts +15 -5
  8. package/dist/lib/api/controllers/authController.js +35 -20
  9. package/dist/lib/api/controllers/baseController.js +17 -7
  10. package/dist/lib/api/controllers/bulkController.js +17 -7
  11. package/dist/lib/api/controllers/collectionController.js +17 -7
  12. package/dist/lib/api/controllers/debugController.js +17 -7
  13. package/dist/lib/api/controllers/documentController.js +17 -7
  14. package/dist/lib/api/controllers/indexController.d.ts +1 -1
  15. package/dist/lib/api/controllers/memoryStorageController.js +17 -7
  16. package/dist/lib/api/controllers/securityController.d.ts +30 -28
  17. package/dist/lib/api/controllers/securityController.js +18 -8
  18. package/dist/lib/api/controllers/serverController.d.ts +0 -1
  19. package/dist/lib/api/controllers/serverController.js +17 -7
  20. package/dist/lib/api/documentExtractor.js +17 -7
  21. package/dist/lib/api/funnel.js +19 -9
  22. package/dist/lib/api/httpRoutes.d.ts +2 -1
  23. package/dist/lib/api/openapi/OpenApiManager.d.ts +2 -1
  24. package/dist/lib/api/openapi/openApiGenerator.js +1 -2
  25. package/dist/lib/api/request/kuzzleRequest.d.ts +9 -2
  26. package/dist/lib/api/request/kuzzleRequest.js +21 -7
  27. package/dist/lib/api/request/requestContext.d.ts +7 -2
  28. package/dist/lib/api/request/requestContext.js +17 -7
  29. package/dist/lib/api/request/requestInput.d.ts +1 -1
  30. package/dist/lib/api/request/requestInput.js +17 -7
  31. package/dist/lib/api/request/requestResponse.d.ts +2 -2
  32. package/dist/lib/api/request/requestResponse.js +21 -10
  33. package/dist/lib/cluster/command.d.ts +0 -2
  34. package/dist/lib/cluster/command.js +17 -7
  35. package/dist/lib/cluster/idCardHandler.d.ts +3 -2
  36. package/dist/lib/cluster/idCardHandler.js +1 -1
  37. package/dist/lib/cluster/node.js +69 -64
  38. package/dist/lib/cluster/protobuf/command.proto +23 -22
  39. package/dist/lib/cluster/protobuf/sync.proto +91 -85
  40. package/dist/lib/cluster/publisher.js +17 -7
  41. package/dist/lib/cluster/state.js +17 -7
  42. package/dist/lib/cluster/subscriber.d.ts +0 -2
  43. package/dist/lib/cluster/subscriber.js +17 -7
  44. package/dist/lib/cluster/workers/IDCardRenewer.d.ts +0 -1
  45. package/dist/lib/config/index.js +20 -14
  46. package/dist/lib/core/auth/formatProcessing.d.ts +1 -1
  47. package/dist/lib/core/auth/formatProcessing.js +3 -3
  48. package/dist/lib/core/auth/passportResponse.d.ts +0 -1
  49. package/dist/lib/core/auth/passportWrapper.js +22 -8
  50. package/dist/lib/core/backend/backend.js +17 -7
  51. package/dist/lib/core/backend/backendConfig.d.ts +1 -1
  52. package/dist/lib/core/backend/backendConfig.js +17 -7
  53. package/dist/lib/core/backend/backendController.js +17 -7
  54. package/dist/lib/core/backend/backendErrors.js +17 -7
  55. package/dist/lib/core/backend/backendHook.js +17 -7
  56. package/dist/lib/core/backend/backendImport.js +17 -7
  57. package/dist/lib/core/backend/backendPipe.d.ts +4 -1
  58. package/dist/lib/core/backend/backendPipe.js +17 -7
  59. package/dist/lib/core/backend/backendPlugin.js +17 -7
  60. package/dist/lib/core/backend/backendStorage.js +8 -8
  61. package/dist/lib/core/backend/backendSubscription.js +17 -7
  62. package/dist/lib/core/backend/backendVault.js +17 -7
  63. package/dist/lib/core/backend/internalLogger.d.ts +7 -7
  64. package/dist/lib/core/debug/kuzzleDebugger.js +17 -7
  65. package/dist/lib/core/network/accessLogger.d.ts +0 -1
  66. package/dist/lib/core/network/accessLogger.js +17 -7
  67. package/dist/lib/core/network/clientConnection.js +17 -7
  68. package/dist/lib/core/network/context.d.ts +0 -1
  69. package/dist/lib/core/network/entryPoint.js +17 -7
  70. package/dist/lib/core/network/httpRouter/routePart.js +17 -7
  71. package/dist/lib/core/network/protocols/httpMessage.d.ts +0 -2
  72. package/dist/lib/core/network/protocols/httpwsProtocol.d.ts +0 -3
  73. package/dist/lib/core/network/protocols/httpwsProtocol.js +29 -12
  74. package/dist/lib/core/network/protocols/mqttProtocol.d.ts +1 -2
  75. package/dist/lib/core/network/protocols/mqttProtocol.js +31 -10
  76. package/dist/lib/core/network/router.js +17 -7
  77. package/dist/lib/core/plugin/plugin.js +17 -7
  78. package/dist/lib/core/plugin/pluginContext.d.ts +20 -9
  79. package/dist/lib/core/plugin/pluginContext.js +39 -24
  80. package/dist/lib/core/plugin/pluginsManager.js +17 -7
  81. package/dist/lib/core/realtime/channel.js +17 -7
  82. package/dist/lib/core/realtime/hotelClerk.d.ts +3 -2
  83. package/dist/lib/core/realtime/hotelClerk.js +17 -7
  84. package/dist/lib/core/realtime/notifier.js +17 -7
  85. package/dist/lib/core/security/profileRepository.js +17 -7
  86. package/dist/lib/core/security/roleRepository.js +17 -7
  87. package/dist/lib/core/security/securityLoader.js +17 -7
  88. package/dist/lib/core/security/tokenRepository.js +18 -8
  89. package/dist/lib/core/security/userRepository.js +17 -7
  90. package/dist/lib/core/shared/ObjectRepository.js +20 -7
  91. package/dist/lib/core/shared/abstractManifest.js +17 -7
  92. package/dist/lib/core/shared/sdk/embeddedSdk.js +20 -8
  93. package/dist/lib/core/shared/sdk/funnelProtocol.js +17 -7
  94. package/dist/lib/core/shared/store.d.ts +11 -1
  95. package/dist/lib/core/shared/store.js +26 -30
  96. package/dist/lib/core/statistics/statistics.d.ts +0 -1
  97. package/dist/lib/core/statistics/statistics.js +17 -7
  98. package/dist/lib/core/storage/clientAdapter.d.ts +4 -6
  99. package/dist/lib/core/storage/clientAdapter.js +24 -18
  100. package/dist/lib/core/storage/indexCache.js +17 -7
  101. package/dist/lib/core/validation/specification.js +1 -2
  102. package/dist/lib/core/validation/types/date.js +17 -7
  103. package/dist/lib/core/validation/types/enum.js +17 -7
  104. package/dist/lib/core/validation/types/geoShape.js +17 -7
  105. package/dist/lib/core/validation/types/geoShapeUtils.js +6 -7
  106. package/dist/lib/core/validation/validation.js +17 -7
  107. package/dist/lib/core/validation/validationUtils.js +24 -15
  108. package/dist/lib/kerror/codes/index.js +20 -10
  109. package/dist/lib/kerror/errors/kuzzleError.js +18 -8
  110. package/dist/lib/kerror/index.js +31 -18
  111. package/dist/lib/kuzzle/Logger.js +4 -3
  112. package/dist/lib/kuzzle/dumpGenerator.js +17 -7
  113. package/dist/lib/kuzzle/event/KuzzleEventEmitter.d.ts +3 -3
  114. package/dist/lib/kuzzle/event/KuzzleEventEmitter.js +17 -7
  115. package/dist/lib/kuzzle/event/pipeRunner.js +17 -7
  116. package/dist/lib/kuzzle/internalIndexHandler.js +37 -32
  117. package/dist/lib/kuzzle/kuzzle.d.ts +13 -8
  118. package/dist/lib/kuzzle/kuzzle.js +103 -128
  119. package/dist/lib/model/security/profile.js +17 -7
  120. package/dist/lib/model/security/role.js +17 -7
  121. package/dist/lib/model/security/user.js +17 -7
  122. package/dist/lib/model/storage/apiKey.d.ts +1 -1
  123. package/dist/lib/model/storage/apiKey.js +17 -7
  124. package/dist/lib/service/cache/redis.js +40 -9
  125. package/dist/lib/service/service.js +17 -7
  126. package/dist/lib/service/storage/7/elasticsearch.d.ts +36 -25
  127. package/dist/lib/service/storage/7/elasticsearch.js +73 -55
  128. package/dist/lib/service/storage/8/elasticsearch.d.ts +47 -36
  129. package/dist/lib/service/storage/8/elasticsearch.js +73 -55
  130. package/dist/lib/service/storage/Elasticsearch.d.ts +10 -4
  131. package/dist/lib/types/HttpStream.d.ts +0 -1
  132. package/dist/lib/types/Plugin.js +17 -7
  133. package/dist/lib/types/config/SecurityConfiguration.d.ts +0 -2
  134. package/dist/lib/types/config/storageEngine/StorageEngineElasticsearchConfiguration.d.ts +5 -0
  135. package/dist/lib/types/controllers/GetCurrentUserResponse.d.ts +5 -0
  136. package/dist/lib/types/controllers/GetCurrentUserResponse.js +3 -0
  137. package/dist/lib/types/controllers/ResetSecurityResult.d.ts +5 -0
  138. package/dist/lib/types/controllers/ResetSecurityResult.js +3 -0
  139. package/dist/lib/types/controllers/adminController.type.d.ts +7 -5
  140. package/dist/lib/types/controllers/authController.type.d.ts +7 -5
  141. package/dist/lib/types/core/auth/Serialized.d.ts +14 -0
  142. package/dist/lib/types/core/auth/Serialized.js +3 -0
  143. package/dist/lib/types/core/auth/formatProcessing.type.d.ts +5 -11
  144. package/dist/lib/types/events/EventProtocol.d.ts +0 -2
  145. package/dist/lib/types/index.d.ts +1 -0
  146. package/dist/lib/types/index.js +1 -0
  147. package/dist/lib/types/storage/8/Elasticsearch.d.ts +4 -3
  148. package/dist/lib/util/array.d.ts +1 -1
  149. package/dist/lib/util/array.js +7 -6
  150. package/dist/lib/util/assertType.js +5 -6
  151. package/dist/lib/util/async.js +1 -2
  152. package/dist/lib/util/asyncStore.d.ts +0 -1
  153. package/dist/lib/util/bufferedPassThrough.d.ts +0 -3
  154. package/dist/lib/util/crypto.js +1 -2
  155. package/dist/lib/util/debug.d.ts +0 -1
  156. package/dist/lib/util/debug.js +17 -7
  157. package/dist/lib/util/distributedLock.d.ts +27 -4
  158. package/dist/lib/util/distributedLock.js +69 -16
  159. package/dist/lib/util/dump-collection.d.ts +2 -2
  160. package/dist/lib/util/dump-collection.js +22 -13
  161. package/dist/lib/util/esRequest.js +1 -2
  162. package/dist/lib/util/extractFields.d.ts +16 -7
  163. package/dist/lib/util/extractFields.js +1 -10
  164. package/dist/lib/util/koncordeCompat.js +22 -13
  165. package/dist/lib/util/mutex.d.ts +4 -4
  166. package/dist/lib/util/mutex.js +21 -11
  167. package/dist/lib/util/name-generator.js +2 -2
  168. package/dist/lib/util/requestAssertions.js +27 -18
  169. package/dist/lib/util/safeObject.js +3 -4
  170. package/dist/lib/util/stackTrace.js +2 -3
  171. package/dist/lib/util/thrown.js +1 -2
  172. package/dist/lib/util/time.js +1 -2
  173. package/dist/package.json +22 -15
  174. 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 (mod) {
39
- if (mod && mod.__esModule) return mod;
40
- var result = {};
41
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
42
- __setModuleDefault(result, mod);
43
- return result;
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 (!name) {
85
+ const header = assert.assertString("header name", name);
86
+ if (!header) {
77
87
  return;
78
88
  }
79
- const storedName = this.namesMap.get(name.toLowerCase());
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
  }
@@ -1,5 +1,3 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
1
  import Long from "long";
4
2
  import type { SerializedState } from "./state";
5
3
  import type { Activity, Decoded, FullStateResponse, HandshakeResponse } from "./protobuf/commandMessages";
@@ -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 (mod) {
39
- if (mod && mod.__esModule) return mod;
40
- var result = {};
41
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
42
- __setModuleDefault(result, mod);
43
- return result;
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,7 @@ export declare class ClusterIdCardHandler {
85
86
  */
86
87
  private disposed;
87
88
  private readonly logger;
88
- constructor(node: any);
89
+ constructor(node: ClusterNode);
89
90
  /**
90
91
  * Generates and reserves a unique ID for this node instance.
91
92
  * Makes sure that the ID is not already taken by another node instance.
@@ -207,7 +207,7 @@ class ClusterIdCardHandler {
207
207
  // this method runs on the line after it is filled.
208
208
  refreshWorker.on("message", ({ initialized }) => {
209
209
  if (initialized) {
210
- clearInterval(this.refreshTimer);
210
+ clearInterval(this.refreshTimer ?? undefined);
211
211
  this.refreshTimer = null;
212
212
  }
213
213
  });
@@ -40,10 +40,7 @@ const long_1 = __importDefault(require("long"));
40
40
  const kuzzleStateEnum_1 = __importDefault(require("../kuzzle/kuzzleStateEnum"));
41
41
  const debug_1 = __importDefault(require("../util/debug"));
42
42
  const koncordeCompat_1 = require("../util/koncordeCompat");
43
- // NOSONAR: `Mutex` is deprecated in favour of `withLock`, but the two use
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
43
+ const distributedLock_1 = require("../util/distributedLock");
47
44
  const command_1 = __importDefault(require("./command"));
48
45
  const idCardHandler_1 = require("./idCardHandler");
49
46
  const publisher_1 = __importDefault(require("./publisher"));
@@ -563,72 +560,80 @@ class ClusterNode {
563
560
  this.logger.error(`[CLUSTER] Failed to join the cluster: timed out (joinTimeout: ${this.config.joinTimeout}ms)`);
564
561
  global.kuzzle.shutdown();
565
562
  }, this.config.joinTimeout);
566
- // NOSONAR (S1874): same deferral as the import above — every node takes
567
- // "clusterHandshake" with `Mutex`, so converting one site to `withLock`
568
- // would remove the exclusion it exists for. #2894.
569
- const lockOptions = { timeout: this.config.joinTimeout };
570
- const mutex = new mutex_1.Mutex("clusterHandshake", lockOptions); // NOSONAR
563
+ // No retry limit: the handshakes are taken one at a time, and the
564
+ // `joinTimeout` above is what gives up on a cluster that never lets this
565
+ // node in.
571
566
  try {
572
- await mutex.lock();
573
- // Create the ID Key AFTER the handshake mutex is actually locked,
574
- // to prevent race conditions (other nodes attempting to connect to this
575
- // node while it's still initializing)
576
- await this.idCardHandler.createIdCard();
577
- debug("[CLUSTER] ID Card created");
578
- const nodeId = this.idCardHandler.nodeId;
579
- (0, assert_1.default)(nodeId !== null, "[CLUSTER] createIdCard() returned without reserving a node id");
580
- this.nodeId = nodeId;
581
- await this.startHeartbeat(); // NOSONAR: TD-26
582
- debug("[CLUSTER] Start heartbeat");
583
- let retried = false;
584
- let fullState = null;
585
- let nodes;
586
- debug("[CLUSTER] Start retrieving full state..");
587
- do {
588
- nodes = await this.idCardHandler.getRemoteIdCards();
589
- debug("[CLUSTER] %s remote nodes discovered", nodes.length);
590
- // No other nodes detected = no handshake required
591
- if (nodes.length === 0) {
592
- this.trackActivity(this.nodeId, this.ip, nodeActivityEnum.ADDED);
593
- return;
594
- }
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
- await this.subscribeToNodes(nodes);
603
- debug("[CLUSTER] Successfully subscribed to nodes");
604
- fullState = await this.command.getFullState(nodes);
605
- if (fullState === null) {
606
- if (!(await this.retryFullState(retried))) {
567
+ await (0, distributedLock_1.withLock)("clusterHandshake", async () => {
568
+ // Create the ID Key AFTER the handshake lock is actually taken,
569
+ // to prevent race conditions (other nodes attempting to connect to this
570
+ // node while it's still initializing)
571
+ await this.idCardHandler.createIdCard();
572
+ debug("[CLUSTER] ID Card created");
573
+ const nodeId = this.idCardHandler.nodeId;
574
+ (0, assert_1.default)(nodeId !== null, "[CLUSTER] createIdCard() returned without reserving a node id");
575
+ this.nodeId = nodeId;
576
+ await this.startHeartbeat(); // NOSONAR: TD-26
577
+ debug("[CLUSTER] Start heartbeat");
578
+ let retried = false;
579
+ let fullState;
580
+ let nodes;
581
+ debug("[CLUSTER] Start retrieving full state..");
582
+ do {
583
+ nodes = await this.idCardHandler.getRemoteIdCards();
584
+ debug("[CLUSTER] %s remote nodes discovered", nodes.length);
585
+ // No other nodes detected = no handshake required
586
+ if (nodes.length === 0) {
587
+ this.trackActivity(this.nodeId, this.ip, nodeActivityEnum.ADDED);
607
588
  return;
608
589
  }
609
- retried = true;
610
- }
611
- } while (fullState === null);
612
- debug("[CLUSTER] Fullstate retrieved, loading into node..");
613
- await this.fullState.loadFullState(fullState); // NOSONAR: TD-26
614
- this.activity = fullState.activity ? fullState.activity : this.activity;
615
- debug("[CLUSTER] Fullstate loaded.");
616
- const handshakeResponses = await this.command.broadcastHandshake(nodes);
617
- debug("[CLUSTER] Successful handshakes with other nodes.");
618
- await this.syncWithPeers(handshakeResponses, fullState);
619
- // `createIdCard()` adopts `global.nodeId`, so these are normally the same
620
- // string and the line reads as one name (TD-59, #2764). They still differ
621
- // in the two cases the adoption cannot cover — a stale IdCard holding the
622
- // key, and a Kuzzle running without a Backend — and those are exactly the
623
- // cases where a reader needs to be told, so the line says both.
624
- this.logger.info(global.nodeId && global.nodeId !== this.nodeId
625
- ? `[CLUSTER] Successfully joined the cluster as "${this.nodeId}" (process node id: ${global.nodeId}).`
626
- : `[CLUSTER] Successfully joined the cluster as "${this.nodeId}".`);
627
- this.trackActivity(this.nodeId, this.ip, nodeActivityEnum.ADDED);
590
+ // Verify that no other node share the same IP address as this one
591
+ const duplicate = nodes.filter((node) => node.ip === this.ip);
592
+ if (duplicate.length > 0) {
593
+ this.logger.error(`[CLUSTER] Another node share the same IP address as this one (${this.ip}): ${duplicate[0]?.id}. Shutting down.`);
594
+ global.kuzzle.shutdown();
595
+ return;
596
+ }
597
+ await this.subscribeToNodes(nodes);
598
+ debug("[CLUSTER] Successfully subscribed to nodes");
599
+ fullState = await this.command.getFullState(nodes);
600
+ if (fullState === null) {
601
+ if (!(await this.retryFullState(retried))) {
602
+ return;
603
+ }
604
+ retried = true;
605
+ }
606
+ } while (fullState === null);
607
+ debug("[CLUSTER] Fullstate retrieved, loading into node..");
608
+ await this.fullState.loadFullState(fullState); // NOSONAR: TD-26
609
+ this.activity = fullState.activity ?? this.activity;
610
+ debug("[CLUSTER] Fullstate loaded.");
611
+ const handshakeResponses = await this.command.broadcastHandshake(nodes);
612
+ debug("[CLUSTER] Successful handshakes with other nodes.");
613
+ await this.syncWithPeers(handshakeResponses, fullState);
614
+ // `createIdCard()` adopts `global.nodeId`, so these are normally the same
615
+ // string and the line reads as one name (TD-59, #2764). They still differ
616
+ // in the two cases the adoption cannot cover — a stale IdCard holding the
617
+ // key, and a Kuzzle running without a Backend — and those are exactly the
618
+ // cases where a reader needs to be told, so the line says both.
619
+ this.logger.info(global.nodeId && global.nodeId !== this.nodeId
620
+ ? `[CLUSTER] Successfully joined the cluster as "${this.nodeId}" (process node id: ${global.nodeId}).`
621
+ : `[CLUSTER] Successfully joined the cluster as "${this.nodeId}".`);
622
+ this.trackActivity(this.nodeId, this.ip, nodeActivityEnum.ADDED);
623
+ }, { retryAttempts: Number.POSITIVE_INFINITY });
624
+ }
625
+ catch (error) {
626
+ // The handshake cannot be stopped where it is: it may still introduce
627
+ // this node to the others. Leave the cluster rather than stay in it
628
+ // half-joined, whatever the application does with the failed startup.
629
+ if (error instanceof distributedLock_1.MutexLockLostError) {
630
+ this.logger.error("[CLUSTER] Lost the handshake lock while joining the cluster. Shutting down.");
631
+ void global.kuzzle.shutdown();
632
+ }
633
+ throw error;
628
634
  }
629
635
  finally {
630
636
  clearTimeout(handshakeTimeout);
631
- await mutex.unlock();
632
637
  }
633
638
  }
634
639
  /**
@@ -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;
@@ -1,107 +1,113 @@
1
1
  // sync messages definitions
2
2
  // /!\ ALL messages must have a "messageId" property
3
+ // /!\ Every singular scalar field is "optional": protobufjs 8 otherwise drops a
4
+ // field holding its default value ("", 0, false) on encode and in
5
+ // toObject(), where protobufjs 7 kept it. "optional" keeps the bytes and
6
+ // the decoded objects identical between both versions, which a cluster
7
+ // mixing them during a rolling upgrade relies on. Declare new fields the
8
+ // same way (tests/cluster/protobufWireCompat.test.ts checks the result).
3
9
  package sync;
4
10
 
5
11
  syntax="proto3";
6
12
 
7
13
  message Heartbeat {
8
- uint64 messageId = 1;
9
- string address = 2;
14
+ optional uint64 messageId = 1;
15
+ optional string address = 2;
10
16
  }
11
17
 
12
18
  message NodeEvicted {
13
- uint64 messageId = 1;
14
- string nodeId = 2;
15
- string reason = 3;
16
- string evictor = 4;
19
+ optional uint64 messageId = 1;
20
+ optional string nodeId = 2;
21
+ optional string reason = 3;
22
+ optional string evictor = 4;
17
23
  }
18
24
 
19
25
  message NodeShutdown {
20
- uint64 messageId = 1;
21
- string nodeId = 2;
26
+ optional uint64 messageId = 1;
27
+ optional string nodeId = 2;
22
28
  }
23
29
 
24
30
  message NodePreventEviction {
25
- uint64 messageId = 1;
26
- bool evictionPrevented = 2;
31
+ optional uint64 messageId = 1;
32
+ optional bool evictionPrevented = 2;
27
33
  }
28
34
 
29
35
  message NewRealtimeRoom {
30
- uint64 messageId = 1;
31
- string index = 2;
32
- string id = 3;
36
+ optional uint64 messageId = 1;
37
+ optional string index = 2;
38
+ optional string id = 3;
33
39
 
34
40
  // serialized JSON
35
- string filter = 4;
41
+ optional string filter = 4;
36
42
  }
37
43
 
38
44
  message RemoveRealtimeRoom {
39
- uint64 messageId = 1;
40
- string roomId = 2;
45
+ optional uint64 messageId = 1;
46
+ optional string roomId = 2;
41
47
  }
42
48
 
43
49
  message Subscription {
44
- uint64 messageId = 1;
45
- string roomId = 2;
50
+ optional uint64 messageId = 1;
51
+ optional string roomId = 2;
46
52
  }
47
53
 
48
54
  message Unsubscription {
49
- uint64 messageId = 1;
50
- string roomId = 2;
55
+ optional uint64 messageId = 1;
56
+ optional string roomId = 2;
51
57
  }
52
58
 
53
59
  message DocumentNotification {
54
- uint64 messageId = 1;
60
+ optional uint64 messageId = 1;
55
61
  repeated string rooms = 2;
56
- uint32 status = 3;
57
- string requestId = 4;
58
- uint64 timestamp = 5;
59
- string index = 6;
60
- string collection = 7;
61
- string controller = 8;
62
- string action = 9;
63
- string protocol = 10;
64
- string scope = 11;
62
+ optional uint32 status = 3;
63
+ optional string requestId = 4;
64
+ optional uint64 timestamp = 5;
65
+ optional string index = 6;
66
+ optional string collection = 7;
67
+ optional string controller = 8;
68
+ optional string action = 9;
69
+ optional string protocol = 10;
70
+ optional string scope = 11;
65
71
 
66
72
  // serialized JSON
67
- string volatile = 12;
68
- string result = 13;
73
+ optional string volatile = 12;
74
+ optional string result = 13;
69
75
  }
70
76
 
71
77
  message UserNotification {
72
- uint64 messageId = 1;
73
- string room = 2;
74
- uint32 status = 3;
75
- uint64 timestamp = 4;
76
- string index = 5;
77
- string collection = 6;
78
- string controller = 7;
79
- string action = 8;
80
- string protocol = 9;
81
- string user = 10;
78
+ optional uint64 messageId = 1;
79
+ optional string room = 2;
80
+ optional uint32 status = 3;
81
+ optional uint64 timestamp = 4;
82
+ optional string index = 5;
83
+ optional string collection = 6;
84
+ optional string controller = 7;
85
+ optional string action = 8;
86
+ optional string protocol = 9;
87
+ optional string user = 10;
82
88
 
83
89
  // serialized JSON
84
- string volatile = 11;
85
- string result = 12;
90
+ optional string volatile = 11;
91
+ optional string result = 12;
86
92
  }
87
93
 
88
94
  message NewAuthStrategy {
89
95
  message AuthStrategyConfig {
90
- string authenticator = 1;
96
+ optional string authenticator = 1;
91
97
  repeated string fields = 2;
92
98
 
93
99
  // serialized JSON
94
- string authenticateOptions = 3;
95
- string strategyOptions = 4;
100
+ optional string authenticateOptions = 3;
101
+ optional string strategyOptions = 4;
96
102
  }
97
103
 
98
104
  message AuthStrategyMethods {
99
- string create = 1;
100
- string delete = 2;
101
- string exists = 3;
102
- string update = 4;
103
- string validate = 5;
104
- string verify = 6;
105
+ optional string create = 1;
106
+ optional string delete = 2;
107
+ optional string exists = 3;
108
+ optional string update = 4;
109
+ optional string validate = 5;
110
+ optional string verify = 6;
105
111
  optional string getById = 7;
106
112
  optional string getInfo = 8;
107
113
  optional string afterRegister = 9;
@@ -112,77 +118,77 @@ message NewAuthStrategy {
112
118
  AuthStrategyConfig config = 2;
113
119
  }
114
120
 
115
- uint64 messageId = 1;
116
- string strategyName = 2;
117
- string pluginName = 3;
121
+ optional uint64 messageId = 1;
122
+ optional string strategyName = 2;
123
+ optional string pluginName = 3;
118
124
  AuthStrategy strategy = 4;
119
125
  }
120
126
 
121
127
  message RemoveAuthStrategy {
122
- uint64 messageId = 1;
123
- string strategyName = 2;
124
- string pluginName = 3;
128
+ optional uint64 messageId = 1;
129
+ optional string strategyName = 2;
130
+ optional string pluginName = 3;
125
131
  }
126
132
 
127
133
  message DumpRequest {
128
- uint64 messageId = 1;
129
- string suffix = 2;
134
+ optional uint64 messageId = 1;
135
+ optional string suffix = 2;
130
136
  }
131
137
 
132
138
  message ResetSecurity {
133
- uint64 messageId = 1;
139
+ optional uint64 messageId = 1;
134
140
  }
135
141
 
136
142
  message Shutdown {
137
- uint64 messageId = 1;
143
+ optional uint64 messageId = 1;
138
144
  }
139
145
 
140
146
  message RefreshValidators {
141
- uint64 messageId = 1;
147
+ optional uint64 messageId = 1;
142
148
  }
143
149
 
144
150
  message InvalidateProfile {
145
- uint64 messageId = 1;
146
- string profileId = 2;
151
+ optional uint64 messageId = 1;
152
+ optional string profileId = 2;
147
153
  }
148
154
 
149
155
  message InvalidateRole {
150
- uint64 messageId = 1;
151
- string roleId = 2;
156
+ optional uint64 messageId = 1;
157
+ optional string roleId = 2;
152
158
  }
153
159
 
154
160
  message AddIndex {
155
- uint64 messageId = 1;
156
- string scope = 2;
157
- string index = 3;
161
+ optional uint64 messageId = 1;
162
+ optional string scope = 2;
163
+ optional string index = 3;
158
164
  }
159
165
 
160
166
  message AddCollection {
161
- uint64 messageId = 1;
162
- string scope = 2;
163
- string index = 3;
164
- string collection = 4;
167
+ optional uint64 messageId = 1;
168
+ optional string scope = 2;
169
+ optional string index = 3;
170
+ optional string collection = 4;
165
171
  }
166
172
 
167
173
  message RemoveIndexes {
168
- uint64 messageId = 1;
169
- string scope = 2;
174
+ optional uint64 messageId = 1;
175
+ optional string scope = 2;
170
176
  repeated string indexes = 3;
171
177
  }
172
178
 
173
179
  message RemoveCollection {
174
- uint64 messageId = 1;
175
- string scope = 2;
176
- string index = 3;
177
- string collection = 4;
180
+ optional uint64 messageId = 1;
181
+ optional string scope = 2;
182
+ optional string index = 3;
183
+ optional string collection = 4;
178
184
  }
179
185
 
180
186
  message ClusterWideEvent {
181
- uint64 messageId = 1;
182
- string event = 2;
183
- string payload = 3;
187
+ optional uint64 messageId = 1;
188
+ optional string event = 2;
189
+ optional string payload = 3;
184
190
  }
185
191
 
186
192
  message RefreshIndexCache {
187
- uint64 messageId = 1;
193
+ optional uint64 messageId = 1;
188
194
  }