@fluidframework/container-loader 2.0.0-internal.1.0.0.83139 → 2.0.0-internal.1.1.1
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/.mocharc.js +12 -0
- package/dist/catchUpMonitor.js +1 -1
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/collabWindowTracker.d.ts.map +1 -1
- package/dist/collabWindowTracker.js +10 -3
- package/dist/collabWindowTracker.js.map +1 -1
- package/dist/connectionManager.d.ts +4 -4
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +5 -6
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.js +1 -1
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +2 -1
- package/dist/container.js.map +1 -1
- package/dist/contracts.d.ts +5 -5
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +3 -0
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +15 -0
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.js +3 -3
- package/dist/deltaQueue.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/catchUpMonitor.js +1 -1
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/collabWindowTracker.d.ts.map +1 -1
- package/lib/collabWindowTracker.js +10 -3
- package/lib/collabWindowTracker.js.map +1 -1
- package/lib/connectionManager.d.ts +4 -4
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +5 -6
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.js +1 -1
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +2 -1
- package/lib/container.js.map +1 -1
- package/lib/contracts.d.ts +5 -5
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +3 -0
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +17 -2
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.js +3 -3
- package/lib/deltaQueue.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +14 -14
- package/src/catchUpMonitor.ts +1 -1
- package/src/collabWindowTracker.ts +11 -3
- package/src/connectionManager.ts +14 -15
- package/src/connectionStateHandler.ts +1 -1
- package/src/container.ts +4 -2
- package/src/contracts.ts +7 -7
- package/src/deltaManager.ts +22 -4
- package/src/deltaQueue.ts +3 -3
- package/src/packageVersion.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-loader",
|
|
3
|
-
"version": "2.0.0-internal.1.
|
|
3
|
+
"version": "2.0.0-internal.1.1.1",
|
|
4
4
|
"description": "Fluid container loader",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -62,15 +62,15 @@
|
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
65
|
-
"@fluidframework/common-utils": "^0.
|
|
66
|
-
"@fluidframework/container-definitions": "2.0.0-internal.1.
|
|
67
|
-
"@fluidframework/container-utils": "2.0.0-internal.1.
|
|
68
|
-
"@fluidframework/core-interfaces": "2.0.0-internal.1.
|
|
69
|
-
"@fluidframework/driver-definitions": "2.0.0-internal.1.
|
|
70
|
-
"@fluidframework/driver-utils": "2.0.0-internal.1.
|
|
71
|
-
"@fluidframework/protocol-base": "^0.1037.1000
|
|
72
|
-
"@fluidframework/protocol-definitions": "^0.
|
|
73
|
-
"@fluidframework/telemetry-utils": "2.0.0-internal.1.
|
|
65
|
+
"@fluidframework/common-utils": "^1.0.0",
|
|
66
|
+
"@fluidframework/container-definitions": "^2.0.0-internal.1.1.1",
|
|
67
|
+
"@fluidframework/container-utils": "^2.0.0-internal.1.1.1",
|
|
68
|
+
"@fluidframework/core-interfaces": "^2.0.0-internal.1.1.1",
|
|
69
|
+
"@fluidframework/driver-definitions": "^2.0.0-internal.1.1.1",
|
|
70
|
+
"@fluidframework/driver-utils": "^2.0.0-internal.1.1.1",
|
|
71
|
+
"@fluidframework/protocol-base": "^0.1037.1000",
|
|
72
|
+
"@fluidframework/protocol-definitions": "^1.0.0",
|
|
73
|
+
"@fluidframework/telemetry-utils": "^2.0.0-internal.1.1.1",
|
|
74
74
|
"abort-controller": "^3.0.0",
|
|
75
75
|
"double-ended-queue": "^2.1.0-0",
|
|
76
76
|
"lodash": "^4.17.21",
|
|
@@ -79,11 +79,11 @@
|
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@fluidframework/build-common": "^0.24.0",
|
|
82
|
-
"@fluidframework/build-tools": "^0.3.
|
|
82
|
+
"@fluidframework/build-tools": "^0.3.1000",
|
|
83
83
|
"@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@^1.0.0",
|
|
84
84
|
"@fluidframework/eslint-config-fluid": "^0.28.2000",
|
|
85
|
-
"@fluidframework/mocha-test-setup": "2.0.0-internal.1.
|
|
86
|
-
"@fluidframework/test-loader-utils": "2.0.0-internal.1.
|
|
85
|
+
"@fluidframework/mocha-test-setup": "^2.0.0-internal.1.1.1",
|
|
86
|
+
"@fluidframework/test-loader-utils": "^2.0.0-internal.1.1.1",
|
|
87
87
|
"@microsoft/api-extractor": "^7.22.2",
|
|
88
88
|
"@rushstack/eslint-config": "^2.5.1",
|
|
89
89
|
"@types/double-ended-queue": "^2.1.0",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"@types/node": "^14.18.0",
|
|
93
93
|
"@types/sinon": "^7.0.13",
|
|
94
94
|
"concurrently": "^6.2.0",
|
|
95
|
-
"copyfiles": "^2.1
|
|
95
|
+
"copyfiles": "^2.4.1",
|
|
96
96
|
"cross-env": "^7.0.2",
|
|
97
97
|
"eslint": "~8.6.0",
|
|
98
98
|
"mocha": "^10.0.0",
|
package/src/catchUpMonitor.ts
CHANGED
|
@@ -45,7 +45,7 @@ export class CatchUpMonitor extends TypedEventEmitter<ICatchUpMonitorEvents> imp
|
|
|
45
45
|
this.targetSeqNumber = this.deltaManager.lastKnownSeqNumber;
|
|
46
46
|
|
|
47
47
|
assert(this.targetSeqNumber >= this.deltaManager.lastSequenceNumber,
|
|
48
|
-
|
|
48
|
+
0x37c /* Cannot wait for seqNumber below last processed sequence number */);
|
|
49
49
|
|
|
50
50
|
this.deltaManager.on("op", this.opHandler);
|
|
51
51
|
|
|
@@ -69,9 +69,17 @@ export class CollabWindowTracker {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
this.opsCountSinceNoop++;
|
|
72
|
-
if (this.opsCountSinceNoop
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
if (this.opsCountSinceNoop === this.NoopCountFrequency) {
|
|
73
|
+
// Ensure we only send noop after a batch of many ops is processed
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
75
|
+
Promise.resolve().then(() => {
|
|
76
|
+
assert(this.opsCountSinceNoop >= this.NoopCountFrequency,
|
|
77
|
+
0x3ae /* not enough ops were sent to reach the noop frequency */);
|
|
78
|
+
this.submitNoop(false /* immediate */);
|
|
79
|
+
// reset count now that all ops are processed
|
|
80
|
+
this.opsCountSinceNoop = 0;
|
|
81
|
+
return;
|
|
82
|
+
});
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
if (this.timer !== undefined) {
|
package/src/connectionManager.ts
CHANGED
|
@@ -136,8 +136,8 @@ interface IPendingConnection {
|
|
|
136
136
|
|
|
137
137
|
/**
|
|
138
138
|
* Implementation of IConnectionManager, used by Container class
|
|
139
|
-
* Implements constant connectivity to relay service, by reconnecting in case of
|
|
140
|
-
* Exposes various controls to
|
|
139
|
+
* Implements constant connectivity to relay service, by reconnecting in case of lost connection or error.
|
|
140
|
+
* Exposes various controls to influence this process, including manual reconnects, forced read-only mode, etc.
|
|
141
141
|
*/
|
|
142
142
|
export class ConnectionManager implements IConnectionManager {
|
|
143
143
|
/** Connection mode used when reconnecting on error or disconnect. */
|
|
@@ -201,7 +201,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
201
201
|
* Automatic reconnecting enabled or disabled.
|
|
202
202
|
* If set to Never, then reconnecting will never be allowed.
|
|
203
203
|
*/
|
|
204
|
-
|
|
204
|
+
public get reconnectMode(): ReconnectMode {
|
|
205
205
|
return this._reconnectMode;
|
|
206
206
|
}
|
|
207
207
|
|
|
@@ -233,7 +233,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
233
233
|
* Returns set of props that can be logged in telemetry that provide some insights / statistics
|
|
234
234
|
* about current or last connection (if there is no connection at the moment)
|
|
235
235
|
*/
|
|
236
|
-
|
|
236
|
+
public get connectionProps(): ITelemetryProperties {
|
|
237
237
|
if (this.connection !== undefined) {
|
|
238
238
|
return this._connectionProps;
|
|
239
239
|
} else {
|
|
@@ -378,7 +378,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
378
378
|
*
|
|
379
379
|
* @param readonly - set or clear force readonly.
|
|
380
380
|
*/
|
|
381
|
-
|
|
381
|
+
public forceReadonly(readonly: boolean) {
|
|
382
382
|
if (readonly !== this._forceReadonly) {
|
|
383
383
|
this.logger.sendTelemetryEvent({
|
|
384
384
|
eventName: "ForceReadOnly",
|
|
@@ -567,11 +567,11 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
567
567
|
}
|
|
568
568
|
|
|
569
569
|
/**
|
|
570
|
-
* Start the connection. Any error should result in container being
|
|
571
|
-
* And report the error if it
|
|
570
|
+
* Start the connection. Any error should result in container being closed.
|
|
571
|
+
* And report the error if it escapes for any reason.
|
|
572
572
|
* @param args - The connection arguments
|
|
573
573
|
*/
|
|
574
|
-
|
|
574
|
+
private triggerConnect(connectionMode: ConnectionMode) {
|
|
575
575
|
assert(this.connection === undefined, 0x239 /* "called only in disconnected state" */);
|
|
576
576
|
if (this.reconnectMode !== ReconnectMode.Enabled) {
|
|
577
577
|
return;
|
|
@@ -584,7 +584,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
584
584
|
* @param reason - Text description of disconnect reason to emit with disconnect event
|
|
585
585
|
* @returns A boolean that indicates if there was an existing connection (or pending connection) to disconnect
|
|
586
586
|
*/
|
|
587
|
-
|
|
587
|
+
private disconnectFromDeltaStream(reason: string): boolean {
|
|
588
588
|
this.pendingReconnect = false;
|
|
589
589
|
|
|
590
590
|
if (this.connection === undefined) {
|
|
@@ -637,7 +637,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
637
637
|
* initial messages.
|
|
638
638
|
* @param connection - The newly established connection
|
|
639
639
|
*/
|
|
640
|
-
|
|
640
|
+
private setupNewSuccessfulConnection(connection: IDocumentDeltaConnection, requestedMode: ConnectionMode) {
|
|
641
641
|
// Old connection should have been cleaned up before establishing a new one
|
|
642
642
|
assert(this.connection === undefined, 0x0e6 /* "old connection exists on new connection setup" */);
|
|
643
643
|
assert(!connection.disposed, 0x28a /* "can't be disposed - Callers need to ensure that!" */);
|
|
@@ -735,7 +735,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
735
735
|
* @param error - Error reconnect information including whether or not to reconnect
|
|
736
736
|
* @returns A promise that resolves when the connection is reestablished or we stop trying
|
|
737
737
|
*/
|
|
738
|
-
|
|
738
|
+
private reconnectOnError(
|
|
739
739
|
requestedMode: ConnectionMode,
|
|
740
740
|
error: IAnyDriverError,
|
|
741
741
|
) {
|
|
@@ -743,7 +743,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
743
743
|
requestedMode,
|
|
744
744
|
error.message,
|
|
745
745
|
error)
|
|
746
|
-
|
|
746
|
+
.catch(this.props.closeHandler);
|
|
747
747
|
}
|
|
748
748
|
|
|
749
749
|
/**
|
|
@@ -772,7 +772,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
772
772
|
this.logger.sendTelemetryEvent({
|
|
773
773
|
eventName: "reconnectingDespiteFatalError",
|
|
774
774
|
reconnectMode: this.reconnectMode,
|
|
775
|
-
|
|
775
|
+
}, error);
|
|
776
776
|
}
|
|
777
777
|
|
|
778
778
|
if (this.reconnectMode === ReconnectMode.Never) {
|
|
@@ -857,8 +857,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
857
857
|
"Switch to write", // message
|
|
858
858
|
);
|
|
859
859
|
}
|
|
860
|
-
})
|
|
861
|
-
.catch(() => {});
|
|
860
|
+
}).catch(() => { });
|
|
862
861
|
}
|
|
863
862
|
return;
|
|
864
863
|
}
|
|
@@ -190,7 +190,7 @@ export class ConnectionStateHandler {
|
|
|
190
190
|
this.waitEvent?.end({ source });
|
|
191
191
|
|
|
192
192
|
assert(this.catchUpMonitor !== undefined,
|
|
193
|
-
|
|
193
|
+
0x37d /* catchUpMonitor should always be set if pendingClientId is set */);
|
|
194
194
|
this.catchUpMonitor.on("caughtUp", this.transitionToConnectedState);
|
|
195
195
|
} else {
|
|
196
196
|
// Adding this event temporarily so that we can get help debugging if something goes wrong.
|
package/src/container.ts
CHANGED
|
@@ -800,12 +800,12 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
|
|
|
800
800
|
// runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
|
|
801
801
|
// container at the same time we get pending state, otherwise this container could reconnect and resubmit with
|
|
802
802
|
// a new clientId and a future container using stale pending state without the new clientId would resubmit them
|
|
803
|
-
|
|
804
803
|
assert(this.attachState === AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
|
|
805
804
|
assert(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid",
|
|
806
805
|
0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
807
806
|
assert(!!this._protocolHandler, 0x2e3 /* "Must have a valid protocol handler instance" */);
|
|
808
|
-
assert(this._protocolHandler.attributes.term !== undefined,
|
|
807
|
+
assert(this._protocolHandler.attributes.term !== undefined,
|
|
808
|
+
0x37e /* Must have a valid protocol handler instance */);
|
|
809
809
|
const pendingState: IPendingContainerState = {
|
|
810
810
|
pendingRuntimeState: this.context.getPendingLocalState(),
|
|
811
811
|
url: this.resolvedUrl.url,
|
|
@@ -814,6 +814,8 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
|
|
|
814
814
|
clientId: this.clientId,
|
|
815
815
|
};
|
|
816
816
|
|
|
817
|
+
this.mc.logger.sendTelemetryEvent({ eventName: "CloseAndGetPendingLocalState" });
|
|
818
|
+
|
|
817
819
|
this.close();
|
|
818
820
|
|
|
819
821
|
return JSON.stringify(pendingState);
|
package/src/contracts.ts
CHANGED
|
@@ -56,13 +56,13 @@ export interface IConnectionManager {
|
|
|
56
56
|
|
|
57
57
|
readonly readOnlyInfo: ReadOnlyInfo;
|
|
58
58
|
|
|
59
|
-
// Various connectivity
|
|
59
|
+
// Various connectivity properties for telemetry describing type of current connection
|
|
60
60
|
// Things like connection mode, service info, etc.
|
|
61
61
|
// Called when connection state changes (connect / disconnect)
|
|
62
62
|
readonly connectionProps: ITelemetryProperties;
|
|
63
63
|
|
|
64
64
|
// Verbose information about connection logged to telemetry in case of issues with
|
|
65
|
-
// maintaining
|
|
65
|
+
// maintaining healthy connection, including op gaps, not receiving join op in time, etc.
|
|
66
66
|
// Contains details information, like sequence numbers at connection time, initial ops info, etc.
|
|
67
67
|
readonly connectionVerboseProps: ITelemetryProperties;
|
|
68
68
|
|
|
@@ -73,7 +73,7 @@ export interface IConnectionManager {
|
|
|
73
73
|
prepareMessageToSend(message: Omit<IDocumentMessage, "clientSequenceNumber">): IDocumentMessage | undefined;
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
* Called before
|
|
76
|
+
* Called before incoming message is processed. Incoming messages can be combing from connection,
|
|
77
77
|
* but also could come from storage.
|
|
78
78
|
* This call allows connection manager to adjust knowledge about acked ops sent on previous connection.
|
|
79
79
|
* Can be called at any time, including when there is no active connection.
|
|
@@ -107,11 +107,11 @@ export interface IConnectionManager {
|
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
109
|
* This interface represents a set of callbacks provided by DeltaManager to IConnectionManager on its creation
|
|
110
|
-
* IConnectionManager instance will use them to communicate to DeltaManager
|
|
110
|
+
* IConnectionManager instance will use them to communicate to DeltaManager about various events.
|
|
111
111
|
*/
|
|
112
112
|
export interface IConnectionManagerFactoryArgs {
|
|
113
113
|
/**
|
|
114
|
-
* Called by connection manager for each
|
|
114
|
+
* Called by connection manager for each incoming op. Some ops maybe delivered before
|
|
115
115
|
* connectHandler is called (initial ops on socket connection)
|
|
116
116
|
*/
|
|
117
117
|
readonly incomingOpHandler: (messages: ISequencedDocumentMessage[], reason: string) => void;
|
|
@@ -131,8 +131,8 @@ export interface IConnectionManagerFactoryArgs {
|
|
|
131
131
|
readonly reconnectionDelayHandler: (delayMs: number, error: unknown) => void;
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
|
-
* Called by connection manager
|
|
135
|
-
* Expects dispose() call in
|
|
134
|
+
* Called by connection manager whenever critical error happens and container should be closed.
|
|
135
|
+
* Expects dispose() call in response to this call.
|
|
136
136
|
*/
|
|
137
137
|
readonly closeHandler: (error?: any) => void;
|
|
138
138
|
|
package/src/deltaManager.ts
CHANGED
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
normalizeError,
|
|
26
26
|
logIfFalse,
|
|
27
27
|
safeRaiseEvent,
|
|
28
|
+
MonitoringContext,
|
|
29
|
+
loggerToMonitoringContext,
|
|
28
30
|
} from "@fluidframework/telemetry-utils";
|
|
29
31
|
import {
|
|
30
32
|
IDocumentDeltaStorageService,
|
|
@@ -47,6 +49,7 @@ import {
|
|
|
47
49
|
DataCorruptionError,
|
|
48
50
|
extractSafePropertiesFromMessage,
|
|
49
51
|
DataProcessingError,
|
|
52
|
+
UsageError,
|
|
50
53
|
} from "@fluidframework/container-utils";
|
|
51
54
|
import { DeltaQueue } from "./deltaQueue";
|
|
52
55
|
import {
|
|
@@ -89,6 +92,15 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
89
92
|
private pending: ISequencedDocumentMessage[] = [];
|
|
90
93
|
private fetchReason: string | undefined;
|
|
91
94
|
|
|
95
|
+
private readonly mc: MonitoringContext;
|
|
96
|
+
|
|
97
|
+
// A boolean used to assert that ops are not being sent while processing another op.
|
|
98
|
+
private currentlyProcessingOps: boolean = false;
|
|
99
|
+
|
|
100
|
+
// Feature gate that closes a container when sending an op if the container is
|
|
101
|
+
// concurrently processing another op
|
|
102
|
+
private readonly preventConcurrentOpSend: boolean = true;
|
|
103
|
+
|
|
92
104
|
// The minimum sequence number and last sequence number received from the server
|
|
93
105
|
private minSequenceNumber: number = 0;
|
|
94
106
|
|
|
@@ -113,6 +125,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
113
125
|
private previouslyProcessedMessage: ISequencedDocumentMessage | undefined;
|
|
114
126
|
|
|
115
127
|
// The sequence number we initially loaded from
|
|
128
|
+
// In case of reading from a snapshot or pending state, its value will be equal to
|
|
129
|
+
// the last message that got serialized.
|
|
116
130
|
private initSequenceNumber: number = 0;
|
|
117
131
|
|
|
118
132
|
private readonly _inbound: DeltaQueue<ISequencedDocumentMessage>;
|
|
@@ -186,6 +200,9 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
186
200
|
public get clientDetails() { return this.connectionManager.clientDetails; }
|
|
187
201
|
|
|
188
202
|
public submit(type: MessageType, contents: any, batch = false, metadata?: any) {
|
|
203
|
+
if (this.currentlyProcessingOps && this.preventConcurrentOpSend) {
|
|
204
|
+
this.close(new UsageError("Making changes to data model is disallowed while processing ops."));
|
|
205
|
+
}
|
|
189
206
|
const messagePartial: Omit<IDocumentMessage, "clientSequenceNumber"> = {
|
|
190
207
|
contents: JSON.stringify(contents),
|
|
191
208
|
metadata,
|
|
@@ -196,7 +213,6 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
196
213
|
if (!batch) {
|
|
197
214
|
this.flush();
|
|
198
215
|
}
|
|
199
|
-
|
|
200
216
|
const message = this.connectionManager.prepareMessageToSend(messagePartial);
|
|
201
217
|
if (message === undefined) {
|
|
202
218
|
return -1;
|
|
@@ -211,7 +227,6 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
211
227
|
if (!batch) {
|
|
212
228
|
this.flush();
|
|
213
229
|
}
|
|
214
|
-
|
|
215
230
|
return message.clientSequenceNumber;
|
|
216
231
|
}
|
|
217
232
|
|
|
@@ -293,7 +308,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
293
308
|
};
|
|
294
309
|
|
|
295
310
|
this.connectionManager = createConnectionManager(props);
|
|
296
|
-
|
|
311
|
+
this.mc = loggerToMonitoringContext(logger);
|
|
312
|
+
this.preventConcurrentOpSend = this.mc.config.getBoolean("Fluid.Container.ConcurrentOpSend") === true;
|
|
297
313
|
this._inbound = new DeltaQueue<ISequencedDocumentMessage>(
|
|
298
314
|
(op) => {
|
|
299
315
|
this.processInboundMessage(op);
|
|
@@ -762,6 +778,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
762
778
|
|
|
763
779
|
private processInboundMessage(message: ISequencedDocumentMessage): void {
|
|
764
780
|
const startTime = Date.now();
|
|
781
|
+
assert(!this.currentlyProcessingOps, 0x3af /* Already processing ops. */);
|
|
782
|
+
this.currentlyProcessingOps = true;
|
|
765
783
|
this.lastProcessedMessage = message;
|
|
766
784
|
|
|
767
785
|
// All non-system messages are coming from some client, and should have clientId
|
|
@@ -816,7 +834,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
816
834
|
throw new Error("Attempted to process an inbound message without a handler attached");
|
|
817
835
|
}
|
|
818
836
|
this.handler.process(message);
|
|
819
|
-
|
|
837
|
+
this.currentlyProcessingOps = false;
|
|
820
838
|
const endTime = Date.now();
|
|
821
839
|
|
|
822
840
|
// Should be last, after changing this.lastProcessedSequenceNumber above, as many callers
|
package/src/deltaQueue.ts
CHANGED
|
@@ -114,9 +114,9 @@ export class DeltaQueue<T>
|
|
|
114
114
|
if (this.anythingToProcess() && this.processingPromise === undefined) {
|
|
115
115
|
// Use a resolved promise to start the processing on a separate stack.
|
|
116
116
|
this.processingPromise = Promise.resolve().then(() => {
|
|
117
|
-
assert(this.processingPromise !== undefined,
|
|
117
|
+
assert(this.processingPromise !== undefined, 0x37f /* reentrancy? */);
|
|
118
118
|
const result = this.processDeltas();
|
|
119
|
-
assert(this.processingPromise !== undefined,
|
|
119
|
+
assert(this.processingPromise !== undefined, 0x380 /* reentrancy? */);
|
|
120
120
|
// WARNING: Do not move next line to .finally() clause!
|
|
121
121
|
// It runs async and creates a race condition where incoming ensureProcessing() call observes
|
|
122
122
|
// from previous run while previous run is over (but finally clause was not scheduled yet)
|
|
@@ -128,7 +128,7 @@ export class DeltaQueue<T>
|
|
|
128
128
|
this.emit("error", error);
|
|
129
129
|
return { count: 0, duration: 0 };
|
|
130
130
|
});
|
|
131
|
-
assert(this.processingPromise !== undefined,
|
|
131
|
+
assert(this.processingPromise !== undefined, 0x381 /* processDeltas() should run async */);
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
package/src/packageVersion.ts
CHANGED