node-opcua-client 2.166.0 → 2.168.0

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 (102) hide show
  1. package/dist/alarms_and_conditions/client_alarm_tools.d.ts +1 -1
  2. package/dist/alarms_and_conditions/client_alarm_tools.js +2 -2
  3. package/dist/alarms_and_conditions/client_alarm_tools.js.map +1 -1
  4. package/dist/alarms_and_conditions/client_tools.d.ts +3 -0
  5. package/dist/alarms_and_conditions/client_tools.js +2 -2
  6. package/dist/alarms_and_conditions/client_tools.js.map +1 -1
  7. package/dist/client_base.d.ts +47 -40
  8. package/dist/client_base.js +5 -6
  9. package/dist/client_base.js.map +1 -1
  10. package/dist/client_monitored_item.d.ts +7 -7
  11. package/dist/client_monitored_item.js.map +1 -1
  12. package/dist/client_monitored_item_base.d.ts +12 -13
  13. package/dist/client_monitored_item_group.d.ts +5 -5
  14. package/dist/client_monitored_item_toolbox.d.ts +6 -7
  15. package/dist/client_monitored_item_toolbox.js.map +1 -1
  16. package/dist/client_session.d.ts +19 -23
  17. package/dist/client_session.js.map +1 -1
  18. package/dist/client_session_keepalive_manager.d.ts +1 -1
  19. package/dist/client_session_keepalive_manager.js +6 -6
  20. package/dist/client_session_keepalive_manager.js.map +1 -1
  21. package/dist/client_subscription.d.ts +11 -12
  22. package/dist/client_subscription.js.map +1 -1
  23. package/dist/client_utils.d.ts +3 -3
  24. package/dist/client_utils.js.map +1 -1
  25. package/dist/common.d.ts +12 -12
  26. package/dist/index.d.ts +26 -27
  27. package/dist/index.js +65 -66
  28. package/dist/index.js.map +1 -1
  29. package/dist/opcua_client.d.ts +17 -19
  30. package/dist/opcua_client.js.map +1 -1
  31. package/dist/private/client_base_impl.d.ts +19 -18
  32. package/dist/private/client_base_impl.js +220 -192
  33. package/dist/private/client_base_impl.js.map +1 -1
  34. package/dist/private/client_monitored_item_group_impl.d.ts +4 -5
  35. package/dist/private/client_monitored_item_group_impl.js +1 -1
  36. package/dist/private/client_monitored_item_group_impl.js.map +1 -1
  37. package/dist/private/client_monitored_item_impl.d.ts +9 -10
  38. package/dist/private/client_monitored_item_impl.js.map +1 -1
  39. package/dist/private/client_publish_engine.d.ts +3 -3
  40. package/dist/private/client_publish_engine.js +6 -7
  41. package/dist/private/client_publish_engine.js.map +1 -1
  42. package/dist/private/client_session_impl.d.ts +24 -25
  43. package/dist/private/client_session_impl.js +14 -10
  44. package/dist/private/client_session_impl.js.map +1 -1
  45. package/dist/private/client_subscription_impl.d.ts +10 -11
  46. package/dist/private/client_subscription_impl.js +12 -11
  47. package/dist/private/client_subscription_impl.js.map +1 -1
  48. package/dist/private/i_private_client.d.ts +7 -7
  49. package/dist/private/opcua_client_impl.d.ts +12 -9
  50. package/dist/private/opcua_client_impl.js +58 -48
  51. package/dist/private/opcua_client_impl.js.map +1 -1
  52. package/dist/private/performance.js.map +1 -1
  53. package/dist/private/reconnection/client_publish_engine_reconnection.d.ts +1 -1
  54. package/dist/private/reconnection/client_publish_engine_reconnection.js +54 -43
  55. package/dist/private/reconnection/client_publish_engine_reconnection.js.map +1 -1
  56. package/dist/private/reconnection/client_reconnection.d.ts +1 -1
  57. package/dist/private/reconnection/client_subscription_reconnection.d.ts +1 -1
  58. package/dist/private/reconnection/client_subscription_reconnection.js +2 -2
  59. package/dist/private/reconnection/client_subscription_reconnection.js.map +1 -1
  60. package/dist/private/reconnection/reconnection.d.ts +4 -4
  61. package/dist/private/reconnection/reconnection.js +200 -281
  62. package/dist/private/reconnection/reconnection.js.map +1 -1
  63. package/dist/tools/findservers.d.ts +7 -2
  64. package/dist/tools/findservers.js +41 -53
  65. package/dist/tools/findservers.js.map +1 -1
  66. package/dist/tools/read_history_server_capabilities.d.ts +1 -1
  67. package/dist/tools/read_history_server_capabilities.js +9 -8
  68. package/dist/tools/read_history_server_capabilities.js.map +1 -1
  69. package/dist/user_identity_info.d.ts +1 -1
  70. package/dist/verify.js.map +1 -1
  71. package/package.json +45 -45
  72. package/source/alarms_and_conditions/client_alarm_tools.ts +14 -8
  73. package/source/alarms_and_conditions/client_tools.ts +7 -6
  74. package/source/client_base.ts +93 -79
  75. package/source/client_monitored_item.ts +7 -8
  76. package/source/client_monitored_item_base.ts +12 -14
  77. package/source/client_monitored_item_group.ts +5 -5
  78. package/source/client_monitored_item_toolbox.ts +13 -14
  79. package/source/client_session.ts +37 -40
  80. package/source/client_session_keepalive_manager.ts +7 -10
  81. package/source/client_subscription.ts +11 -12
  82. package/source/client_utils.ts +6 -6
  83. package/source/common.ts +12 -12
  84. package/source/index.ts +44 -40
  85. package/source/opcua_client.ts +24 -23
  86. package/source/private/client_base_impl.ts +329 -290
  87. package/source/private/client_monitored_item_group_impl.ts +10 -14
  88. package/source/private/client_monitored_item_impl.ts +17 -17
  89. package/source/private/client_publish_engine.ts +11 -12
  90. package/source/private/client_session_impl.ts +85 -82
  91. package/source/private/client_subscription_impl.ts +42 -41
  92. package/source/private/i_private_client.ts +8 -8
  93. package/source/private/opcua_client_impl.ts +118 -97
  94. package/source/private/performance.ts +2 -3
  95. package/source/private/reconnection/client_publish_engine_reconnection.ts +76 -68
  96. package/source/private/reconnection/client_reconnection.ts +1 -1
  97. package/source/private/reconnection/client_subscription_reconnection.ts +16 -13
  98. package/source/private/reconnection/reconnection.ts +307 -301
  99. package/source/tools/findservers.ts +43 -62
  100. package/source/tools/read_history_server_capabilities.ts +20 -22
  101. package/source/user_identity_info.ts +5 -5
  102. package/source/verify.ts +25 -25
