@fluidframework/test-utils 0.49.2 → 0.50.0-41365

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.
@@ -1,431 +0,0 @@
1
- "use strict";
2
- /*!
3
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
4
- * Licensed under the MIT License.
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.OpProcessingController = void 0;
8
- const assert_1 = require("assert");
9
- const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
10
- const debug_1 = require("./debug");
11
- class DeltaManagerToggle {
12
- constructor(deltaManager) {
13
- this.deltaManager = deltaManager;
14
- }
15
- async togglePauseAll() {
16
- return Promise.all([this.togglePauseInbound(), this.togglePauseOutbound()]);
17
- }
18
- toggleResumeAll() {
19
- this.toggleResumeInbound();
20
- this.toggleResumeOutbound();
21
- }
22
- async togglePauseInbound() {
23
- if (!this.inboundPauseP) {
24
- this.inboundPauseP = this.deltaManager.inbound.pause();
25
- }
26
- return this.inboundPauseP;
27
- }
28
- async togglePauseOutbound() {
29
- if (!this.outboundPauseP) {
30
- this.outboundPauseP = this.deltaManager.outbound.pause();
31
- }
32
- return this.outboundPauseP;
33
- }
34
- toggleResumeInbound() {
35
- if (this.inboundPauseP) {
36
- this.inboundPauseP = undefined;
37
- this.deltaManager.inbound.resume();
38
- }
39
- }
40
- toggleResumeOutbound() {
41
- if (this.outboundPauseP) {
42
- this.outboundPauseP = undefined;
43
- this.deltaManager.outbound.resume();
44
- }
45
- }
46
- get inboundPaused() {
47
- return this.inboundPauseP !== undefined;
48
- }
49
- }
50
- /**
51
- * Monitor for DeltaManager, and track in/out ops to figure out whether there are
52
- * outstanding ops that the server hasn't ack yet. Used by the OpProcessingController
53
- * to wait for all the ops has round tripped.
54
- *
55
- * For outbound, we monitor ops leaving the outbound queue on the "op" event.
56
- * For inbound, we monitor the first moment we see an op coming back on the "push" event.
57
- *
58
- * It also monitor connect and disconnect state so that we can refresh the tracking and clientId
59
- *
60
- * The monitor ignores ops generated by the server. It also don't track NoOp since the server
61
- * might coalesce them with other ops, or a single NoOp, or delay it if it don't think it is necessary
62
- */
63
- class DeltaManagerMonitor extends DeltaManagerToggle {
64
- constructor(deltaManager) {
65
- var _a, _b;
66
- super(deltaManager);
67
- this.pendingCount = 0;
68
- this.readMode = true;
69
- this.firstClientSequenceNumber = -1;
70
- this.pendingLeaveClientIds = new Set();
71
- this.lastInboundPerClient = new Map();
72
- this.pendingWriteConnection = false;
73
- // The deltaManager may be connected already, need to get the clientId.
74
- // TODO: hackery to get the clientId from the delta manager, find a better way
75
- const anyDeltaManager = deltaManager;
76
- // Unwrap the proxy if there is any
77
- const fullDeltaManager = ((_a = anyDeltaManager.deltaManager) !== null && _a !== void 0 ? _a : anyDeltaManager);
78
- const id = (_b = fullDeltaManager.connection) === null || _b === void 0 ? void 0 : _b.clientId;
79
- if (id !== undefined) {
80
- this.connect(id);
81
- }
82
- deltaManager.on("connect", (details) => this.connect(details.clientId));
83
- deltaManager.on("disconnect", (reason) => {
84
- assert_1.strict(this.clientId !== undefined);
85
- this.trace("DIS");
86
- this.clientId = undefined;
87
- // Once disconnected, the runtime is going to keep track of ops and replay as necessary
88
- // Clear the pending count and start anew
89
- this.pendingCount = 0;
90
- this.firstClientSequenceNumber = -1;
91
- this.lastOutbound = undefined;
92
- });
93
- deltaManager.outbound.on("op", this.outbound.bind(this));
94
- deltaManager.inbound.on("push", this.inbound.bind(this));
95
- }
96
- /**
97
- * Determines if this monitor should expect work/ops from the outbound monitor.
98
- * @param outbound - the monitor who's outbound to consider
99
- */
100
- expectingInboundFrom(outbound) {
101
- // there should be no outstanding work for disposed delta managers
102
- if (this.deltaManager.disposed || outbound.deltaManager.disposed) {
103
- return false;
104
- }
105
- // if there is no last outbound, we are not waiting for anything
106
- if (outbound.lastOutbound === undefined
107
- || outbound.clientId === undefined) {
108
- return false;
109
- }
110
- // if out inbound is paused we are not expecting to receive anything more
111
- if (this.inboundPaused) {
112
- return false;
113
- }
114
- // if outbound is ourself, return if we having pending work
115
- if (this === outbound) {
116
- return this.hasPendingWork();
117
- }
118
- // check if we are waiting to see a message from outbound
119
- const lastInboundForOutbound = this.lastInboundPerClient.get(outbound.clientId);
120
- if (lastInboundForOutbound !== undefined) {
121
- return outbound.lastOutbound.clientSequenceNumber > lastInboundForOutbound.clientSequenceNumber;
122
- }
123
- // has pending work will be true for outbound until it receives it's own seq
124
- // this check ensures the other client has seen the same ops as the outbound
125
- return outbound.latestSequenceNumber > this.latestSequenceNumber;
126
- }
127
- get latestSequenceNumber() {
128
- return this.deltaManager.lastSequenceNumber;
129
- }
130
- hasPendingWork() {
131
- return !this.deltaManager.disposed
132
- && (this.pendingWriteConnection || this.pendingCount !== 0 || this.pendingLeaveClientIds.size !== 0);
133
- }
134
- connect(clientId) {
135
- this.clientId = clientId;
136
- this.readMode = !this.deltaManager.active;
137
- this.trace("CON");
138
- }
139
- inbound(message) {
140
- if (message.clientId) {
141
- this.lastInboundPerClient.set(message.clientId, message);
142
- }
143
- if (message.type === protocol_definitions_1.MessageType.ClientLeave) {
144
- const systemLeaveMessage = message;
145
- const clientId = JSON.parse(systemLeaveMessage.data);
146
- this.lastInboundPerClient.delete(clientId);
147
- this.pendingLeaveClientIds.delete(clientId);
148
- }
149
- if (this.clientId === undefined) {
150
- // Ignore message when we are not connected.
151
- return;
152
- }
153
- if (message.clientId === undefined || message.clientId !== this.clientId) {
154
- this.trace("SEQ", message.type);
155
- return;
156
- }
157
- if (this.firstClientSequenceNumber === -1 || this.firstClientSequenceNumber > message.clientSequenceNumber) {
158
- this.trace("SEQ", message.type);
159
- // if we haven't seen any outbound or the message is before the outbound message that we have seen,
160
- // then message is sent before we start monitoring, ignore.
161
- return;
162
- }
163
- // Need to filter system messages
164
- switch (message.type) {
165
- case protocol_definitions_1.MessageType.ClientJoin:
166
- case protocol_definitions_1.MessageType.ClientLeave:
167
- assert_1.strict(false, "join and leave message shouldn't have clientId");
168
- // These are generated by the server, don't count
169
- case protocol_definitions_1.MessageType.NoOp:
170
- case protocol_definitions_1.MessageType.NoClient:
171
- this.trace("SEQ", message.type);
172
- break;
173
- default:
174
- assert_1.strict(this.pendingCount);
175
- this.pendingCount--;
176
- this.trace("IN", message.type);
177
- }
178
- }
179
- outbound(messages) {
180
- assert_1.strict(this.clientId);
181
- assert_1.strict(messages.length);
182
- if (this.firstClientSequenceNumber === -1) {
183
- // save the client sequence number of the first outbound message we see
184
- // to exclude any message that was sent before we start monitoring the delta manager
185
- this.firstClientSequenceNumber = messages[0].clientSequenceNumber;
186
- }
187
- // if we are not active, the outbound with nack, and we will reconnect write
188
- // this flag tracks the process. after reconnection, the op will be resubmitted
189
- // on the write connection and reset this flag
190
- this.pendingWriteConnection = !this.deltaManager.active;
191
- for (const message of messages) {
192
- // No-op's are not directly broadcast
193
- // the server coaleses and send it's own
194
- // no-op if no user messages arrive
195
- // to bump min seq
196
- if (message.type !== protocol_definitions_1.MessageType.NoOp) {
197
- this.pendingCount++;
198
- this.lastOutbound = message;
199
- }
200
- this.trace("OUT", message.type);
201
- }
202
- }
203
- trace(action, op) {
204
- debug_1.debug(`DeltaConnectionMonitor: ${action.padEnd(3)}: ${this.clientId} `
205
- + `pending:${this.pendingCount} seq:${this.latestSequenceNumber} ${op !== null && op !== void 0 ? op : ""}`);
206
- }
207
- onClientDisconnect(clientId) {
208
- // Keep track of a list of clientIds that we expect leave message from
209
- this.pendingLeaveClientIds.add(clientId);
210
- }
211
- }
212
- /**
213
- * Class with access to the local delta connection server and delta managers that can control op processing.
214
- *
215
- * @deprecated Can be removed \>=0.38. Replaced with LoaderContainerTracker
216
- */
217
- class OpProcessingController {
218
- /**
219
- * @param deltaConnectionServerMonitor - delta connection server monitor to tell whether we have
220
- * pending work
221
- */
222
- constructor(deltaConnectionServerMonitor) {
223
- this.deltaConnectionServerMonitor = deltaConnectionServerMonitor;
224
- this.deltaManagerMonitors = new Map();
225
- this.isNormalProcessingPaused = false;
226
- }
227
- /**
228
- * Yields control in the JavaScript event loop.
229
- */
230
- static async yield() {
231
- await new Promise((resolve) => {
232
- setTimeout(resolve, 0);
233
- });
234
- }
235
- /*
236
- * Is processing being deterministically controlled, or are changes allowed to flow freely?
237
- */
238
- get isProcessingControlled() {
239
- return this.isNormalProcessingPaused;
240
- }
241
- /**
242
- * Add a collection of delta managers by adding them to the local collection.
243
- * @param deltaManagers - Array of deltaManagers to add
244
- */
245
- addDeltaManagers(...deltaManagers) {
246
- deltaManagers.forEach((deltaManager) => {
247
- const monitorSetup = (monitor1, monitor2) => {
248
- if (monitor1.clientId !== undefined && monitor1.deltaManager.active) {
249
- const clientId = monitor1.clientId;
250
- monitor1.deltaManager.once("disconnect", () => {
251
- monitor2.onClientDisconnect(clientId);
252
- });
253
- }
254
- monitor1.deltaManager.on("connect", (details) => {
255
- if (monitor1.deltaManager.active) {
256
- monitor1.deltaManager.once("disconnect", () => {
257
- monitor2.onClientDisconnect(details.clientId);
258
- });
259
- }
260
- });
261
- };
262
- // Wire up event listener so we can keep track of leave message that we expects
263
- const newMonitor = new DeltaManagerMonitor(deltaManager);
264
- for (const monitor of this.deltaManagerMonitors.values()) {
265
- monitorSetup(newMonitor, monitor);
266
- monitorSetup(monitor, newMonitor);
267
- }
268
- this.deltaManagerMonitors.set(deltaManager, newMonitor);
269
- });
270
- }
271
- /**
272
- * Processes incoming and outgoing op) of the given delta managers.
273
- * It validates the delta managers and resumes its inbound and outbound queues. It then keeps yielding
274
- * the JS event loop until all the ops have been processed by the server and by the delta managers.
275
- *
276
- * @param deltaMangers - Array of delta managers whose ops to process. If no delta manager is provided, it
277
- * processes the ops for all the delta managers in our collection.
278
- */
279
- async process(...deltaMangers) {
280
- const monitors = this.mapDeltaManagerMonitor(deltaMangers);
281
- // Pause the queues of all the delta managers in our collection to make sure that we only process the ops of
282
- // the requested delta managers.
283
- await this.pauseAllDeltaManagerQueues();
284
- // Resume the delta queues so that we can process incoming and outgoing ops.
285
- monitors.forEach((monitor) => monitor.toggleResumeAll());
286
- // Wait for all pending ops to be processed.
287
- await this.yieldWhileDeltaManagersHaveWork(monitors, (deltaManager) => !deltaManager.inbound.idle || !deltaManager.outbound.idle);
288
- }
289
- /**
290
- * Processes incoming ops of the given delta managers.
291
- * It validates the delta managers and resumes its inbound queue. It then keeps yielding the JS event loop until
292
- * all the ops have been processed by the server and by the delta managers.
293
- *
294
- * @param deltaMangers - Array of delta managers whose incoming ops to process. If no delta manager is provided, it
295
- * processes the ops for all the delta managers in our collection.
296
- */
297
- async processIncoming(...deltaMangers) {
298
- const monitors = this.mapDeltaManagerMonitor(deltaMangers);
299
- // Pause the queues of all the delta managers in our collection to make sure that we only process the incoming
300
- // ops of the requested delta managers.
301
- await this.pauseAllDeltaManagerQueues();
302
- // Resume the inbound delta queue so that we can process incoming ops.
303
- monitors.forEach((monitor) => {
304
- monitor.toggleResumeInbound();
305
- });
306
- // Wait for all pending incoming ops to be processed.
307
- await this.yieldWhileDeltaManagersHaveWork(monitors, (deltaManager) => !deltaManager.inbound.idle);
308
- }
309
- /**
310
- * Processes outgoing ops of the given delta managers.
311
- * It validates the delta managers and resumes its outbound queue. It then keeps yielding the JS event loop until
312
- * all the ops have been processed by the server and by the delta managers.
313
- *
314
- * @param deltaMangers - Array of delta managers whose outgoing ops to process. If no delta manager is provided, it
315
- * processes the ops for all the delta managers in our collection.
316
- */
317
- async processOutgoing(...deltaMangers) {
318
- const monitors = this.mapDeltaManagerMonitor(deltaMangers);
319
- // Pause the queues of all the delta managers in our collection to make sure that we only process the outgoing
320
- // ops of the requested delta managers.
321
- await this.pauseAllDeltaManagerQueues();
322
- // Resume the outbound delta queue so that we can process outgoing ops.
323
- monitors.forEach((monitor) => {
324
- monitor.toggleResumeOutbound();
325
- });
326
- // Wait for all pending outgoing ops to be processed.
327
- await this.yieldWhileDeltaManagersHaveWork(monitors, (deltaManager) => !deltaManager.outbound.idle);
328
- }
329
- /**
330
- * Pauses the delta processing for controlled testing by pausing the inbound and outbound queues of the delta
331
- * managers.
332
- *
333
- * @param deltaMangers - Array of delta managers whose processing to pause. If no delta manager is provided, it
334
- * pauses the processing of all the delta managers in our collection.
335
- */
336
- async pauseProcessing(...deltaMangers) {
337
- const monitors = this.mapDeltaManagerMonitor(deltaMangers);
338
- // Pause the inbound and outbound delta queues.
339
- await this.pauseDeltaManagerQueues(monitors);
340
- this.isNormalProcessingPaused = true;
341
- }
342
- /**
343
- * Resumes the delta processing after a pauseProcessing calls by resuming the inbound and outbound queues of
344
- * the delta managers.
345
- *
346
- * @param deltaMangers - Array of delta managers whose processing to resume. If no delta manager is provided, it
347
- * resumes the processing of all the delta managers in our collection.
348
- */
349
- resumeProcessing(...deltaMangers) {
350
- const monitors = this.mapDeltaManagerMonitor(deltaMangers);
351
- // Resume the inbound and outbound delta queues.
352
- monitors.forEach((monitor) => monitor.toggleResumeAll());
353
- this.isNormalProcessingPaused = false;
354
- }
355
- /**
356
- * Map a list of DeltaManager to its monitor. Throw an error if the delta manager is not in our collection
357
- * @param deltaMangers - The delta managers to get the monitors for
358
- */
359
- mapDeltaManagerMonitor(deltaMangers) {
360
- if (deltaMangers.length === 0) {
361
- // If no delta managers are provided, process all delta managers in our collection.
362
- return Array.from(this.deltaManagerMonitors.values());
363
- }
364
- return deltaMangers.map((deltaManager) => {
365
- const monitor = this.deltaManagerMonitors.get(deltaManager);
366
- assert_1.strict(monitor, "All delta managers must be added to deterministically control processing");
367
- return monitor;
368
- });
369
- }
370
- /**
371
- * It keeps yielding the JS event loop until all the ops have been processed by the server and by the passed
372
- * delta managers.
373
- * @param monitors - The delta managers should ops have to be processed.
374
- * @param hasWork - Function that tells if the delta manager has pending work or not.
375
- */
376
- async yieldWhileDeltaManagersHaveWork(monitors, hasWork) {
377
- var _a;
378
- let working;
379
- do {
380
- await OpProcessingController.yield();
381
- working = false;
382
- if (await ((_a = this.deltaConnectionServerMonitor) === null || _a === void 0 ? void 0 : _a.hasPendingWork()) === true) {
383
- working = true;
384
- }
385
- else {
386
- for (const monitor of monitors) {
387
- if (!monitor.deltaManager.disposed) {
388
- if (monitor.hasPendingWork() || hasWork(monitor.deltaManager)) {
389
- working = true;
390
- break;
391
- }
392
- for (const outBoundMonitor of monitors) {
393
- if (monitor !== outBoundMonitor) {
394
- if (monitor.expectingInboundFrom(outBoundMonitor)) {
395
- working = true;
396
- break;
397
- }
398
- }
399
- }
400
- if (working === true) {
401
- break;
402
- }
403
- }
404
- }
405
- }
406
- } while (working);
407
- // If deterministically controlling events, need to pause before continuing
408
- if (this.isNormalProcessingPaused) {
409
- await this.pauseDeltaManagerQueues(monitors);
410
- }
411
- }
412
- /**
413
- * Pauses the inbound and outbound queues of all the delta managers given
414
- * @param monitors - The delta managers should ops have to be processed.
415
- */
416
- async pauseDeltaManagerQueues(monitors) {
417
- const p = [];
418
- for (const monitor of monitors) {
419
- p.push(monitor.togglePauseAll());
420
- }
421
- return Promise.all(p);
422
- }
423
- /**
424
- * Pauses the inbound and outbound queues of all the delta managers in our collection.
425
- */
426
- async pauseAllDeltaManagerQueues() {
427
- return this.pauseDeltaManagerQueues(this.deltaManagerMonitors.values());
428
- }
429
- }
430
- exports.OpProcessingController = OpProcessingController;
431
- //# sourceMappingURL=opProcessingController.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"opProcessingController.js","sourceRoot":"","sources":["../src/opProcessingController.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"assert\";\nimport { IDeltaManager } from \"@fluidframework/container-definitions\";\nimport {\n IDocumentMessage,\n ISequencedDocumentMessage,\n ISequencedDocumentSystemMessage,\n MessageType,\n} from \"@fluidframework/protocol-definitions\";\nimport { debug } from \"./debug\";\n\n// An IDeltaManager alias to be used within this class.\nexport type DeltaManager = IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>;\n\nclass DeltaManagerToggle {\n private inboundPauseP: Promise<void> | undefined;\n private outboundPauseP: Promise<void> | undefined;\n constructor(public readonly deltaManager: DeltaManager) {\n }\n\n public async togglePauseAll() {\n return Promise.all([this.togglePauseInbound(), this.togglePauseOutbound()]);\n }\n\n public toggleResumeAll() {\n this.toggleResumeInbound();\n this.toggleResumeOutbound();\n }\n public async togglePauseInbound() {\n if (!this.inboundPauseP) {\n this.inboundPauseP = this.deltaManager.inbound.pause();\n }\n return this.inboundPauseP;\n }\n\n public async togglePauseOutbound() {\n if (!this.outboundPauseP) {\n this.outboundPauseP = this.deltaManager.outbound.pause();\n }\n return this.outboundPauseP;\n }\n\n public toggleResumeInbound() {\n if (this.inboundPauseP) {\n this.inboundPauseP = undefined;\n this.deltaManager.inbound.resume();\n }\n }\n\n public toggleResumeOutbound() {\n if (this.outboundPauseP) {\n this.outboundPauseP = undefined;\n this.deltaManager.outbound.resume();\n }\n }\n\n public get inboundPaused() {\n return this.inboundPauseP !== undefined;\n }\n}\n\n/**\n * Monitor for DeltaManager, and track in/out ops to figure out whether there are\n * outstanding ops that the server hasn't ack yet. Used by the OpProcessingController\n * to wait for all the ops has round tripped.\n *\n * For outbound, we monitor ops leaving the outbound queue on the \"op\" event.\n * For inbound, we monitor the first moment we see an op coming back on the \"push\" event.\n *\n * It also monitor connect and disconnect state so that we can refresh the tracking and clientId\n *\n * The monitor ignores ops generated by the server. It also don't track NoOp since the server\n * might coalesce them with other ops, or a single NoOp, or delay it if it don't think it is necessary\n */\nclass DeltaManagerMonitor extends DeltaManagerToggle {\n private pendingCount: number = 0;\n public clientId: string | undefined;\n public readMode = true;\n private firstClientSequenceNumber: number = -1;\n private lastOutbound: IDocumentMessage | undefined;\n private readonly pendingLeaveClientIds = new Set<string>();\n private readonly lastInboundPerClient = new Map<string, ISequencedDocumentMessage>();\n private pendingWriteConnection = false;\n\n /**\n * Determines if this monitor should expect work/ops from the outbound monitor.\n * @param outbound - the monitor who's outbound to consider\n */\n public expectingInboundFrom(outbound: DeltaManagerMonitor): boolean {\n // there should be no outstanding work for disposed delta managers\n if (this.deltaManager.disposed || outbound.deltaManager.disposed) {\n return false;\n }\n // if there is no last outbound, we are not waiting for anything\n if (outbound.lastOutbound === undefined\n || outbound.clientId === undefined) {\n return false;\n }\n // if out inbound is paused we are not expecting to receive anything more\n if (this.inboundPaused) {\n return false;\n }\n\n // if outbound is ourself, return if we having pending work\n if (this === outbound) {\n return this.hasPendingWork();\n }\n\n // check if we are waiting to see a message from outbound\n const lastInboundForOutbound = this.lastInboundPerClient.get(outbound.clientId);\n if (lastInboundForOutbound !== undefined) {\n return outbound.lastOutbound.clientSequenceNumber > lastInboundForOutbound.clientSequenceNumber;\n }\n\n // has pending work will be true for outbound until it receives it's own seq\n // this check ensures the other client has seen the same ops as the outbound\n return outbound.latestSequenceNumber > this.latestSequenceNumber;\n }\n\n constructor(deltaManager: DeltaManager) {\n super(deltaManager);\n\n // The deltaManager may be connected already, need to get the clientId.\n // TODO: hackery to get the clientId from the delta manager, find a better way\n const anyDeltaManager = deltaManager as any;\n // Unwrap the proxy if there is any\n const fullDeltaManager = (anyDeltaManager.deltaManager ?? anyDeltaManager);\n const id = fullDeltaManager.connection?.clientId;\n if (id !== undefined) {\n this.connect(id);\n }\n\n deltaManager.on(\"connect\", (details) => this.connect(details.clientId));\n deltaManager.on(\"disconnect\", (reason) => {\n assert(this.clientId !== undefined);\n this.trace(\"DIS\");\n this.clientId = undefined;\n // Once disconnected, the runtime is going to keep track of ops and replay as necessary\n // Clear the pending count and start anew\n this.pendingCount = 0;\n this.firstClientSequenceNumber = -1;\n this.lastOutbound = undefined;\n });\n deltaManager.outbound.on(\"op\", this.outbound.bind(this));\n deltaManager.inbound.on(\"push\", this.inbound.bind(this));\n }\n\n public get latestSequenceNumber() {\n return this.deltaManager.lastSequenceNumber;\n }\n\n public hasPendingWork() {\n return !this.deltaManager.disposed\n && (this.pendingWriteConnection || this.pendingCount !== 0 || this.pendingLeaveClientIds.size !== 0);\n }\n\n private connect(clientId: string) {\n this.clientId = clientId;\n this.readMode = !this.deltaManager.active;\n this.trace(\"CON\");\n }\n private inbound(message: ISequencedDocumentMessage) {\n if (message.clientId) {\n this.lastInboundPerClient.set(message.clientId, message);\n }\n if (message.type === MessageType.ClientLeave) {\n const systemLeaveMessage = message as ISequencedDocumentSystemMessage;\n const clientId = JSON.parse(systemLeaveMessage.data) as string;\n this.lastInboundPerClient.delete(clientId);\n this.pendingLeaveClientIds.delete(clientId);\n }\n\n if (this.clientId === undefined) {\n // Ignore message when we are not connected.\n return;\n }\n\n if (message.clientId === undefined || message.clientId !== this.clientId) {\n this.trace(\"SEQ\", message.type);\n return;\n }\n\n if (this.firstClientSequenceNumber === -1 || this.firstClientSequenceNumber > message.clientSequenceNumber) {\n this.trace(\"SEQ\", message.type);\n // if we haven't seen any outbound or the message is before the outbound message that we have seen,\n // then message is sent before we start monitoring, ignore.\n return;\n }\n\n // Need to filter system messages\n switch (message.type) {\n case MessageType.ClientJoin:\n case MessageType.ClientLeave:\n assert(false, \"join and leave message shouldn't have clientId\");\n // These are generated by the server, don't count\n case MessageType.NoOp:\n case MessageType.NoClient:\n this.trace(\"SEQ\", message.type);\n break;\n default:\n assert(this.pendingCount);\n this.pendingCount--;\n this.trace(\"IN\", message.type);\n }\n }\n\n private outbound(messages: IDocumentMessage[]) {\n assert(this.clientId);\n assert(messages.length);\n if (this.firstClientSequenceNumber === -1) {\n // save the client sequence number of the first outbound message we see\n // to exclude any message that was sent before we start monitoring the delta manager\n this.firstClientSequenceNumber = messages[0].clientSequenceNumber;\n }\n // if we are not active, the outbound with nack, and we will reconnect write\n // this flag tracks the process. after reconnection, the op will be resubmitted\n // on the write connection and reset this flag\n this.pendingWriteConnection = !this.deltaManager.active;\n for (const message of messages) {\n // No-op's are not directly broadcast\n // the server coaleses and send it's own\n // no-op if no user messages arrive\n // to bump min seq\n if (message.type !== MessageType.NoOp) {\n this.pendingCount++;\n this.lastOutbound = message;\n }\n this.trace(\"OUT\", message.type);\n }\n }\n\n public trace(action: string, op?: string) {\n debug(`DeltaConnectionMonitor: ${action.padEnd(3)}: ${this.clientId} `\n + `pending:${this.pendingCount} seq:${this.latestSequenceNumber} ${op ?? \"\"}`);\n }\n\n public onClientDisconnect(clientId: string) {\n // Keep track of a list of clientIds that we expect leave message from\n this.pendingLeaveClientIds.add(clientId);\n }\n}\n/**\n * @deprecated OpProcessingController has been improved to not need server information and work against other servers.\n * So this is no longer necessary, and allows this and test to be run against different endpoints.\n */\nexport interface IDeltaConnectionServerMonitor {\n hasPendingWork(): Promise<boolean>;\n}\n\n/**\n * Class with access to the local delta connection server and delta managers that can control op processing.\n *\n * @deprecated Can be removed \\>=0.38. Replaced with LoaderContainerTracker\n */\nexport class OpProcessingController {\n /**\n * Yields control in the JavaScript event loop.\n */\n public static async yield(): Promise<void> {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 0);\n });\n }\n\n private readonly deltaManagerMonitors = new Map<DeltaManager, DeltaManagerMonitor>();\n\n private isNormalProcessingPaused = false;\n\n /*\n * Is processing being deterministically controlled, or are changes allowed to flow freely?\n */\n public get isProcessingControlled(): boolean {\n return this.isNormalProcessingPaused;\n }\n\n /**\n * @param deltaConnectionServerMonitor - delta connection server monitor to tell whether we have\n * pending work\n */\n public constructor(private readonly deltaConnectionServerMonitor?: IDeltaConnectionServerMonitor) { }\n\n /**\n * Add a collection of delta managers by adding them to the local collection.\n * @param deltaManagers - Array of deltaManagers to add\n */\n public addDeltaManagers(...deltaManagers: DeltaManager[]) {\n deltaManagers.forEach((deltaManager) => {\n const monitorSetup = (monitor1: DeltaManagerMonitor, monitor2: DeltaManagerMonitor) => {\n if (monitor1.clientId !== undefined && monitor1.deltaManager.active) {\n const clientId = monitor1.clientId;\n monitor1.deltaManager.once(\"disconnect\", () => {\n monitor2.onClientDisconnect(clientId);\n });\n }\n monitor1.deltaManager.on(\"connect\", (details) => {\n if (monitor1.deltaManager.active) {\n monitor1.deltaManager.once(\"disconnect\", () => {\n monitor2.onClientDisconnect(details.clientId);\n });\n }\n });\n };\n\n // Wire up event listener so we can keep track of leave message that we expects\n const newMonitor = new DeltaManagerMonitor(deltaManager);\n for (const monitor of this.deltaManagerMonitors.values()) {\n monitorSetup(newMonitor, monitor);\n monitorSetup(monitor, newMonitor);\n }\n\n this.deltaManagerMonitors.set(deltaManager, newMonitor);\n });\n }\n\n /**\n * Processes incoming and outgoing op) of the given delta managers.\n * It validates the delta managers and resumes its inbound and outbound queues. It then keeps yielding\n * the JS event loop until all the ops have been processed by the server and by the delta managers.\n *\n * @param deltaMangers - Array of delta managers whose ops to process. If no delta manager is provided, it\n * processes the ops for all the delta managers in our collection.\n */\n public async process(...deltaMangers: DeltaManager[]): Promise<void> {\n const monitors = this.mapDeltaManagerMonitor(deltaMangers);\n\n // Pause the queues of all the delta managers in our collection to make sure that we only process the ops of\n // the requested delta managers.\n await this.pauseAllDeltaManagerQueues();\n\n // Resume the delta queues so that we can process incoming and outgoing ops.\n monitors.forEach((monitor) => monitor.toggleResumeAll());\n\n // Wait for all pending ops to be processed.\n await this.yieldWhileDeltaManagersHaveWork(\n monitors,\n (deltaManager) => !deltaManager.inbound.idle || !deltaManager.outbound.idle);\n }\n\n /**\n * Processes incoming ops of the given delta managers.\n * It validates the delta managers and resumes its inbound queue. It then keeps yielding the JS event loop until\n * all the ops have been processed by the server and by the delta managers.\n *\n * @param deltaMangers - Array of delta managers whose incoming ops to process. If no delta manager is provided, it\n * processes the ops for all the delta managers in our collection.\n */\n public async processIncoming(...deltaMangers: DeltaManager[]): Promise<void> {\n const monitors = this.mapDeltaManagerMonitor(deltaMangers);\n\n // Pause the queues of all the delta managers in our collection to make sure that we only process the incoming\n // ops of the requested delta managers.\n await this.pauseAllDeltaManagerQueues();\n\n // Resume the inbound delta queue so that we can process incoming ops.\n monitors.forEach((monitor) => {\n monitor.toggleResumeInbound();\n });\n\n // Wait for all pending incoming ops to be processed.\n await this.yieldWhileDeltaManagersHaveWork(\n monitors,\n (deltaManager) => !deltaManager.inbound.idle);\n }\n\n /**\n * Processes outgoing ops of the given delta managers.\n * It validates the delta managers and resumes its outbound queue. It then keeps yielding the JS event loop until\n * all the ops have been processed by the server and by the delta managers.\n *\n * @param deltaMangers - Array of delta managers whose outgoing ops to process. If no delta manager is provided, it\n * processes the ops for all the delta managers in our collection.\n */\n public async processOutgoing(...deltaMangers: DeltaManager[]): Promise<void> {\n const monitors = this.mapDeltaManagerMonitor(deltaMangers);\n\n // Pause the queues of all the delta managers in our collection to make sure that we only process the outgoing\n // ops of the requested delta managers.\n await this.pauseAllDeltaManagerQueues();\n\n // Resume the outbound delta queue so that we can process outgoing ops.\n monitors.forEach((monitor) => {\n monitor.toggleResumeOutbound();\n });\n\n // Wait for all pending outgoing ops to be processed.\n await this.yieldWhileDeltaManagersHaveWork(\n monitors,\n (deltaManager) => !deltaManager.outbound.idle);\n }\n\n /**\n * Pauses the delta processing for controlled testing by pausing the inbound and outbound queues of the delta\n * managers.\n *\n * @param deltaMangers - Array of delta managers whose processing to pause. If no delta manager is provided, it\n * pauses the processing of all the delta managers in our collection.\n */\n public async pauseProcessing(...deltaMangers: DeltaManager[]) {\n const monitors = this.mapDeltaManagerMonitor(deltaMangers);\n\n // Pause the inbound and outbound delta queues.\n await this.pauseDeltaManagerQueues(monitors);\n\n this.isNormalProcessingPaused = true;\n }\n\n /**\n * Resumes the delta processing after a pauseProcessing calls by resuming the inbound and outbound queues of\n * the delta managers.\n *\n * @param deltaMangers - Array of delta managers whose processing to resume. If no delta manager is provided, it\n * resumes the processing of all the delta managers in our collection.\n */\n public resumeProcessing(...deltaMangers: DeltaManager[]) {\n const monitors = this.mapDeltaManagerMonitor(deltaMangers);\n\n // Resume the inbound and outbound delta queues.\n monitors.forEach((monitor) => monitor.toggleResumeAll());\n\n this.isNormalProcessingPaused = false;\n }\n\n /**\n * Map a list of DeltaManager to its monitor. Throw an error if the delta manager is not in our collection\n * @param deltaMangers - The delta managers to get the monitors for\n */\n private mapDeltaManagerMonitor(deltaMangers: DeltaManager[]) {\n if (deltaMangers.length === 0) {\n // If no delta managers are provided, process all delta managers in our collection.\n return Array.from(this.deltaManagerMonitors.values());\n }\n\n return deltaMangers.map((deltaManager) => {\n const monitor = this.deltaManagerMonitors.get(deltaManager);\n assert(monitor, \"All delta managers must be added to deterministically control processing\");\n return monitor;\n });\n }\n\n /**\n * It keeps yielding the JS event loop until all the ops have been processed by the server and by the passed\n * delta managers.\n * @param monitors - The delta managers should ops have to be processed.\n * @param hasWork - Function that tells if the delta manager has pending work or not.\n */\n private async yieldWhileDeltaManagersHaveWork(\n monitors: Iterable<DeltaManagerMonitor>,\n hasWork: (deltaManagers: DeltaManager) => boolean,\n ): Promise<void> {\n let working: boolean;\n do {\n await OpProcessingController.yield();\n working = false;\n if (await this.deltaConnectionServerMonitor?.hasPendingWork() === true) {\n working = true;\n } else {\n for (const monitor of monitors) {\n if (!monitor.deltaManager.disposed) {\n if (monitor.hasPendingWork() || hasWork(monitor.deltaManager)) {\n working = true;\n break;\n }\n\n for (const outBoundMonitor of monitors) {\n if (monitor !== outBoundMonitor) {\n if (monitor.expectingInboundFrom(outBoundMonitor)) {\n working = true;\n break;\n }\n }\n }\n if (working === true) {\n break;\n }\n }\n }\n }\n } while (working);\n\n // If deterministically controlling events, need to pause before continuing\n if (this.isNormalProcessingPaused) {\n await this.pauseDeltaManagerQueues(monitors);\n }\n }\n\n /**\n * Pauses the inbound and outbound queues of all the delta managers given\n * @param monitors - The delta managers should ops have to be processed.\n */\n private async pauseDeltaManagerQueues(monitors: Iterable<DeltaManagerToggle>) {\n const p: Promise<[void, void]>[] = [];\n for (const monitor of monitors) {\n p.push(monitor.togglePauseAll());\n }\n return Promise.all(p);\n }\n\n /**\n * Pauses the inbound and outbound queues of all the delta managers in our collection.\n */\n private async pauseAllDeltaManagerQueues() {\n return this.pauseDeltaManagerQueues(this.deltaManagerMonitors.values());\n }\n}\n"]}