node-opcua-client 2.167.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.
- package/dist/private/client_base_impl.js +88 -81
- package/dist/private/client_base_impl.js.map +1 -1
- package/dist/private/reconnection/client_publish_engine_reconnection.js +54 -45
- package/dist/private/reconnection/client_publish_engine_reconnection.js.map +1 -1
- package/dist/private/reconnection/reconnection.d.ts +1 -1
- package/dist/private/reconnection/reconnection.js +199 -280
- package/dist/private/reconnection/reconnection.js.map +1 -1
- package/package.json +41 -42
- package/source/private/client_base_impl.ts +142 -193
- package/source/private/reconnection/client_publish_engine_reconnection.ts +67 -60
- package/source/private/reconnection/reconnection.ts +253 -344
|
@@ -3,16 +3,13 @@
|
|
|
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";
|
|
9
8
|
import { invalidateExtraDataTypeManager } from "node-opcua-client-dynamic-extension-object";
|
|
10
9
|
import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
|
|
11
10
|
import { TransferSubscriptionsRequest, type TransferSubscriptionsResponse } from "node-opcua-service-subscription";
|
|
12
|
-
import {
|
|
11
|
+
import { StatusCodes } from "node-opcua-status-code";
|
|
13
12
|
import { CloseSessionRequest } from "node-opcua-types";
|
|
14
|
-
|
|
15
|
-
import type { SubscriptionId } from "../../client_session";
|
|
16
13
|
import type { ClientSessionImpl, Reconnectable } from "../client_session_impl";
|
|
17
14
|
import type { ClientSubscriptionImpl } from "../client_subscription_impl";
|
|
18
15
|
import type { IClientBase } from "../i_private_client";
|
|
@@ -43,6 +40,12 @@ export function _shouldNotContinue2(subscription: ClientSubscriptionImpl) {
|
|
|
43
40
|
}
|
|
44
41
|
return _shouldNotContinue(subscription.session);
|
|
45
42
|
}
|
|
43
|
+
|
|
44
|
+
function _throwIfShouldNotContinue(session: ClientSessionImpl): void {
|
|
45
|
+
const err = _shouldNotContinue(session);
|
|
46
|
+
if (err) throw err;
|
|
47
|
+
}
|
|
48
|
+
|
|
46
49
|
//
|
|
47
50
|
// a new secure channel has be created, we need to reactivate the corresponding session,
|
|
48
51
|
// and reestablish the subscription and restart the publish engine.
|
|
@@ -107,7 +110,7 @@ export function _shouldNotContinue2(subscription: ClientSubscriptionImpl) {
|
|
|
107
110
|
// |
|
|
108
111
|
// v +-------------------+ +----------------------+
|
|
109
112
|
// / \ | CreateSession | | |
|
|
110
|
-
// / SR? \
|
|
113
|
+
// / SR? \______Bad_____> | ActivateSession |-----> | TransferSubscription |
|
|
111
114
|
// \ / | | | | (1)
|
|
112
115
|
// \ / +-------------------+ +----------------------+ ^
|
|
113
116
|
// | Good | |
|
|
@@ -163,359 +166,264 @@ function _ask_for_subscription_republish(session: ClientSessionImpl, callback: (
|
|
|
163
166
|
});
|
|
164
167
|
}
|
|
165
168
|
|
|
166
|
-
function create_session_and_repeat_if_failed(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
+
}
|
|
178
189
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
190
|
+
function activate_session(client: IClientBase, session: ClientSessionImpl, newSession: ClientSessionImpl): Promise<void> {
|
|
191
|
+
return new Promise<void>((resolve, reject) => {
|
|
192
|
+
_throwIfShouldNotContinue(session);
|
|
193
|
+
|
|
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");
|
|
186
199
|
if (err) {
|
|
187
|
-
|
|
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();
|
|
188
223
|
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (!err && session1) {
|
|
192
|
-
assert(session === session1, "session should have been recycled");
|
|
193
|
-
callback(err, session);
|
|
194
|
-
return;
|
|
195
|
-
} else {
|
|
196
|
-
doDebug && debugLog("Cannot complete subscription republish err = ", err?.message);
|
|
197
|
-
callback(err);
|
|
198
|
-
}
|
|
224
|
+
});
|
|
199
225
|
});
|
|
200
226
|
}
|
|
201
227
|
|
|
202
|
-
function
|
|
203
|
-
client: IClientBase,
|
|
228
|
+
async function recreateSubscriptions(
|
|
204
229
|
session: ClientSessionImpl,
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
{
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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}`);
|
|
212
253
|
}
|
|
213
254
|
}
|
|
255
|
+
_throwIfShouldNotContinue(session);
|
|
256
|
+
}
|
|
214
257
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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(", "), "]");
|
|
225
275
|
|
|
226
|
-
|
|
276
|
+
// Transfer subscriptions - ask for initial values....
|
|
277
|
+
const subscriptionsToTransfer = new TransferSubscriptionsRequest({
|
|
278
|
+
sendInitialValues: true,
|
|
279
|
+
subscriptionIds: subscriptionsIds
|
|
280
|
+
});
|
|
227
281
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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;
|
|
238
301
|
}
|
|
239
302
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
+
);
|
|
243
310
|
}
|
|
244
|
-
const subscription = session.getPublishEngine().getSubscription(subscriptionId);
|
|
245
|
-
doDebug && debugLog(chalk.red(" => RECREATING SUBSCRIPTION "), subscriptionId);
|
|
246
|
-
assert(subscription.session === newSession, "must have the new session");
|
|
247
311
|
|
|
248
|
-
|
|
249
|
-
|
|
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
|
|
250
320
|
doDebug &&
|
|
251
321
|
debugLog(
|
|
252
|
-
chalk.
|
|
253
|
-
|
|
322
|
+
chalk.red(" WARNING SUBSCRIPTION "),
|
|
323
|
+
subscriptionsIds[i],
|
|
324
|
+
chalk.red(" SHOULD BE RECREATED")
|
|
254
325
|
);
|
|
255
|
-
next();
|
|
256
|
-
})
|
|
257
|
-
.catch((err) => {
|
|
258
|
-
doDebug && debugLog("_recreateSubscription failed !" + (err as Error).message);
|
|
259
|
-
next();
|
|
260
|
-
});
|
|
261
|
-
},
|
|
262
|
-
(err1?: Error | null) => {
|
|
263
|
-
// prettier-ignore
|
|
264
|
-
{
|
|
265
|
-
const err = _shouldNotContinue(session);
|
|
266
|
-
if (err) {
|
|
267
|
-
return innerCallback(err);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
if (!err1) {
|
|
271
|
-
// prettier-ignore
|
|
272
|
-
}
|
|
273
|
-
innerCallback(err1!);
|
|
274
|
-
}
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
async.series(
|
|
279
|
-
[
|
|
280
|
-
function suspend_old_session_publish_engine(innerCallback: ErrorCallback) {
|
|
281
|
-
// prettier-ignore
|
|
282
|
-
{
|
|
283
|
-
const err = _shouldNotContinue(session);
|
|
284
|
-
if (err) {
|
|
285
|
-
return innerCallback(err);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// c8 ignore next
|
|
290
|
-
doDebug && debugLog(chalk.bgWhite.red(" => suspend old session publish engine...."));
|
|
291
|
-
session.getPublishEngine().suspend(true);
|
|
292
|
-
innerCallback();
|
|
293
|
-
},
|
|
294
|
-
|
|
295
|
-
function create_new_session(innerCallback: ErrorCallback) {
|
|
296
|
-
create_session_and_repeat_if_failed(client, session, (err?: Error | null, _newSession?: ClientSessionImpl) => {
|
|
297
|
-
// prettier-ignore
|
|
298
|
-
{
|
|
299
|
-
const err = _shouldNotContinue(session);
|
|
300
|
-
if (err) {
|
|
301
|
-
return innerCallback(err);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
326
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
innerCallback(err || undefined);
|
|
309
|
-
});
|
|
310
|
-
},
|
|
311
|
-
|
|
312
|
-
function activate_new_session(innerCallback: ErrorCallback) {
|
|
313
|
-
// prettier-ignore
|
|
314
|
-
{
|
|
315
|
-
const err = _shouldNotContinue(session);
|
|
316
|
-
if (err) {
|
|
317
|
-
return innerCallback(err);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
327
|
+
subscriptionsToRecreate.push(subscriptionsIds[i]);
|
|
328
|
+
} else {
|
|
329
|
+
const availableSequenceNumbers = results[i].availableSequenceNumbers;
|
|
320
330
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
if (err) {
|
|
330
|
-
doDebug &&
|
|
331
|
-
debugLog(
|
|
332
|
-
"reactivation of the new session has failed: let be smart and close it before failing this repair attempt"
|
|
333
|
-
);
|
|
334
|
-
// but just on the server side, not on the client side
|
|
335
|
-
const closeSessionRequest = new CloseSessionRequest({
|
|
336
|
-
requestHeader: {
|
|
337
|
-
authenticationToken: newSession.authenticationToken
|
|
338
|
-
},
|
|
339
|
-
deleteSubscriptions: true
|
|
340
|
-
});
|
|
341
|
-
newSession._client!.performMessageTransaction(closeSessionRequest, (err2?: Error | null) => {
|
|
342
|
-
if (err2) {
|
|
343
|
-
warningLog("closing session", err2.message);
|
|
344
|
-
}
|
|
345
|
-
// c8 ignore next
|
|
346
|
-
doDebug && debugLog("the temporary replacement session is now closed");
|
|
347
|
-
// c8 ignore next
|
|
348
|
-
doDebug && debugLog(" err ", err.message, "propagated upwards");
|
|
349
|
-
innerCallback(err);
|
|
350
|
-
});
|
|
351
|
-
} else {
|
|
352
|
-
innerCallback(err ? err : undefined);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
);
|
|
356
|
-
},
|
|
357
|
-
|
|
358
|
-
function beforeSubscriptionRepair(innerCallback: ErrorCallback) {
|
|
359
|
-
if (!client.beforeSubscriptionRecreate) {
|
|
360
|
-
innerCallback();
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
client
|
|
364
|
-
.beforeSubscriptionRecreate(newSession)
|
|
365
|
-
.then((err) => {
|
|
366
|
-
{
|
|
367
|
-
const err = _shouldNotContinue(session);
|
|
368
|
-
if (err) {
|
|
369
|
-
return innerCallback(err);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
if (!err) {
|
|
373
|
-
innerCallback();
|
|
374
|
-
} else {
|
|
375
|
-
innerCallback(err);
|
|
376
|
-
}
|
|
377
|
-
})
|
|
378
|
-
.catch((err) => {
|
|
379
|
-
innerCallback(err);
|
|
380
|
-
});
|
|
381
|
-
},
|
|
382
|
-
|
|
383
|
-
function attempt_subscription_transfer(innerCallback: ErrorCallback) {
|
|
384
|
-
// prettier-ignore
|
|
385
|
-
{
|
|
386
|
-
const err = _shouldNotContinue(session);
|
|
387
|
-
if (err) {
|
|
388
|
-
return innerCallback(err);
|
|
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.
|
|
389
339
|
}
|
|
390
340
|
}
|
|
341
|
+
doDebug && debugLog(" new session subscriptionCount = ", newSession.getPublishEngine().subscriptionCount);
|
|
391
342
|
|
|
392
|
-
|
|
393
|
-
|
|
343
|
+
recreateSubscriptions(session, newSession, subscriptionsToRecreate)
|
|
344
|
+
.then(() => resolve())
|
|
345
|
+
.catch((e) => reject(e));
|
|
346
|
+
}
|
|
347
|
+
);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
394
350
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
doDebug && debugLog(" => asking server to transfer subscriptions = [", subscriptionsIds.join(", "), "]");
|
|
351
|
+
async function repair_client_session_by_recreating_a_new_session_async(
|
|
352
|
+
client: IClientBase,
|
|
353
|
+
session: ClientSessionImpl
|
|
354
|
+
): Promise<void> {
|
|
355
|
+
_throwIfShouldNotContinue(session);
|
|
401
356
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
subscriptionIds: subscriptionsIds
|
|
406
|
-
});
|
|
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);
|
|
407
360
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
subscriptionsToTransfer,
|
|
413
|
-
(err: Error | null, transferSubscriptionsResponse?: TransferSubscriptionsResponse) => {
|
|
414
|
-
// may be the connection with server has been disconnected
|
|
415
|
-
// prettier-ignore
|
|
416
|
-
{
|
|
417
|
-
const err = _shouldNotContinue(session);
|
|
418
|
-
if (err) {
|
|
419
|
-
return innerCallback(err);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
361
|
+
// c8 ignore next
|
|
362
|
+
if (doDebug) {
|
|
363
|
+
debugLog(" repairing client session by_recreating a new session for old session ", session.sessionId.toString());
|
|
364
|
+
}
|
|
422
365
|
|
|
423
|
-
|
|
424
|
-
warningLog(chalk.bgCyan("May be the server is not supporting this feature"));
|
|
425
|
-
// when transfer subscription has failed, we have no other choice but
|
|
426
|
-
// recreate the subscriptions on the server side
|
|
427
|
-
const subscriptionsToRecreate = [...(subscriptionsToTransfer.subscriptionIds || [])];
|
|
428
|
-
warningLog(chalk.bgCyan("We need to recreate entirely the subscription"));
|
|
429
|
-
recreateSubscription(subscriptionsToRecreate, innerCallback);
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
366
|
+
const listenerCountBefore = session.listenerCount("");
|
|
432
367
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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
|
+
}
|
|
441
390
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
// those one need to be recreated and repaired ....
|
|
446
|
-
for (let i = 0; i < results.length; i++) {
|
|
447
|
-
const statusCode = results[i].statusCode;
|
|
448
|
-
if (statusCode.equals(StatusCodes.BadSubscriptionIdInvalid)) {
|
|
449
|
-
// repair subscription
|
|
450
|
-
doDebug &&
|
|
451
|
-
debugLog(
|
|
452
|
-
chalk.red(" WARNING SUBSCRIPTION "),
|
|
453
|
-
subscriptionsIds[i],
|
|
454
|
-
chalk.red(" SHOULD BE RECREATED")
|
|
455
|
-
);
|
|
456
|
-
|
|
457
|
-
subscriptionsToRecreate.push(subscriptionsIds[i]);
|
|
458
|
-
} else {
|
|
459
|
-
const availableSequenceNumbers = results[i].availableSequenceNumbers;
|
|
460
|
-
|
|
461
|
-
doDebug &&
|
|
462
|
-
debugLog(
|
|
463
|
-
chalk.green(" SUBSCRIPTION "),
|
|
464
|
-
subscriptionsIds[i],
|
|
465
|
-
chalk.green(" CAN BE REPAIRED AND AVAILABLE "),
|
|
466
|
-
availableSequenceNumbers
|
|
467
|
-
);
|
|
468
|
-
// should be Good.
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
doDebug && debugLog(" new session subscriptionCount = ", newSession.getPublishEngine().subscriptionCount);
|
|
391
|
+
// Step 5: attempt subscription transfer
|
|
392
|
+
await attempt_subscription_transfer(client, session, newSession);
|
|
393
|
+
_throwIfShouldNotContinue(session);
|
|
472
394
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}
|
|
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);
|
|
486
407
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
return _ask_for_subscription_republish(newSession, (err) => {
|
|
490
|
-
if (err) {
|
|
491
|
-
warningLog("warning: Subscription republished has failed ", err.message);
|
|
492
|
-
}
|
|
493
|
-
innerCallback(err);
|
|
494
|
-
});
|
|
495
|
-
},
|
|
496
|
-
|
|
497
|
-
function start_publishing_as_normal(innerCallback: ErrorCallback) {
|
|
498
|
-
// prettier-ignore
|
|
499
|
-
{
|
|
500
|
-
const err = _shouldNotContinue(session);
|
|
501
|
-
if (err) {
|
|
502
|
-
return innerCallback(err);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
408
|
+
// Step 7: start publishing as normal
|
|
409
|
+
newSession.getPublishEngine().suspend(false);
|
|
505
410
|
|
|
506
|
-
|
|
411
|
+
const listenerCountAfter = session.listenerCount("");
|
|
412
|
+
assert(newSession === session);
|
|
413
|
+
doDebug && debugLog("listenerCountBefore =", listenerCountBefore, "listenerCountAfter = ", listenerCountAfter);
|
|
414
|
+
}
|
|
507
415
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
(
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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
|
+
});
|
|
519
427
|
}
|
|
520
428
|
|
|
521
429
|
function _repair_client_session(client: IClientBase, session: ClientSessionImpl, callback: (err?: Error) => void): void {
|
|
@@ -543,7 +451,7 @@ function _repair_client_session(client: IClientBase, session: ClientSessionImpl,
|
|
|
543
451
|
}
|
|
544
452
|
}
|
|
545
453
|
|
|
546
|
-
client._activateSession(session, session.userIdentityInfo!, (err: Error | null,
|
|
454
|
+
client._activateSession(session, session.userIdentityInfo!, (err: Error | null, _session2?: ClientSessionImpl) => {
|
|
547
455
|
// prettier-ignore
|
|
548
456
|
{
|
|
549
457
|
const err = _shouldNotContinue(session);
|
|
@@ -575,7 +483,7 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
|
|
|
575
483
|
}
|
|
576
484
|
doDebug && debugLog(chalk.yellow("Starting client session repair"));
|
|
577
485
|
|
|
578
|
-
const privateSession = session as
|
|
486
|
+
const privateSession = session as unknown as Reconnectable;
|
|
579
487
|
privateSession._reconnecting = privateSession._reconnecting || { reconnecting: false, pendingCallbacks: [] };
|
|
580
488
|
|
|
581
489
|
if (session.hasBeenClosed()) {
|
|
@@ -656,35 +564,36 @@ export function repair_client_session(client: IClientBase, session: ClientSessio
|
|
|
656
564
|
// c8 ignore next
|
|
657
565
|
if (transactionQueue.length > 0) {
|
|
658
566
|
doDebug && debugLog(chalk.yellow("re-injecting transaction queue"), transactionQueue.length);
|
|
659
|
-
transactionQueue.forEach((e
|
|
567
|
+
transactionQueue.forEach((e) => privateSession._reconnecting.pendingTransactions.push(e));
|
|
660
568
|
}
|
|
661
569
|
otherCallbacks.forEach((c: EmptyCallback) => c(err));
|
|
662
570
|
callback(err);
|
|
663
571
|
});
|
|
664
572
|
}
|
|
665
573
|
|
|
666
|
-
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> {
|
|
667
575
|
// repair session
|
|
668
576
|
const sessions = client.getSessions();
|
|
669
577
|
doDebug && debugLog(chalk.red.bgWhite(" Starting sessions reactivation", sessions.length));
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
578
|
+
|
|
579
|
+
const allErrors: (Error | undefined)[] = [];
|
|
580
|
+
for (const session of sessions) {
|
|
581
|
+
await new Promise<void>((resolve) => {
|
|
673
582
|
repair_client_session(client, session as ClientSessionImpl, (err) => {
|
|
674
|
-
|
|
583
|
+
allErrors.push(err);
|
|
584
|
+
resolve();
|
|
675
585
|
});
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
err && errorLog("sessions reactivation completed with err: err ", err ? err.message : "null");
|
|
679
|
-
// prettier-ignore
|
|
680
|
-
{
|
|
681
|
-
const err = _shouldNotContinue3(client);
|
|
682
|
-
if (err) {
|
|
683
|
-
return callback(err);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
586
|
+
});
|
|
587
|
+
}
|
|
686
588
|
|
|
687
|
-
|
|
589
|
+
// prettier-ignore
|
|
590
|
+
{
|
|
591
|
+
const err = _shouldNotContinue3(client);
|
|
592
|
+
if (err) {
|
|
593
|
+
return callback(err);
|
|
688
594
|
}
|
|
689
|
-
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
const firstError = allErrors.find((e) => e != null);
|
|
598
|
+
callback(firstError);
|
|
690
599
|
}
|