@@ -3,20 +3,16 @@
3
3
  */
4
4
 
5
5
  // tslint:disable:only-arrow-functions
6
- import async from "async";
7
6
  import chalk from "chalk";
8
7
  import { assert } from "node-opcua-assert";
8
+ import { invalidateExtraDataTypeManager } from "node-opcua-client-dynamic-extension-object";
9
9
  import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
10
- import { TransferSubscriptionsRequest, TransferSubscriptionsResponse } from "node-opcua-service-subscription";
11
- import { CallbackT, StatusCode, StatusCodes } from "node-opcua-status-code";
12
- import { ErrorCallback } from "node-opcua-status-code";
10
+ import { TransferSubscriptionsRequest, type TransferSubscriptionsResponse } from "node-opcua-service-subscription";
11
+ import { StatusCodes } from "node-opcua-status-code";
13
12
  import { CloseSessionRequest } from "node-opcua-types";
14
- import { invalidateExtraDataTypeManager } from "node-opcua-client-dynamic-extension-object";
15
-
16
- import { SubscriptionId } from "../../client_session";
17
- import { ClientSessionImpl, Reconnectable } from "../client_session_impl";
18
- import { ClientSubscriptionImpl } from "../client_subscription_impl";
19
- import { IClientBase } from "../i_private_client";
13
+ import type { ClientSessionImpl, Reconnectable } from "../client_session_impl";
14
+ import type { ClientSubscriptionImpl } from "../client_subscription_impl";
15
+ import type { IClientBase } from "../i_private_client";
20
16
  import { republish } from "./client_publish_engine_reconnection";
21
17
  import { recreateSubscriptionAndMonitoredItem } from "./client_subscription_reconnection";
22
18
 
@@ -44,6 +40,12 @@ export function _shouldNotContinue2(subscription: ClientSubscriptionImpl) {
44
40
  }
45
41
  return _shouldNotContinue(subscription.session);
46
42
  }
43
+
44
+ function _throwIfShouldNotContinue(session: ClientSessionImpl): void {
45
+ const err = _shouldNotContinue(session);
46
+ if (err) throw err;
47
+ }
48
+
47
49
  //
48
50
  // a new secure channel has be created, we need to reactivate the corresponding session,
49
51
  // and reestablish the subscription and restart the publish engine.
