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.
- package/dist/alarms_and_conditions/client_alarm_tools.d.ts +1 -1
- package/dist/alarms_and_conditions/client_alarm_tools.js +2 -2
- package/dist/alarms_and_conditions/client_alarm_tools.js.map +1 -1
- package/dist/alarms_and_conditions/client_tools.d.ts +3 -0
- package/dist/alarms_and_conditions/client_tools.js +2 -2
- package/dist/alarms_and_conditions/client_tools.js.map +1 -1
- package/dist/client_base.d.ts +47 -40
- package/dist/client_base.js +5 -6
- package/dist/client_base.js.map +1 -1
- package/dist/client_monitored_item.d.ts +7 -7
- package/dist/client_monitored_item.js.map +1 -1
- package/dist/client_monitored_item_base.d.ts +12 -13
- package/dist/client_monitored_item_group.d.ts +5 -5
- package/dist/client_monitored_item_toolbox.d.ts +6 -7
- package/dist/client_monitored_item_toolbox.js.map +1 -1
- package/dist/client_session.d.ts +19 -23
- package/dist/client_session.js.map +1 -1
- package/dist/client_session_keepalive_manager.d.ts +1 -1
- package/dist/client_session_keepalive_manager.js +6 -6
- package/dist/client_session_keepalive_manager.js.map +1 -1
- package/dist/client_subscription.d.ts +11 -12
- package/dist/client_subscription.js.map +1 -1
- package/dist/client_utils.d.ts +3 -3
- package/dist/client_utils.js.map +1 -1
- package/dist/common.d.ts +12 -12
- package/dist/index.d.ts +26 -27
- package/dist/index.js +65 -66
- package/dist/index.js.map +1 -1
- package/dist/opcua_client.d.ts +17 -19
- package/dist/opcua_client.js.map +1 -1
- package/dist/private/client_base_impl.d.ts +19 -18
- package/dist/private/client_base_impl.js +220 -192
- package/dist/private/client_base_impl.js.map +1 -1
- package/dist/private/client_monitored_item_group_impl.d.ts +4 -5
- package/dist/private/client_monitored_item_group_impl.js +1 -1
- package/dist/private/client_monitored_item_group_impl.js.map +1 -1
- package/dist/private/client_monitored_item_impl.d.ts +9 -10
- package/dist/private/client_monitored_item_impl.js.map +1 -1
- package/dist/private/client_publish_engine.d.ts +3 -3
- package/dist/private/client_publish_engine.js +6 -7
- package/dist/private/client_publish_engine.js.map +1 -1
- package/dist/private/client_session_impl.d.ts +24 -25
- package/dist/private/client_session_impl.js +14 -10
- package/dist/private/client_session_impl.js.map +1 -1
- package/dist/private/client_subscription_impl.d.ts +10 -11
- package/dist/private/client_subscription_impl.js +12 -11
- package/dist/private/client_subscription_impl.js.map +1 -1
- package/dist/private/i_private_client.d.ts +7 -7
- package/dist/private/opcua_client_impl.d.ts +12 -9
- package/dist/private/opcua_client_impl.js +58 -48
- package/dist/private/opcua_client_impl.js.map +1 -1
- package/dist/private/performance.js.map +1 -1
- package/dist/private/reconnection/client_publish_engine_reconnection.d.ts +1 -1
- package/dist/private/reconnection/client_publish_engine_reconnection.js +54 -43
- package/dist/private/reconnection/client_publish_engine_reconnection.js.map +1 -1
- package/dist/private/reconnection/client_reconnection.d.ts +1 -1
- package/dist/private/reconnection/client_subscription_reconnection.d.ts +1 -1
- package/dist/private/reconnection/client_subscription_reconnection.js +2 -2
- package/dist/private/reconnection/client_subscription_reconnection.js.map +1 -1
- package/dist/private/reconnection/reconnection.d.ts +4 -4
- package/dist/private/reconnection/reconnection.js +200 -281
- package/dist/private/reconnection/reconnection.js.map +1 -1
- package/dist/tools/findservers.d.ts +7 -2
- package/dist/tools/findservers.js +41 -53
- package/dist/tools/findservers.js.map +1 -1
- package/dist/tools/read_history_server_capabilities.d.ts +1 -1
- package/dist/tools/read_history_server_capabilities.js +9 -8
- package/dist/tools/read_history_server_capabilities.js.map +1 -1
- package/dist/user_identity_info.d.ts +1 -1
- package/dist/verify.js.map +1 -1
- package/package.json +45 -45
- package/source/alarms_and_conditions/client_alarm_tools.ts +14 -8
- package/source/alarms_and_conditions/client_tools.ts +7 -6
- package/source/client_base.ts +93 -79
- package/source/client_monitored_item.ts +7 -8
- package/source/client_monitored_item_base.ts +12 -14
- package/source/client_monitored_item_group.ts +5 -5
- package/source/client_monitored_item_toolbox.ts +13 -14
- package/source/client_session.ts +37 -40
- package/source/client_session_keepalive_manager.ts +7 -10
- package/source/client_subscription.ts +11 -12
- package/source/client_utils.ts +6 -6
- package/source/common.ts +12 -12
- package/source/index.ts +44 -40
- package/source/opcua_client.ts +24 -23
- package/source/private/client_base_impl.ts +329 -290
- package/source/private/client_monitored_item_group_impl.ts +10 -14
- package/source/private/client_monitored_item_impl.ts +17 -17
- package/source/private/client_publish_engine.ts +11 -12
- package/source/private/client_session_impl.ts +85 -82
- package/source/private/client_subscription_impl.ts +42 -41
- package/source/private/i_private_client.ts +8 -8
- package/source/private/opcua_client_impl.ts +118 -97
- package/source/private/performance.ts +2 -3
- package/source/private/reconnection/client_publish_engine_reconnection.ts +76 -68
- package/source/private/reconnection/client_reconnection.ts +1 -1
- package/source/private/reconnection/client_subscription_reconnection.ts +16 -13
- package/source/private/reconnection/reconnection.ts +307 -301
- package/source/tools/findservers.ts +43 -62
- package/source/tools/read_history_server_capabilities.ts +20 -22
- package/source/user_identity_info.ts +5 -5
- 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 {
|
|
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 {
|
|
15
|
-
|
|
16
|
-
import {
|
|
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? \
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
|
184
|
-
client: IClientBase,
|
|
228
|
+
async function recreateSubscriptions(
|
|
185
229
|
session: ClientSessionImpl,
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
debugLog("
|
|
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
|
-
|
|
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
|
-
|
|
276
|
+
// Transfer subscriptions - ask for initial values....
|
|
277
|
+
const subscriptionsToTransfer = new TransferSubscriptionsRequest({
|
|
278
|
+
sendInitialValues: true,
|
|
279
|
+
subscriptionIds: subscriptionsIds
|
|
280
|
+
});
|
|
203
281
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
220
|
-
|
|
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.
|
|
224
|
-
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
343
|
+
recreateSubscriptions(session, newSession, subscriptionsToRecreate)
|
|
344
|
+
.then(() => resolve())
|
|
345
|
+
.catch((e) => reject(e));
|
|
346
|
+
}
|
|
347
|
+
);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
353
350
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
-
|
|
415
|
-
}
|
|
416
|
-
);
|
|
417
|
-
},
|
|
366
|
+
const listenerCountBefore = session.listenerCount("");
|
|
418
367
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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
|
-
|
|
434
|
-
|
|
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
|
-
|
|
411
|
+
const listenerCountAfter = session.listenerCount("");
|
|
412
|
+
assert(newSession === session);
|
|
413
|
+
doDebug && debugLog("listenerCountBefore =", listenerCountBefore, "listenerCountAfter = ", listenerCountAfter);
|
|
414
|
+
}
|
|
438
415
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
(
|
|
446
|
-
|
|
447
|
-
|
|
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
|
-
{
|
|
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,
|
|
454
|
+
client._activateSession(session, session.userIdentityInfo!, (err: Error | null, _session2?: ClientSessionImpl) => {
|
|
473
455
|
// prettier-ignore
|
|
474
|
-
{
|
|
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
|
|
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 =
|
|
503
|
+
const transactionQueue = privateSession._reconnecting.pendingTransactions.splice(0);
|
|
517
504
|
|
|
518
505
|
const repeatedAction = (callback: EmptyCallback) => {
|
|
519
506
|
// prettier-ignore
|
|
520
|
-
{
|
|
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
|
-
{
|
|
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
|
-
{
|
|
538
|
-
|
|
539
|
-
|
|
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
|
|
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
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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
|
-
|
|
583
|
+
allErrors.push(err);
|
|
584
|
+
resolve();
|
|
583
585
|
});
|
|
584
|
-
}
|
|
585
|
-
|
|
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
|
-
|
|
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
|
}
|