@fluidframework/container-loader 2.0.0-internal.1.1.0 → 2.0.0-internal.1.2.0.93071
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/collabWindowTracker.d.ts +1 -1
- package/dist/collabWindowTracker.d.ts.map +1 -1
- package/dist/collabWindowTracker.js +2 -1
- package/dist/collabWindowTracker.js.map +1 -1
- package/dist/connectionManager.d.ts +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +5 -5
- package/dist/connectionManager.js.map +1 -1
- package/dist/container.d.ts +10 -0
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +54 -42
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +18 -7
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +18 -8
- package/dist/containerContext.js.map +1 -1
- package/dist/deltaManager.d.ts +1 -1
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +18 -6
- package/dist/deltaManager.js.map +1 -1
- package/dist/loader.d.ts +1 -1
- package/dist/loader.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/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +2 -1
- package/dist/protocol.js.map +1 -1
- package/lib/collabWindowTracker.d.ts +1 -1
- package/lib/collabWindowTracker.d.ts.map +1 -1
- package/lib/collabWindowTracker.js +3 -2
- package/lib/collabWindowTracker.js.map +1 -1
- package/lib/connectionManager.d.ts +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +6 -8
- package/lib/connectionManager.js.map +1 -1
- package/lib/container.d.ts +10 -0
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +56 -44
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +18 -7
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +19 -9
- package/lib/containerContext.js.map +1 -1
- package/lib/deltaManager.d.ts +1 -1
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +18 -6
- package/lib/deltaManager.js.map +1 -1
- package/lib/loader.d.ts +1 -1
- package/lib/loader.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/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +2 -1
- package/lib/protocol.js.map +1 -1
- package/package.json +12 -12
- package/src/collabWindowTracker.ts +4 -3
- package/src/connectionManager.ts +6 -6
- package/src/container.ts +67 -50
- package/src/containerContext.ts +22 -8
- package/src/deltaManager.ts +20 -7
- package/src/loader.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +2 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { assert, Timer } from "@fluidframework/common-utils";
|
|
7
7
|
import { ISequencedDocumentMessage, MessageType } from "@fluidframework/protocol-definitions";
|
|
8
|
-
import { isRuntimeMessage } from "@fluidframework/driver-utils";
|
|
8
|
+
import { isRuntimeMessage, MessageType2 } from "@fluidframework/driver-utils";
|
|
9
9
|
|
|
10
10
|
const defaultNoopTimeFrequency = 2000;
|
|
11
11
|
const defaultNoopCountFrequency = 50;
|
|
@@ -34,7 +34,7 @@ export class CollabWindowTracker {
|
|
|
34
34
|
private readonly timer: Timer | undefined;
|
|
35
35
|
|
|
36
36
|
constructor(
|
|
37
|
-
private readonly submit: (type: MessageType
|
|
37
|
+
private readonly submit: (type: MessageType) => void,
|
|
38
38
|
NoopTimeFrequency: number = defaultNoopTimeFrequency,
|
|
39
39
|
private readonly NoopCountFrequency: number = defaultNoopCountFrequency,
|
|
40
40
|
) {
|
|
@@ -93,7 +93,8 @@ export class CollabWindowTracker {
|
|
|
93
93
|
|
|
94
94
|
private submitNoop(immediate: boolean) {
|
|
95
95
|
// Anything other than null is immediate noop
|
|
96
|
-
|
|
96
|
+
// ADO:1385: Remove cast and use MessageType once definition changes propagate
|
|
97
|
+
this.submit(immediate ? (MessageType2.Accept as unknown as MessageType) : MessageType.NoOp);
|
|
97
98
|
assert(this.opsCountSinceNoop === 0,
|
|
98
99
|
0x243 /* "stopSequenceNumberUpdate should be called as result of sending any op!" */);
|
|
99
100
|
}
|
package/src/connectionManager.ts
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
waitForConnectedState,
|
|
32
32
|
DeltaStreamConnectionForbiddenError,
|
|
33
33
|
logNetworkFailure,
|
|
34
|
-
|
|
34
|
+
isRuntimeMessage,
|
|
35
35
|
} from "@fluidframework/driver-utils";
|
|
36
36
|
import {
|
|
37
37
|
ConnectionMode,
|
|
@@ -168,7 +168,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
168
168
|
private clientSequenceNumber = 0;
|
|
169
169
|
private clientSequenceNumberObserved = 0;
|
|
170
170
|
/** Counts the number of noops sent by the client which may not be acked. */
|
|
171
|
-
private
|
|
171
|
+
private localOpsToIgnore = 0;
|
|
172
172
|
|
|
173
173
|
/** track clientId used last time when we sent any ops */
|
|
174
174
|
private lastSubmittedClientId: string | undefined;
|
|
@@ -247,7 +247,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
247
247
|
|
|
248
248
|
public shouldJoinWrite(): boolean {
|
|
249
249
|
// We don't have to wait for ack for topmost NoOps. So subtract those.
|
|
250
|
-
return this.clientSequenceNumberObserved < (this.clientSequenceNumber - this.
|
|
250
|
+
return this.clientSequenceNumberObserved < (this.clientSequenceNumber - this.localOpsToIgnore);
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
/**
|
|
@@ -819,10 +819,10 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
819
819
|
this.clientSequenceNumberObserved = 0;
|
|
820
820
|
}
|
|
821
821
|
|
|
822
|
-
if (message
|
|
823
|
-
this.
|
|
822
|
+
if (!isRuntimeMessage(message)) {
|
|
823
|
+
this.localOpsToIgnore++;
|
|
824
824
|
} else {
|
|
825
|
-
this.
|
|
825
|
+
this.localOpsToIgnore = 0;
|
|
826
826
|
}
|
|
827
827
|
|
|
828
828
|
return {
|
package/src/container.ts
CHANGED
|
@@ -29,10 +29,9 @@ import {
|
|
|
29
29
|
IContainerLoadMode,
|
|
30
30
|
IFluidCodeDetails,
|
|
31
31
|
isFluidCodeDetails,
|
|
32
|
+
IBatchMessage,
|
|
32
33
|
} from "@fluidframework/container-definitions";
|
|
33
34
|
import {
|
|
34
|
-
DataCorruptionError,
|
|
35
|
-
extractSafePropertiesFromMessage,
|
|
36
35
|
GenericError,
|
|
37
36
|
UsageError,
|
|
38
37
|
} from "@fluidframework/container-utils";
|
|
@@ -50,8 +49,6 @@ import {
|
|
|
50
49
|
combineAppAndProtocolSummary,
|
|
51
50
|
runWithRetry,
|
|
52
51
|
isFluidResolvedUrl,
|
|
53
|
-
isRuntimeMessage,
|
|
54
|
-
isUnpackedRuntimeMessage,
|
|
55
52
|
} from "@fluidframework/driver-utils";
|
|
56
53
|
import { IQuorumSnapshot } from "@fluidframework/protocol-base";
|
|
57
54
|
import {
|
|
@@ -61,7 +58,6 @@ import {
|
|
|
61
58
|
ICommittedProposal,
|
|
62
59
|
IDocumentAttributes,
|
|
63
60
|
IDocumentMessage,
|
|
64
|
-
IProcessMessageResult,
|
|
65
61
|
IProtocolState,
|
|
66
62
|
IQuorumClients,
|
|
67
63
|
IQuorumProposals,
|
|
@@ -1416,7 +1412,7 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
|
|
|
1416
1412
|
const protocol = protocolHandlerBuilder(
|
|
1417
1413
|
attributes,
|
|
1418
1414
|
quorumSnapshot,
|
|
1419
|
-
(key, value) => this.submitMessage(MessageType.Propose, { key, value }),
|
|
1415
|
+
(key, value) => this.submitMessage(MessageType.Propose, JSON.stringify({ key, value })),
|
|
1420
1416
|
this._initialClients ?? [],
|
|
1421
1417
|
);
|
|
1422
1418
|
|
|
@@ -1585,6 +1581,7 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
|
|
|
1585
1581
|
});
|
|
1586
1582
|
|
|
1587
1583
|
deltaManager.on("readonly", (readonly) => {
|
|
1584
|
+
this.setContextConnectedState(this.connectionState === ConnectionState.Connected, readonly);
|
|
1588
1585
|
this.emit("readonly", readonly);
|
|
1589
1586
|
});
|
|
1590
1587
|
|
|
@@ -1682,9 +1679,7 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
|
|
|
1682
1679
|
|
|
1683
1680
|
// Both protocol and context should not be undefined if we got so far.
|
|
1684
1681
|
|
|
1685
|
-
|
|
1686
|
-
this.context.setConnectionState(state, this.clientId);
|
|
1687
|
-
}
|
|
1682
|
+
this.setContextConnectedState(state, this._deltaManager.connectionManager.readOnlyInfo.readonly ?? false);
|
|
1688
1683
|
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
1689
1684
|
raiseConnectedEvent(this.mc.logger, this, state, this.clientId);
|
|
1690
1685
|
|
|
@@ -1694,35 +1689,53 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
|
|
|
1694
1689
|
}
|
|
1695
1690
|
}
|
|
1696
1691
|
|
|
1692
|
+
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
1697
1693
|
private submitContainerMessage(type: MessageType, contents: any, batch?: boolean, metadata?: any): number {
|
|
1698
|
-
|
|
1699
|
-
switch (outboundMessageType) {
|
|
1694
|
+
switch (type) {
|
|
1700
1695
|
case MessageType.Operation:
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
const summary = contents as ISummaryContent;
|
|
1709
|
-
if (summary.details === undefined) {
|
|
1710
|
-
summary.details = {};
|
|
1711
|
-
}
|
|
1712
|
-
summary.details.includesProtocolTree =
|
|
1713
|
-
this.options.summarizeProtocolTree === true;
|
|
1714
|
-
break;
|
|
1715
|
-
}
|
|
1696
|
+
return this.submitMessage(
|
|
1697
|
+
type,
|
|
1698
|
+
JSON.stringify(contents),
|
|
1699
|
+
batch,
|
|
1700
|
+
metadata);
|
|
1701
|
+
case MessageType.Summarize:
|
|
1702
|
+
return this.submitSummaryMessage(contents as unknown as ISummaryContent);
|
|
1716
1703
|
default:
|
|
1717
1704
|
this.close(new GenericError("invalidContainerSubmitOpType",
|
|
1718
1705
|
undefined /* error */,
|
|
1719
1706
|
{ messageType: type }));
|
|
1720
1707
|
return -1;
|
|
1721
1708
|
}
|
|
1722
|
-
return this.submitMessage(type, contents, batch, metadata);
|
|
1723
1709
|
}
|
|
1724
1710
|
|
|
1725
|
-
|
|
1711
|
+
/** @returns clientSequenceNumber of last message in a batch */
|
|
1712
|
+
private submitBatch(batch: IBatchMessage[]): number {
|
|
1713
|
+
let clientSequenceNumber = -1;
|
|
1714
|
+
for (const message of batch) {
|
|
1715
|
+
clientSequenceNumber = this.submitMessage(
|
|
1716
|
+
MessageType.Operation,
|
|
1717
|
+
message.contents,
|
|
1718
|
+
true, // batch
|
|
1719
|
+
message.metadata);
|
|
1720
|
+
}
|
|
1721
|
+
this._deltaManager.flush();
|
|
1722
|
+
return clientSequenceNumber;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
private submitSummaryMessage(summary: ISummaryContent) {
|
|
1726
|
+
// github #6451: this is only needed for staging so the server
|
|
1727
|
+
// know when the protocol tree is included
|
|
1728
|
+
// this can be removed once all clients send
|
|
1729
|
+
// protocol tree by default
|
|
1730
|
+
if (summary.details === undefined) {
|
|
1731
|
+
summary.details = {};
|
|
1732
|
+
}
|
|
1733
|
+
summary.details.includesProtocolTree =
|
|
1734
|
+
this.options.summarizeProtocolTree === true;
|
|
1735
|
+
return this.submitMessage(MessageType.Summarize, JSON.stringify(summary), false /* batch */);
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
private submitMessage(type: MessageType, contents?: string, batch?: boolean, metadata?: any): number {
|
|
1726
1739
|
if (this.connectionState !== ConnectionState.Connected) {
|
|
1727
1740
|
this.mc.logger.sendErrorEvent({ eventName: "SubmitMessageWithNoConnection", type });
|
|
1728
1741
|
return -1;
|
|
@@ -1733,28 +1746,14 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
|
|
|
1733
1746
|
return this._deltaManager.submit(type, contents, batch, metadata);
|
|
1734
1747
|
}
|
|
1735
1748
|
|
|
1736
|
-
private processRemoteMessage(message: ISequencedDocumentMessage)
|
|
1749
|
+
private processRemoteMessage(message: ISequencedDocumentMessage) {
|
|
1737
1750
|
const local = this.clientId === message.clientId;
|
|
1738
1751
|
|
|
1739
1752
|
// Allow the protocol handler to process the message
|
|
1740
|
-
|
|
1741
|
-
try {
|
|
1742
|
-
result = this.protocolHandler.processMessage(message, local);
|
|
1743
|
-
} catch (error) {
|
|
1744
|
-
this.close(wrapError(error, (errorMessage) =>
|
|
1745
|
-
new DataCorruptionError(errorMessage, extractSafePropertiesFromMessage(message))));
|
|
1746
|
-
}
|
|
1753
|
+
const result = this.protocolHandler.processMessage(message, local);
|
|
1747
1754
|
|
|
1748
|
-
//
|
|
1749
|
-
|
|
1750
|
-
this.mc.logger.sendTelemetryEvent(
|
|
1751
|
-
{ eventName: "UnpackedRuntimeMessage", type: message.type });
|
|
1752
|
-
}
|
|
1753
|
-
// Forward non system messages to the loaded runtime for processing
|
|
1754
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
1755
|
-
if (isRuntimeMessage(message) || isUnpackedRuntimeMessage(message)) {
|
|
1756
|
-
this.context.process(message, local, undefined);
|
|
1757
|
-
}
|
|
1755
|
+
// Forward messages to the loaded runtime for processing
|
|
1756
|
+
this.context.process(message, local, undefined);
|
|
1758
1757
|
|
|
1759
1758
|
// Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
|
|
1760
1759
|
if (this.activeConnection()) {
|
|
@@ -1767,10 +1766,10 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
|
|
|
1767
1766
|
this.serviceConfiguration !== undefined,
|
|
1768
1767
|
0x2e4 /* "there should be service config for active connection" */);
|
|
1769
1768
|
this.collabWindowTracker = new CollabWindowTracker(
|
|
1770
|
-
(type
|
|
1769
|
+
(type) => {
|
|
1771
1770
|
assert(this.activeConnection(),
|
|
1772
1771
|
0x241 /* "disconnect should result in stopSequenceNumberUpdate() call" */);
|
|
1773
|
-
this.submitMessage(type
|
|
1772
|
+
this.submitMessage(type);
|
|
1774
1773
|
},
|
|
1775
1774
|
this.serviceConfiguration.noopTimeFrequency,
|
|
1776
1775
|
this.serviceConfiguration.noopCountFrequency,
|
|
@@ -1780,8 +1779,6 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
|
|
|
1780
1779
|
}
|
|
1781
1780
|
|
|
1782
1781
|
this.emit("op", message);
|
|
1783
|
-
|
|
1784
|
-
return result;
|
|
1785
1782
|
}
|
|
1786
1783
|
|
|
1787
1784
|
private submitSignal(message: any) {
|
|
@@ -1857,6 +1854,8 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
|
|
|
1857
1854
|
new QuorumProxy(this.protocolHandler.quorum),
|
|
1858
1855
|
loader,
|
|
1859
1856
|
(type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata),
|
|
1857
|
+
(summaryOp: ISummaryContent) => this.submitSummaryMessage(summaryOp),
|
|
1858
|
+
(batch: IBatchMessage[]) => this.submitBatch(batch),
|
|
1860
1859
|
(message) => this.submitSignal(message),
|
|
1861
1860
|
(error?: ICriticalContainerError) => this.close(error),
|
|
1862
1861
|
Container.version,
|
|
@@ -1879,4 +1878,22 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
|
|
|
1879
1878
|
private logContainerError(warning: ContainerWarning) {
|
|
1880
1879
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerWarning" }, warning);
|
|
1881
1880
|
}
|
|
1881
|
+
|
|
1882
|
+
/**
|
|
1883
|
+
* Set the connected state of the ContainerContext
|
|
1884
|
+
* This controls the "connected" state of the ContainerRuntime as well
|
|
1885
|
+
* @param state - Is the container currently connected?
|
|
1886
|
+
* @param readonly - Is the container in readonly mode?
|
|
1887
|
+
*/
|
|
1888
|
+
private setContextConnectedState(state: boolean, readonly: boolean): void {
|
|
1889
|
+
if (this._context?.disposed === false) {
|
|
1890
|
+
/**
|
|
1891
|
+
* We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
|
|
1892
|
+
* ops getting through to the DeltaManager.
|
|
1893
|
+
* The ContainerRuntime's "connected" state simply means it is ok to send ops
|
|
1894
|
+
* See https://dev.azure.com/fluidframework/internal/_workitems/edit/1246
|
|
1895
|
+
*/
|
|
1896
|
+
this.context.setConnectionState(state && !readonly, this.clientId);
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1882
1899
|
}
|
package/src/containerContext.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
|
-
import {
|
|
7
|
+
import { LazyPromise } from "@fluidframework/common-utils";
|
|
8
8
|
import {
|
|
9
9
|
IAudience,
|
|
10
10
|
IContainerContext,
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
ICodeDetailsLoader,
|
|
23
23
|
IFluidModuleWithDetails,
|
|
24
24
|
ISnapshotTreeWithBlobContents,
|
|
25
|
+
IBatchMessage,
|
|
25
26
|
} from "@fluidframework/container-definitions";
|
|
26
27
|
import {
|
|
27
28
|
IRequest,
|
|
@@ -42,6 +43,7 @@ import {
|
|
|
42
43
|
ISummaryTree,
|
|
43
44
|
IVersion,
|
|
44
45
|
MessageType,
|
|
46
|
+
ISummaryContent,
|
|
45
47
|
} from "@fluidframework/protocol-definitions";
|
|
46
48
|
import { PerformanceEvent } from "@fluidframework/telemetry-utils";
|
|
47
49
|
import { Container } from "./container";
|
|
@@ -59,6 +61,8 @@ export class ContainerContext implements IContainerContext {
|
|
|
59
61
|
quorum: IQuorum,
|
|
60
62
|
loader: ILoader,
|
|
61
63
|
submitFn: (type: MessageType, contents: any, batch: boolean, appData: any) => number,
|
|
64
|
+
submitSummaryFn: (summaryOp: ISummaryContent) => number,
|
|
65
|
+
submitBatchFn: (batch: IBatchMessage[]) => number,
|
|
62
66
|
submitSignalFn: (contents: any) => void,
|
|
63
67
|
closeFn: (error?: ICriticalContainerError) => void,
|
|
64
68
|
version: string,
|
|
@@ -76,6 +80,8 @@ export class ContainerContext implements IContainerContext {
|
|
|
76
80
|
quorum,
|
|
77
81
|
loader,
|
|
78
82
|
submitFn,
|
|
83
|
+
submitSummaryFn,
|
|
84
|
+
submitBatchFn,
|
|
79
85
|
submitSignalFn,
|
|
80
86
|
closeFn,
|
|
81
87
|
version,
|
|
@@ -107,8 +113,13 @@ export class ContainerContext implements IContainerContext {
|
|
|
107
113
|
return this.container.clientDetails;
|
|
108
114
|
}
|
|
109
115
|
|
|
116
|
+
private _connected: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* When true, ops are free to flow
|
|
119
|
+
* When false, ops should be kept as pending or rejected
|
|
120
|
+
*/
|
|
110
121
|
public get connected(): boolean {
|
|
111
|
-
return this.
|
|
122
|
+
return this._connected;
|
|
112
123
|
}
|
|
113
124
|
|
|
114
125
|
public get canSummarize(): boolean {
|
|
@@ -166,6 +177,9 @@ export class ContainerContext implements IContainerContext {
|
|
|
166
177
|
quorum: IQuorum,
|
|
167
178
|
public readonly loader: ILoader,
|
|
168
179
|
public readonly submitFn: (type: MessageType, contents: any, batch: boolean, appData: any) => number,
|
|
180
|
+
public readonly submitSummaryFn: (summaryOp: ISummaryContent) => number,
|
|
181
|
+
/** @returns clientSequenceNumber of last message in a batch */
|
|
182
|
+
public readonly submitBatchFn: (batch: IBatchMessage[]) => number,
|
|
169
183
|
public readonly submitSignalFn: (contents: any) => void,
|
|
170
184
|
public readonly closeFn: (error?: ICriticalContainerError) => void,
|
|
171
185
|
public readonly version: string,
|
|
@@ -174,6 +188,7 @@ export class ContainerContext implements IContainerContext {
|
|
|
174
188
|
public readonly pendingLocalState?: unknown,
|
|
175
189
|
|
|
176
190
|
) {
|
|
191
|
+
this._connected = this.container.connected;
|
|
177
192
|
this._quorum = quorum;
|
|
178
193
|
this.taggedLogger = container.subLogger;
|
|
179
194
|
this._fluidModuleP = new LazyPromise<IFluidModuleWithDetails>(
|
|
@@ -183,9 +198,10 @@ export class ContainerContext implements IContainerContext {
|
|
|
183
198
|
}
|
|
184
199
|
|
|
185
200
|
/**
|
|
186
|
-
* @deprecated
|
|
187
|
-
* ContainerContext should only take an IQuorumClients
|
|
188
|
-
*
|
|
201
|
+
* @deprecated Temporary migratory API, to be removed when customers no longer need it.
|
|
202
|
+
* When removed, `ContainerContext` should only take an {@link @fluidframework/container-definitions#IQuorumClients}
|
|
203
|
+
* rather than an {@link @fluidframework/protocol-definitions#IQuorum}.
|
|
204
|
+
* See {@link @fluidframework/container-definitions#IContainerContext} for more details.
|
|
189
205
|
*/
|
|
190
206
|
public getSpecifiedCodeDetails(): IFluidCodeDetails | undefined {
|
|
191
207
|
return (this._quorum.get("code") ?? this._quorum.get("code2")) as IFluidCodeDetails | undefined;
|
|
@@ -223,9 +239,7 @@ export class ContainerContext implements IContainerContext {
|
|
|
223
239
|
|
|
224
240
|
public setConnectionState(connected: boolean, clientId?: string) {
|
|
225
241
|
const runtime = this.runtime;
|
|
226
|
-
|
|
227
|
-
assert(connected === this.connected, 0x0de /* "Mismatch in connection state while setting" */);
|
|
228
|
-
|
|
242
|
+
this._connected = connected;
|
|
229
243
|
runtime.setConnectionState(connected, clientId);
|
|
230
244
|
}
|
|
231
245
|
|
package/src/deltaManager.ts
CHANGED
|
@@ -199,12 +199,12 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
199
199
|
public get readOnlyInfo() { return this.connectionManager.readOnlyInfo; }
|
|
200
200
|
public get clientDetails() { return this.connectionManager.clientDetails; }
|
|
201
201
|
|
|
202
|
-
public submit(type: MessageType, contents
|
|
202
|
+
public submit(type: MessageType, contents?: string, batch = false, metadata?: any) {
|
|
203
203
|
if (this.currentlyProcessingOps && this.preventConcurrentOpSend) {
|
|
204
204
|
this.close(new UsageError("Making changes to data model is disallowed while processing ops."));
|
|
205
205
|
}
|
|
206
206
|
const messagePartial: Omit<IDocumentMessage, "clientSequenceNumber"> = {
|
|
207
|
-
contents
|
|
207
|
+
contents,
|
|
208
208
|
metadata,
|
|
209
209
|
referenceSequenceNumber: this.lastProcessedSequenceNumber,
|
|
210
210
|
type,
|
|
@@ -218,7 +218,9 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
218
218
|
return -1;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
if (contents !== undefined) {
|
|
222
|
+
this.opsSize += contents.length;
|
|
223
|
+
}
|
|
222
224
|
|
|
223
225
|
this.messageBuffer.push(message);
|
|
224
226
|
|
|
@@ -233,15 +235,26 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
233
235
|
public submitSignal(content: any) { return this.connectionManager.submitSignal(content); }
|
|
234
236
|
|
|
235
237
|
public flush() {
|
|
236
|
-
|
|
238
|
+
const batch = this.messageBuffer;
|
|
239
|
+
if (batch.length === 0) {
|
|
237
240
|
return;
|
|
238
241
|
}
|
|
239
242
|
|
|
243
|
+
this.messageBuffer = [];
|
|
244
|
+
|
|
240
245
|
// The prepareFlush event allows listeners to append metadata to the batch prior to submission.
|
|
241
|
-
this.emit("prepareSend",
|
|
246
|
+
this.emit("prepareSend", batch);
|
|
242
247
|
|
|
243
|
-
|
|
244
|
-
|
|
248
|
+
if (batch.length === 1) {
|
|
249
|
+
assert(batch[0].metadata?.batch === undefined, "no batch markup on single message");
|
|
250
|
+
} else {
|
|
251
|
+
assert(batch[0].metadata?.batch === true, "no start batch markup");
|
|
252
|
+
assert(batch[batch.length - 1].metadata?.batch === false, "no end batch markup");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
this.connectionManager.sendMessages(batch);
|
|
256
|
+
|
|
257
|
+
assert(this.messageBuffer.length === 0, "reentrancy");
|
|
245
258
|
}
|
|
246
259
|
|
|
247
260
|
public get connectionProps(): ITelemetryProperties {
|
package/src/loader.ts
CHANGED
|
@@ -133,7 +133,7 @@ export interface ILoaderOptions extends ILoaderOptions1 {
|
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
135
|
* @deprecated IFluidModuleWithDetails interface is moved to
|
|
136
|
-
* {@link @fluidframework/container-
|
|
136
|
+
* {@link @fluidframework/container-definitions#IFluidModuleWithDetails}
|
|
137
137
|
* to have all the code loading modules in one package. #8193
|
|
138
138
|
* Encapsulates a module entry point with corresponding code details.
|
|
139
139
|
*/
|
package/src/packageVersion.ts
CHANGED
package/src/protocol.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
ISignalMessage,
|
|
19
19
|
MessageType,
|
|
20
20
|
} from "@fluidframework/protocol-definitions";
|
|
21
|
+
import { canBeCoalescedByService } from "@fluidframework/driver-utils";
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Function to be used for creating a protocol handler.
|
|
@@ -68,7 +69,7 @@ export class ProtocolHandler extends ProtocolOpHandler implements IProtocolHandl
|
|
|
68
69
|
throw new Error("Remote message's clientId is missing from the quorum");
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
if (client?.shouldHaveLeft === true && message
|
|
72
|
+
if (client?.shouldHaveLeft === true && !canBeCoalescedByService(message)) {
|
|
72
73
|
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
73
74
|
throw new Error("Remote message's clientId already should have left");
|
|
74
75
|
}
|