@@ -108,7 +110,7 @@ export function _shouldNotContinue2(subscription: ClientSubscriptionImpl) {
108
110
  // |
109
111
  // v +-------------------+ +----------------------+
110
112
  // / \ | CreateSession | | |
111
- // / SR? \______Bad_______>| ActivateSession |-----> | TransferSubscription |
113
+ // / SR? \______Bad_____> | ActivateSession |-----> | TransferSubscription |
112
114
  // \ / | | | | (1)
113
115
  // \ / +-------------------+ +----------------------+ ^
114
116
  // | Good | |
@@ -131,7 +133,12 @@ export function _shouldNotContinue2(subscription: ClientSubscriptionImpl) {
131
133
 
132
134
  function _ask_for_subscription_republish(session: ClientSessionImpl, callback: (err?: Error) => void) {
133
135
  // prettier-ignore
134
- { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
136
+ {
137
+ const err = _shouldNotContinue(session);
138
+ if (err) {
139
+ return callback(err);
140
+ }
141
+ }
135
142
 
136
143
  doDebug && debugLog(chalk.bgCyan.yellow.bold("_ask_for_subscription_republish "));
137
144
  // assert(session.getPublishEngine().nbPendingPublishRequests === 0,
@@ -141,7 +148,12 @@ function _ask_for_subscription_republish(session: ClientSessionImpl, callback: (
141
148
  republish(engine, (err?: Error) => {
142
149
  doDebug && debugLog("_ask_for_subscription_republish : republish sent");
143
150
  // prettier-ignore
144
- { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
151
+ {
152
+ const err = _shouldNotContinue(session);
153
+ if (err) {
154
+ return callback(err);
155
+ }
156
+ }
145
157
 
146
158
  doDebug && debugLog(chalk.bgCyan.green.bold("_ask_for_subscription_republish done "), err ? err.message : "OK");
147
159
  if (err) {
@@ -154,299 +166,264 @@ function _ask_for_subscription_republish(session: ClientSessionImpl, callback: (
154
166
  });
155
167
  }
156
168
 
157
- function create_session_and_repeat_if_failed(
158
- client: IClientBase,
159
- session: ClientSessionImpl,
160
- callback: CallbackT<ClientSessionImpl>
161
- ) {
162
- // prettier-ignore
163
- { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
169
+ function create_session_and_repeat_if_failed(client: IClientBase, session: ClientSessionImpl): Promise<ClientSessionImpl> {
170
+ return new Promise<ClientSessionImpl>((resolve, reject) => {
171
+ _throwIfShouldNotContinue(session);
172
+
173
+ doDebug && debugLog(chalk.bgWhite.red(" => creating a new session ...."));
174
+ // create new session, based on old session,
175
+ // so we can reuse subscriptions data
176
+ client.__createSession_step2(session, (err: Error | null, session1?: ClientSessionImpl) => {
177
+ _throwIfShouldNotContinue(session);
178
+
179
+ if (!err && session1) {
180
+ assert(session === session1, "session should have been recycled");
181
+ resolve(session);
182
+ } else {
183
+ doDebug && debugLog("Cannot complete subscription republish err = ", err?.message);
184
+ reject(err || new Error("Failed to create session"));
185
+ }
186
+ });
187
+ });
188
+ }
164
189
 
165
- doDebug && debugLog(chalk.bgWhite.red(" => creating a new session ...."));
166
- // create new session, based on old session,
167
- // so we can reuse subscriptions data
168
- client.__createSession_step2(session, (err: Error | null, session1?: ClientSessionImpl) => {
169
- // prettier-ignore
170
- { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
190
+ function activate_session(client: IClientBase, session: ClientSessionImpl, newSession: ClientSessionImpl): Promise<void> {
191
+ return new Promise<void>((resolve, reject) => {
192
+ _throwIfShouldNotContinue(session);
171
193
 
172
- if (!err && session1) {
173
- assert(session === session1, "session should have been recycled");
174
- callback(err, session);
175
- return;
176
- } else {
177
- doDebug && debugLog("Cannot complete subscription republish err = ", err?.message);
178
- callback(err);
179
- }
194
+ doDebug && debugLog(chalk.bgWhite.red(" => activating a new session ...."));
195
+
196
+ client._activateSession(newSession, newSession.userIdentityInfo!, (err: Error | null, _session1?: ClientSessionImpl) => {
197
+ // c8 ignore next
198
+ doDebug && debugLog(" => activating a new session .... Done err=", err ? err.message : "null");
199
+ if (err) {
200
+ doDebug &&
201
+ debugLog(
202
+ "reactivation of the new session has failed: let be smart and close it before failing this repair attempt"
203
+ );
204
+ // but just on the server side, not on the client side
205
+ const closeSessionRequest = new CloseSessionRequest({
206
+ requestHeader: {
207
+ authenticationToken: newSession.authenticationToken
208
+ },
209
+ deleteSubscriptions: true
210
+ });
211
+ newSession._client?.performMessageTransaction(closeSessionRequest, (err2?: Error | null) => {
212
+ if (err2) {
213
+ warningLog("closing session", err2.message);
214
+ }
215
+ // c8 ignore next
216
+ doDebug && debugLog("the temporary replacement session is now closed");
217
+ // c8 ignore next
218
+ doDebug && debugLog(" err ", err.message, "propagated upwards");
219
+ reject(err);
220
+ });
221
+ } else {
222
+ resolve();
223
+ }
224
+ });
180
225
  });
181
226
  }
182
227
 
183
- function repair_client_session_by_recreating_a_new_session(
184
- client: IClientBase,
228
+ async function recreateSubscriptions(
185
229
  session: ClientSessionImpl,
186
- callback: (err?: Error) => void
187
- ) {
188
- // prettier-ignore
189
- { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
190
-
191
- // As we don"t know if server has been rebooted or not,
192
- // and may be upgraded in between, we have to invalidate the extra data type manager
193
- invalidateExtraDataTypeManager(session);
194
-
195
- // c8 ignore next
196
- if (doDebug) {
197
- debugLog(" repairing client session by_recreating a new session for old session ", session.sessionId.toString());
230
+ newSession: ClientSessionImpl,
231
+ subscriptionsToRecreate: number[]
232
+ ): Promise<void> {
233
+ for (const subscriptionId of subscriptionsToRecreate) {
234
+ _throwIfShouldNotContinue(session);
235
+
236
+ if (!session.getPublishEngine().hasSubscription(subscriptionId)) {
237
+ doDebug && debugLog(chalk.red(" => CANNOT RECREATE SUBSCRIPTION "), subscriptionId);
238
+ continue;
239
+ }
240
+ const subscription = session.getPublishEngine().getSubscription(subscriptionId);
241
+ doDebug && debugLog(chalk.red(" => RECREATING SUBSCRIPTION "), subscriptionId);
242
+ assert(subscription.session === newSession, "must have the new session");
243
+
244
+ try {
245
+ await recreateSubscriptionAndMonitoredItem(subscription);
246
+ doDebug &&
247
+ debugLog(
248
+ chalk.cyan(" => RECREATING SUBSCRIPTION AND MONITORED ITEM DONE subscriptionId="),
249
+ subscriptionId
250
+ );
251
+ } catch (err) {
252
+ doDebug && debugLog(`_recreateSubscription failed !${(err as Error).message}`);
253
+ }
198
254
  }
255
+ _throwIfShouldNotContinue(session);
256
+ }
199
257
 
200
- let newSession: ClientSessionImpl;
258
+ function attempt_subscription_transfer(
259
+ _client: IClientBase,
260
+ session: ClientSessionImpl,
261
+ newSession: ClientSessionImpl
262
+ ): Promise<void> {
263
+ return new Promise<void>((resolve, reject) => {
264
+ _throwIfShouldNotContinue(session);
265
+
266
+ // get the old subscriptions id from the old session
267
+ const subscriptionsIds = session.getPublishEngine().getSubscriptionIds();
268
+
269
+ doDebug && debugLog(" session subscriptionCount = ", newSession.getPublishEngine().subscriptionCount);
270
+ if (subscriptionsIds.length === 0) {
271
+ doDebug && debugLog(" No subscriptions => skipping transfer subscriptions");
272
+ return resolve(); // no need to transfer subscriptions
273
+ }
274
+ doDebug && debugLog(" => asking server to transfer subscriptions = [", subscriptionsIds.join(", "), "]");
201
275
 
202
- const listenerCountBefore = session.listenerCount("");
276
+ // Transfer subscriptions - ask for initial values....
277
+ const subscriptionsToTransfer = new TransferSubscriptionsRequest({
278
+ sendInitialValues: true,
279
+ subscriptionIds: subscriptionsIds
280
+ });
203
281
 
204
- function recreateSubscription(subscriptionsToRecreate: number[], innerCallback: ErrorCallback) {
205
- async.forEach(
206
- subscriptionsToRecreate,
207
- (subscriptionId: SubscriptionId, next: ErrorCallback) => {
208
- // prettier-ignore
209
- { const err = _shouldNotContinue(session); if (err) { return next(err); } }
282
+ if (newSession.getPublishEngine().nbPendingPublishRequests !== 0) {
283
+ warningLog("Warning : we should not be publishing here");
284
+ }
285
+ newSession.transferSubscriptions(
286
+ subscriptionsToTransfer,
287
+ (err: Error | null, transferSubscriptionsResponse?: TransferSubscriptionsResponse) => {
288
+ // may be the connection with server has been disconnected
289
+ _throwIfShouldNotContinue(session);
290
+
291
+ if (err || !transferSubscriptionsResponse) {
292
+ warningLog(chalk.bgCyan("May be the server is not supporting this feature"));
293
+ // when transfer subscription has failed, we have no other choice but
294
+ // recreate the subscriptions on the server side
295
+ const subscriptionsToRecreate = [...(subscriptionsToTransfer.subscriptionIds || [])];
296
+ warningLog(chalk.bgCyan("We need to recreate entirely the subscription"));
297
+ recreateSubscriptions(session, newSession, subscriptionsToRecreate)
298
+ .then(() => resolve())
299
+ .catch((e) => reject(e));
300
+ return;
301
+ }
210
302
 
211
- if (!session.getPublishEngine().hasSubscription(subscriptionId)) {
212
- doDebug && debugLog(chalk.red(" => CANNOT RECREATE SUBSCRIPTION "), subscriptionId);
213
- return next();
303
+ const results = transferSubscriptionsResponse.results || [];
304
+ // c8 ignore next
305
+ if (doDebug) {
306
+ debugLog(
307
+ chalk.cyan(" => transfer subscriptions done"),
308
+ results.map((x) => x.statusCode.toString()).join(" ")
309
+ );
214
310
  }
215
- const subscription = session.getPublishEngine().getSubscription(subscriptionId);
216
- doDebug && debugLog(chalk.red(" => RECREATING SUBSCRIPTION "), subscriptionId);
217
- assert(subscription.session === newSession, "must have the new session");
218
311
 
219
- recreateSubscriptionAndMonitoredItem(subscription)
220
- .then(() => {
312
+ const subscriptionsToRecreate = [];
313
+
314
+ // some subscriptions may be marked as invalid on the server side ..
315
+ // those one need to be recreated and repaired ....
316
+ for (let i = 0; i < results.length; i++) {
317
+ const statusCode = results[i].statusCode;
318
+ if (statusCode.equals(StatusCodes.BadSubscriptionIdInvalid)) {
319
+ // repair subscription
221
320
  doDebug &&
222
321
  debugLog(
223
- chalk.cyan(" => RECREATING SUBSCRIPTION AND MONITORED ITEM DONE subscriptionId="),
224
- subscriptionId
322
+ chalk.red(" WARNING SUBSCRIPTION "),
323
+ subscriptionsIds[i],
324
+ chalk.red(" SHOULD BE RECREATED")
225
325
  );
226
- next();
227
- })
228
- .catch((err) => {
229
- doDebug && debugLog("_recreateSubscription failed !" + (err as Error).message);
230
- next();
231
- });
232
- },
233
- (err1?: Error | null) => {
234
- // prettier-ignore
235
- { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
236
- if (!err1) {
237
- // prettier-ignore
238
- }
239
- innerCallback(err1!);
240
- }
241
- );
242
- }
243
326
 
244
- async.series(
245
- [
246
- function suspend_old_session_publish_engine(innerCallback: ErrorCallback) {
247
- // prettier-ignore
248
- { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
327
+ subscriptionsToRecreate.push(subscriptionsIds[i]);
328
+ } else {
329
+ const availableSequenceNumbers = results[i].availableSequenceNumbers;
249
330
 
250
- // c8 ignore next
251
- doDebug && debugLog(chalk.bgWhite.red(" => suspend old session publish engine...."));
252
- session.getPublishEngine().suspend(true);
253
- innerCallback();
254
- },
255
-
256
- function create_new_session(innerCallback: ErrorCallback) {
257
- create_session_and_repeat_if_failed(client, session, (err?: Error | null, _newSession?: ClientSessionImpl) => {
258
- // prettier-ignore
259
- { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
260
-
261
- if (_newSession) {
262
- newSession = _newSession;
263
- }
264
- innerCallback(err || undefined);
265
- });
266
- },
267
-
268
- function activate_new_session(innerCallback: ErrorCallback) {
269
- // prettier-ignore
270
- { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
271
-
272
- doDebug && debugLog(chalk.bgWhite.red(" => activating a new session ...."));
273
-
274
- client._activateSession(
275
- newSession,
276
- newSession.userIdentityInfo!,
277
- (err: Error | null, session1?: ClientSessionImpl) => {
278
- // c8 ignore next
279
- doDebug && debugLog(" => activating a new session .... Done err=", err ? err.message : "null");
280
- if (err) {
281
- doDebug &&
282
- debugLog(
283
- "reactivation of the new session has failed: let be smart and close it before failing this repair attempt"
284
- );
285
- // but just on the server side, not on the client side
286
- const closeSessionRequest = new CloseSessionRequest({
287
- requestHeader: {
288
- authenticationToken: newSession.authenticationToken
289
- },
290
- deleteSubscriptions: true
291
- });
292
- newSession._client!.performMessageTransaction(closeSessionRequest, (err2?: Error | null) => {
293
- if (err2) {
294
- warningLog("closing session", err2.message);
295
- }
296
- // c8 ignore next
297
- doDebug && debugLog("the temporary replacement session is now closed");
298
- // c8 ignore next
299
- doDebug && debugLog(" err ", err.message, "propagated upwards");
300
- innerCallback(err);
301
- });
302
- } else {
303
- innerCallback(err ? err : undefined);
304
- }
331
+ doDebug &&
332
+ debugLog(
333
+ chalk.green(" SUBSCRIPTION "),
334
+ subscriptionsIds[i],
335
+ chalk.green(" CAN BE REPAIRED AND AVAILABLE "),
336
+ availableSequenceNumbers
337
+ );
338
+ // should be Good.
305
339
  }
306
- );
307
- },
308
-
309
- function beforeSubscriptionRepair(innerCallback: ErrorCallback) {
310
- if (!client.beforeSubscriptionRecreate) {
311
- innerCallback();
312
- return;
313
340
  }
314
- client
315
- .beforeSubscriptionRecreate(newSession)
316
- .then((err) => {
317
- {
318
- const err = _shouldNotContinue(session);
319
- if (err) {
320
- return innerCallback(err);
321
- }
322
- }
323
- if (!err) {
324
- innerCallback();
325
- } else {
326
- innerCallback(err);
327
- }
328
- })
329
- .catch((err) => {
330
- innerCallback(err);
331
- });
332
- },
333
-
334
- function attempt_subscription_transfer(innerCallback: ErrorCallback) {
335
- // prettier-ignore
336
- { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
337
-
338
- // get the old subscriptions id from the old session
339
- const subscriptionsIds = session.getPublishEngine().getSubscriptionIds();
340
-
341
- doDebug && debugLog(" session subscriptionCount = ", newSession.getPublishEngine().subscriptionCount);
342
- if (subscriptionsIds.length === 0) {
343
- doDebug && debugLog(" No subscriptions => skipping transfer subscriptions");
344
- return innerCallback(); // no need to transfer subscriptions
345
- }
346
- doDebug && debugLog(" => asking server to transfer subscriptions = [", subscriptionsIds.join(", "), "]");
341
+ doDebug && debugLog(" new session subscriptionCount = ", newSession.getPublishEngine().subscriptionCount);
347
342
 
348
- // Transfer subscriptions - ask for initial values....
349
- const subscriptionsToTransfer = new TransferSubscriptionsRequest({
350
- sendInitialValues: true,
351
- subscriptionIds: subscriptionsIds
352
- });
343
+ recreateSubscriptions(session, newSession, subscriptionsToRecreate)
344
+ .then(() => resolve())
345
+ .catch((e) => reject(e));
346
+ }
347
+ );
348
+ });
349
+ }
353
350
 
354
- if (newSession.getPublishEngine().nbPendingPublishRequests !== 0) {
355
- warningLog("Warning : we should not be publishing here");
356
- }
357
- newSession.transferSubscriptions(
358
- subscriptionsToTransfer,
359
- (err: Error | null, transferSubscriptionsResponse?: TransferSubscriptionsResponse) => {
360
- // may be the connection with server has been disconnected
361
- // prettier-ignore
362
- { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
363
-
364
- if (err || !transferSubscriptionsResponse) {
365
- warningLog(chalk.bgCyan("May be the server is not supporting this feature"));
366
- // when transfer subscription has failed, we have no other choice but
367
- // recreate the subscriptions on the server side
368
- const subscriptionsToRecreate = [...(subscriptionsToTransfer.subscriptionIds || [])];
369
- warningLog(chalk.bgCyan("We need to recreate entirely the subscription"));
370
- recreateSubscription(subscriptionsToRecreate, innerCallback);
371
- return;
372
- }
351
+ async function repair_client_session_by_recreating_a_new_session_async(
352
+ client: IClientBase,
353
+ session: ClientSessionImpl
354
+ ): Promise<void> {
355
+ _throwIfShouldNotContinue(session);
373
356
 
374
- const results = transferSubscriptionsResponse.results || [];
375
- // c8 ignore next
376
- if (doDebug) {
377
- debugLog(
378
- chalk.cyan(" => transfer subscriptions done"),
379
- results.map((x: any) => x.statusCode.toString()).join(" ")
380
- );
381
- }
357
+ // As we don't know if server has been rebooted or not,
358
+ // and may be upgraded in between, we have to invalidate the extra data type manager
359
+ invalidateExtraDataTypeManager(session);
382
360
 
383
- const subscriptionsToRecreate = [];
384
-
385
- // some subscriptions may be marked as invalid on the server side ...
386
- // those one need to be recreated and repaired ....
387
- for (let i = 0; i < results.length; i++) {
388
- const statusCode = results[i].statusCode;
389
- if (statusCode.equals(StatusCodes.BadSubscriptionIdInvalid)) {
390
- // repair subscription
391
- doDebug &&
392
- debugLog(
393
- chalk.red(" WARNING SUBSCRIPTION "),
394
- subscriptionsIds[i],
395
- chalk.red(" SHOULD BE RECREATED")
396
- );
397
-
398
- subscriptionsToRecreate.push(subscriptionsIds[i]);
399
- } else {
400
- const availableSequenceNumbers = results[i].availableSequenceNumbers;
401
-
402
- doDebug &&
403
- debugLog(
404
- chalk.green(" SUBSCRIPTION "),
405
- subscriptionsIds[i],
406
- chalk.green(" CAN BE REPAIRED AND AVAILABLE "),
407
- availableSequenceNumbers
408
- );
409
- // should be Good.
410
- }
411
- }
412
- doDebug && debugLog(" new session subscriptionCount = ", newSession.getPublishEngine().subscriptionCount);
361
+ // c8 ignore next
362
+ if (doDebug) {
363
+ debugLog(" repairing client session by_recreating a new session for old session ", session.sessionId.toString());
364
+ }
413
365
 
414
- recreateSubscription(subscriptionsToRecreate, innerCallback);
415
- }
416
- );
417
- },
366
+ const listenerCountBefore = session.listenerCount("");
418
367
 
419
- function ask_for_subscription_republish(innerCallback: ErrorCallback) {
420
- // prettier-ignore
421
- { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
368
+ // Step 1: suspend old session publish engine
369
+ _throwIfShouldNotContinue(session);
370
+ // c8 ignore next
371
+ doDebug && debugLog(chalk.bgWhite.red(" => suspend old session publish engine...."));
372
+ session.getPublishEngine().suspend(true);
373
+
374
+ // Step 2: create new session
375
+ const newSession = await create_session_and_repeat_if_failed(client, session);
376
+ _throwIfShouldNotContinue(session);
377
+
378
+ // Step 3: activate new session
379
+ await activate_session(client, session, newSession);
380
+ _throwIfShouldNotContinue(session);
381
+
382
+ // Step 4: before subscription repair hook
383
+ if (client.beforeSubscriptionRecreate) {
384
+ const hookErr = await client.beforeSubscriptionRecreate(newSession);
385
+ _throwIfShouldNotContinue(session);
386
+ if (hookErr) {
387
+ throw hookErr;
388
+ }
389
+ }
422
390
 
423
- // assert(newSession.getPublishEngine().nbPendingPublishRequests === 0, "we should not be publishing here");
424
- // call Republish
425
- return _ask_for_subscription_republish(newSession, (err) => {
426
- if (err) {
427
- warningLog("warning: Subscription republished has failed ", err.message);
428
- }
429
- innerCallback(err);
430
- });
431
- },
391
+ // Step 5: attempt subscription transfer
392
+ await attempt_subscription_transfer(client, session, newSession);
393
+ _throwIfShouldNotContinue(session);
394
+
395
+ // Step 6: ask for subscription republish
396
+ await new Promise<void>((resolve, reject) => {
397
+ _ask_for_subscription_republish(newSession, (err) => {
398
+ if (err) {
399
+ warningLog("warning: Subscription republished has failed ", err.message);
400
+ reject(err);
401
+ } else {
402
+ resolve();
403
+ }
404
+ });
405
+ });
406
+ _throwIfShouldNotContinue(session);
432
407
 
433
- function start_publishing_as_normal(innerCallback: ErrorCallback) {
434
- // prettier-ignore
435
- { const err = _shouldNotContinue(session); if (err) { return innerCallback(err); } }
408
+ // Step 7: start publishing as normal
409
+ newSession.getPublishEngine().suspend(false);
436
410
 
437
- newSession.getPublishEngine().suspend(false);
411
+ const listenerCountAfter = session.listenerCount("");
412
+ assert(newSession === session);
413
+ doDebug && debugLog("listenerCountBefore =", listenerCountBefore, "listenerCountAfter = ", listenerCountAfter);
414
+ }
438
415
 
439
- const listenerCountAfter = session.listenerCount("");
440
- assert(newSession === session);
441
- doDebug && debugLog("listenerCountBefore =", listenerCountBefore, "listenerCountAfter = ", listenerCountAfter);
442
- innerCallback();
443
- }
444
- ],
445
- (err) => {
446
- doDebug && err && debugLog("repair_client_session_by_recreating_a_new_session failed with ", err.message);
447
- callback(err!);
448
- }
449
- );
416
+ function repair_client_session_by_recreating_a_new_session(
417
+ client: IClientBase,
418
+ session: ClientSessionImpl,
419
+ callback: (err?: Error) => void
420
+ ) {
421
+ repair_client_session_by_recreating_a_new_session_async(client, session)
422
+ .then(() => callback())
423
+ .catch((err) => {
424
+ doDebug && debugLog("repair_client_session_by_recreating_a_new_session failed with ", err.message);
425
+ callback(err);
426
+ });
450
427
  }
451
428
 
452
429
  function _repair_client_session(client: IClientBase, session: ClientSessionImpl, callback: (err?: Error) => void): void {
@@ -467,11 +444,21 @@ function _repair_client_session(client: IClientBase, session: ClientSessionImpl,
467
444
  }
468
445
 
469
446
  // prettier-ignore
470
- { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
447
+ {
448
+ const err = _shouldNotContinue(session);
449
+ if (err) {
450
+ return callback(err);
451
+ }
452
+ }
471
453
 
472
- client._activateSession(session, session.userIdentityInfo!, (err: Error | null, session2?: ClientSessionImpl) => {
454
+ client._activateSession(session, session.userIdentityInfo!, (err: Error | null, _session2?: ClientSessionImpl) => {
473
455
  // prettier-ignore
474
- { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
456
+ {
457
+ const err = _shouldNotContinue(session);
458
+ if (err) {
459
+ return callback(err);
460
+ }
461
+ }
475
462
  //
476
463
  // Note: current limitation :
477
464
  // - The reconnection doesn't work yet, if connection break is caused by a server that crashes and restarts.
@@ -496,7 +483,7 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
496
483
  }
497
484
  doDebug && debugLog(chalk.yellow("Starting client session repair"));
498
485
 
499
- const privateSession = session as any as Reconnectable;
486
+ const privateSession = session as unknown as Reconnectable;
500
487
  privateSession._reconnecting = privateSession._reconnecting || { reconnecting: false, pendingCallbacks: [] };
501
488
 
502
489
  if (session.hasBeenClosed()) {
@@ -513,15 +500,25 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
513
500
  privateSession._reconnecting.reconnecting = true;
514
501
 
515
502
  // get old transaction queue ...
516
- const transactionQueue = privateSession._reconnecting.pendingTransactions.splice(0);
503
+ const transactionQueue = privateSession._reconnecting.pendingTransactions.splice(0);
517
504
 
518
505
  const repeatedAction = (callback: EmptyCallback) => {
519
506
  // prettier-ignore
520
- { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
507
+ {
508
+ const err = _shouldNotContinue(session);
509
+ if (err) {
510
+ return callback(err);
511
+ }
512
+ }
521
513
 
522
514
  _repair_client_session(client, session, (err) => {
523
515
  // prettier-ignore
524
- { const err = _shouldNotContinue(session); if (err) { return callback(err); } }
516
+ {
517
+ const err = _shouldNotContinue(session);
518
+ if (err) {
519
+ return callback(err);
520
+ }
521
+ }
525
522
 
526
523
  if (err) {
527
524
  errorLog(
@@ -534,10 +531,13 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
534
531
  const delay = 2000;
535
532
  errorLog(chalk.red(`... will retry session repair... in ${delay} ms`));
536
533
  setTimeout(() => {
537
- { const err = _shouldNotContinue(session); if (err) {
538
- warningLog("cancelling session repair");
539
- return callback(err);
540
- } }
534
+ {
535
+ const err = _shouldNotContinue(session);
536
+ if (err) {
537
+ warningLog("cancelling session repair");
538
+ return callback(err);
539
+ }
540
+ }
541
541
  errorLog(chalk.red("Retrying session repair..."));
542
542
  repeatedAction(callback);
543
543
  }, delay);
@@ -560,34 +560,40 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
560
560
  privateSession._reconnecting.reconnecting = false;
561
561
  const otherCallbacks = privateSession._reconnecting.pendingCallbacks.splice(0);
562
562
  // re-inject element in queue
563
-
563
+
564
564
  // c8 ignore next
565
565
  if (transactionQueue.length > 0) {
566
566
  doDebug && debugLog(chalk.yellow("re-injecting transaction queue"), transactionQueue.length);
567
- transactionQueue.forEach((e: any) => privateSession._reconnecting.pendingTransactions.push(e));
567
+ transactionQueue.forEach((e) => privateSession._reconnecting.pendingTransactions.push(e));
568
568
  }
569
569
  otherCallbacks.forEach((c: EmptyCallback) => c(err));
570
570
  callback(err);
571
571
  });
572
572
  }
573
573
 
574
- export function repair_client_sessions(client: IClientBase, callback: (err?: Error) => void): void {
574
+ export async function repair_client_sessions(client: IClientBase, callback: (err?: Error) => void): Promise<void> {
575
575
  // repair session
576
576
  const sessions = client.getSessions();
577
577
  doDebug && debugLog(chalk.red.bgWhite(" Starting sessions reactivation", sessions.length));
578
- async.map(
579
- sessions,
580
- (session, next: (err: Error | null, err2: Error | null | undefined) => void) => {
578
+
579
+ const allErrors: (Error | undefined)[] = [];
580
+ for (const session of sessions) {
581
+ await new Promise<void>((resolve) => {
581
582
  repair_client_session(client, session as ClientSessionImpl, (err) => {
582
- next(null, err);
583
+ allErrors.push(err);
584
+ resolve();
583
585
  });
584
- },
585
- (err, allErrors: (undefined | Error | null)[] | undefined) => {
586
- err && errorLog("sessions reactivation completed with err: err ", err ? err.message : "null");
587
- // prettier-ignore
588
- { const err = _shouldNotContinue3(client); if (err) { return callback(err); } }
586
+ });
587
+ }
589
588
 
590
- return callback(err!);
589
+ // prettier-ignore
590
+ {
591
+ const err = _shouldNotContinue3(client);
592
+ if (err) {
593
+ return callback(err);
591
594
  }
592
- );
595
+ }
596
+
597
+ const firstError = allErrors.find((e) => e != null);
598
+ callback(firstError);
593
599
  }