@fluidframework/container-loader 2.0.0-internal.5.3.4 → 2.0.0-internal.5.4.2
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/CHANGELOG.md +4 -0
- package/README.md +6 -3
- package/dist/audience.d.ts +1 -0
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js +3 -1
- package/dist/audience.js.map +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +6 -11
- package/dist/connectionManager.js.map +1 -1
- package/dist/container.d.ts +2 -3
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +58 -93
- package/dist/container.js.map +1 -1
- package/dist/debugLogger.d.ts +30 -0
- package/dist/debugLogger.d.ts.map +1 -0
- package/dist/debugLogger.js +96 -0
- package/dist/debugLogger.js.map +1 -0
- package/dist/deltaManager.d.ts +0 -1
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +17 -9
- package/dist/deltaManager.js.map +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +16 -5
- package/dist/loader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol.d.ts +4 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +23 -1
- package/dist/protocol.js.map +1 -1
- package/dist/quorum.d.ts +4 -1
- package/dist/quorum.d.ts.map +1 -1
- package/dist/quorum.js +1 -13
- package/dist/quorum.js.map +1 -1
- package/lib/audience.d.ts +1 -0
- package/lib/audience.d.ts.map +1 -1
- package/lib/audience.js +3 -1
- package/lib/audience.js.map +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +7 -9
- package/lib/connectionManager.js.map +1 -1
- package/lib/container.d.ts +2 -3
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +61 -96
- package/lib/container.js.map +1 -1
- package/lib/debugLogger.d.ts +30 -0
- package/lib/debugLogger.d.ts.map +1 -0
- package/lib/debugLogger.js +92 -0
- package/lib/debugLogger.js.map +1 -0
- package/lib/deltaManager.d.ts +0 -1
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +15 -4
- package/lib/deltaManager.js.map +1 -1
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +16 -5
- package/lib/loader.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol.d.ts +4 -2
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +23 -1
- package/lib/protocol.js.map +1 -1
- package/lib/quorum.d.ts +4 -1
- package/lib/quorum.d.ts.map +1 -1
- package/lib/quorum.js +0 -11
- package/lib/quorum.js.map +1 -1
- package/package.json +12 -12
- package/src/audience.ts +6 -0
- package/src/connectionManager.ts +7 -12
- package/src/container.ts +78 -116
- package/src/debugLogger.ts +113 -0
- package/src/deltaManager.ts +28 -4
- package/src/loader.ts +16 -7
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +33 -1
- package/src/quorum.ts +0 -10
package/src/container.ts
CHANGED
|
@@ -62,7 +62,6 @@ import {
|
|
|
62
62
|
runWithRetry,
|
|
63
63
|
isCombinedAppAndProtocolSummary,
|
|
64
64
|
MessageType2,
|
|
65
|
-
canBeCoalescedByService,
|
|
66
65
|
} from "@fluidframework/driver-utils";
|
|
67
66
|
import { IQuorumSnapshot } from "@fluidframework/protocol-base";
|
|
68
67
|
import {
|
|
@@ -85,17 +84,17 @@ import {
|
|
|
85
84
|
SummaryType,
|
|
86
85
|
} from "@fluidframework/protocol-definitions";
|
|
87
86
|
import {
|
|
88
|
-
|
|
87
|
+
createChildLogger,
|
|
89
88
|
EventEmitterWithErrorHandling,
|
|
90
89
|
PerformanceEvent,
|
|
91
90
|
raiseConnectedEvent,
|
|
92
|
-
TelemetryLogger,
|
|
93
91
|
connectedEventName,
|
|
94
92
|
normalizeError,
|
|
95
93
|
MonitoringContext,
|
|
96
|
-
|
|
94
|
+
createChildMonitoringContext,
|
|
97
95
|
wrapError,
|
|
98
96
|
ITelemetryLoggerExt,
|
|
97
|
+
formatTick,
|
|
99
98
|
} from "@fluidframework/telemetry-utils";
|
|
100
99
|
import { Audience } from "./audience";
|
|
101
100
|
import { ContainerContext } from "./containerContext";
|
|
@@ -111,7 +110,7 @@ import {
|
|
|
111
110
|
} from "./containerStorageAdapter";
|
|
112
111
|
import { IConnectionStateHandler, createConnectionStateHandler } from "./connectionStateHandler";
|
|
113
112
|
import { getProtocolSnapshotTree, getSnapshotTreeFromSerializedContainer } from "./utils";
|
|
114
|
-
import { initQuorumValuesFromCodeDetails
|
|
113
|
+
import { initQuorumValuesFromCodeDetails } from "./quorum";
|
|
115
114
|
import { NoopHeuristic } from "./noopHeuristic";
|
|
116
115
|
import { ConnectionManager } from "./connectionManager";
|
|
117
116
|
import { ConnectionState } from "./connectionState";
|
|
@@ -473,7 +472,7 @@ export class Container
|
|
|
473
472
|
private readonly codeLoader: ICodeDetailsLoader;
|
|
474
473
|
private readonly options: ILoaderOptions;
|
|
475
474
|
private readonly scope: FluidObject;
|
|
476
|
-
private readonly subLogger:
|
|
475
|
+
private readonly subLogger: ITelemetryLoggerExt;
|
|
477
476
|
private readonly detachedBlobStorage: IDetachedBlobStorage | undefined;
|
|
478
477
|
private readonly protocolHandlerBuilder: ProtocolHandlerBuilder;
|
|
479
478
|
|
|
@@ -523,13 +522,14 @@ export class Container
|
|
|
523
522
|
|
|
524
523
|
public get closed(): boolean {
|
|
525
524
|
return (
|
|
526
|
-
this._lifecycleState === "closing" ||
|
|
527
|
-
this._lifecycleState === "closed" ||
|
|
528
|
-
this._lifecycleState === "disposing" ||
|
|
529
|
-
this._lifecycleState === "disposed"
|
|
525
|
+
this._lifecycleState === "closing" || this._lifecycleState === "closed" || this.disposed
|
|
530
526
|
);
|
|
531
527
|
}
|
|
532
528
|
|
|
529
|
+
public get disposed(): boolean {
|
|
530
|
+
return this._lifecycleState === "disposing" || this._lifecycleState === "disposed";
|
|
531
|
+
}
|
|
532
|
+
|
|
533
533
|
private _attachState = AttachState.Detached;
|
|
534
534
|
|
|
535
535
|
private readonly storageAdapter: ContainerStorageAdapter;
|
|
@@ -556,7 +556,6 @@ export class Container
|
|
|
556
556
|
private inboundQueuePausedFromInit = true;
|
|
557
557
|
private firstConnection = true;
|
|
558
558
|
private readonly connectionTransitionTimes: number[] = [];
|
|
559
|
-
private messageCountAfterDisconnection: number = 0;
|
|
560
559
|
private _loadedFromVersion: IVersion | undefined;
|
|
561
560
|
private attachStarted = false;
|
|
562
561
|
private _dirtyContainer = false;
|
|
@@ -748,7 +747,19 @@ export class Container
|
|
|
748
747
|
this.scope = scope;
|
|
749
748
|
this.detachedBlobStorage = detachedBlobStorage;
|
|
750
749
|
this.protocolHandlerBuilder =
|
|
751
|
-
protocolHandlerBuilder ??
|
|
750
|
+
protocolHandlerBuilder ??
|
|
751
|
+
((
|
|
752
|
+
attributes: IDocumentAttributes,
|
|
753
|
+
quorumSnapshot: IQuorumSnapshot,
|
|
754
|
+
sendProposal: (key: string, value: any) => number,
|
|
755
|
+
) =>
|
|
756
|
+
new ProtocolHandler(
|
|
757
|
+
attributes,
|
|
758
|
+
quorumSnapshot,
|
|
759
|
+
sendProposal,
|
|
760
|
+
new Audience(),
|
|
761
|
+
(clientId: string) => this.clientsWhoShouldHaveLeft.has(clientId),
|
|
762
|
+
));
|
|
752
763
|
|
|
753
764
|
// Note that we capture the createProps here so we can replicate the creation call when we want to clone.
|
|
754
765
|
this.clone = async (
|
|
@@ -769,42 +780,45 @@ export class Container
|
|
|
769
780
|
}`;
|
|
770
781
|
// Need to use the property getter for docId because for detached flow we don't have the docId initially.
|
|
771
782
|
// We assign the id later so property getter is used.
|
|
772
|
-
this.subLogger =
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
//
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
this.deltaManager?.lastMessage?.
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
783
|
+
this.subLogger = createChildLogger({
|
|
784
|
+
logger: subLogger,
|
|
785
|
+
properties: {
|
|
786
|
+
all: {
|
|
787
|
+
clientType, // Differentiating summarizer container from main container
|
|
788
|
+
containerId: uuid(),
|
|
789
|
+
docId: () => this.resolvedUrl?.id,
|
|
790
|
+
containerAttachState: () => this._attachState,
|
|
791
|
+
containerLifecycleState: () => this._lifecycleState,
|
|
792
|
+
containerConnectionState: () => ConnectionState[this.connectionState],
|
|
793
|
+
serializedContainer: pendingLocalState !== undefined,
|
|
794
|
+
},
|
|
795
|
+
// we need to be judicious with our logging here to avoid generating too much data
|
|
796
|
+
// all data logged here should be broadly applicable, and not specific to a
|
|
797
|
+
// specific error or class of errors
|
|
798
|
+
error: {
|
|
799
|
+
// load information to associate errors with the specific load point
|
|
800
|
+
dmInitialSeqNumber: () => this._deltaManager?.initialSequenceNumber,
|
|
801
|
+
dmLastProcessedSeqNumber: () => this._deltaManager?.lastSequenceNumber,
|
|
802
|
+
dmLastKnownSeqNumber: () => this._deltaManager?.lastKnownSeqNumber,
|
|
803
|
+
containerLoadedFromVersionId: () => this._loadedFromVersion?.id,
|
|
804
|
+
containerLoadedFromVersionDate: () => this._loadedFromVersion?.date,
|
|
805
|
+
// message information to associate errors with the specific execution state
|
|
806
|
+
// dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
|
|
807
|
+
dmLastMsqSeqNumber: () => this.deltaManager?.lastMessage?.sequenceNumber,
|
|
808
|
+
dmLastMsqSeqTimestamp: () => this.deltaManager?.lastMessage?.timestamp,
|
|
809
|
+
dmLastMsqSeqClientId: () =>
|
|
810
|
+
this.deltaManager?.lastMessage?.clientId === null
|
|
811
|
+
? "null"
|
|
812
|
+
: this.deltaManager?.lastMessage?.clientId,
|
|
813
|
+
dmLastMsgClientSeq: () => this.deltaManager?.lastMessage?.clientSequenceNumber,
|
|
814
|
+
connectionStateDuration: () =>
|
|
815
|
+
performance.now() - this.connectionTransitionTimes[this.connectionState],
|
|
816
|
+
},
|
|
803
817
|
},
|
|
804
818
|
});
|
|
805
819
|
|
|
806
820
|
// Prefix all events in this file with container-loader
|
|
807
|
-
this.mc =
|
|
821
|
+
this.mc = createChildMonitoringContext({ logger: this.subLogger, namespace: "Container" });
|
|
808
822
|
|
|
809
823
|
this._deltaManager = this.createDeltaManager();
|
|
810
824
|
|
|
@@ -900,8 +914,8 @@ export class Container
|
|
|
900
914
|
document !== null &&
|
|
901
915
|
typeof document.addEventListener === "function" &&
|
|
902
916
|
document.addEventListener !== null;
|
|
903
|
-
// keep track of last time page was visible for telemetry
|
|
904
|
-
if (isDomAvailable) {
|
|
917
|
+
// keep track of last time page was visible for telemetry (on interactive clients only)
|
|
918
|
+
if (isDomAvailable && interactive) {
|
|
905
919
|
this.lastVisible = document.hidden ? performance.now() : undefined;
|
|
906
920
|
this.visibilityEventHandler = () => {
|
|
907
921
|
if (document.hidden) {
|
|
@@ -1565,8 +1579,7 @@ export class Container
|
|
|
1565
1579
|
await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
|
|
1566
1580
|
|
|
1567
1581
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1568
|
-
await this.
|
|
1569
|
-
true, // existing
|
|
1582
|
+
await this.instantiateRuntime(
|
|
1570
1583
|
codeDetails,
|
|
1571
1584
|
snapshot,
|
|
1572
1585
|
pendingLocalState?.pendingRuntimeState,
|
|
@@ -1654,7 +1667,7 @@ export class Container
|
|
|
1654
1667
|
};
|
|
1655
1668
|
}
|
|
1656
1669
|
|
|
1657
|
-
private async createDetached(
|
|
1670
|
+
private async createDetached(codeDetails: IFluidCodeDetails) {
|
|
1658
1671
|
const attributes: IDocumentAttributes = {
|
|
1659
1672
|
sequenceNumber: detachedContainerRefSeqNumber,
|
|
1660
1673
|
term: OnlyValidTermValue,
|
|
@@ -1664,7 +1677,7 @@ export class Container
|
|
|
1664
1677
|
await this.attachDeltaManagerOpHandler(attributes);
|
|
1665
1678
|
|
|
1666
1679
|
// Need to just seed the source data in the code quorum. Quorum itself is empty
|
|
1667
|
-
const qValues = initQuorumValuesFromCodeDetails(
|
|
1680
|
+
const qValues = initQuorumValuesFromCodeDetails(codeDetails);
|
|
1668
1681
|
this.initializeProtocolState(
|
|
1669
1682
|
attributes,
|
|
1670
1683
|
{
|
|
@@ -1674,10 +1687,7 @@ export class Container
|
|
|
1674
1687
|
}, // IQuorumSnapShot
|
|
1675
1688
|
);
|
|
1676
1689
|
|
|
1677
|
-
|
|
1678
|
-
await this.instantiateContextDetached(
|
|
1679
|
-
false, // existing
|
|
1680
|
-
);
|
|
1690
|
+
await this.instantiateRuntime(codeDetails, undefined);
|
|
1681
1691
|
|
|
1682
1692
|
this.setLoaded();
|
|
1683
1693
|
}
|
|
@@ -1703,21 +1713,17 @@ export class Container
|
|
|
1703
1713
|
this.storageAdapter,
|
|
1704
1714
|
baseTree.blobs.quorumValues,
|
|
1705
1715
|
);
|
|
1706
|
-
const codeDetails = getCodeDetailsFromQuorumValues(qValues);
|
|
1707
1716
|
this.initializeProtocolState(
|
|
1708
1717
|
attributes,
|
|
1709
1718
|
{
|
|
1710
1719
|
members: [],
|
|
1711
1720
|
proposals: [],
|
|
1712
|
-
values:
|
|
1713
|
-
codeDetails !== undefined ? initQuorumValuesFromCodeDetails(codeDetails) : [],
|
|
1721
|
+
values: qValues,
|
|
1714
1722
|
}, // IQuorumSnapShot
|
|
1715
1723
|
);
|
|
1724
|
+
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1716
1725
|
|
|
1717
|
-
await this.
|
|
1718
|
-
true, // existing
|
|
1719
|
-
snapshotTree,
|
|
1720
|
-
);
|
|
1726
|
+
await this.instantiateRuntime(codeDetails, snapshotTree);
|
|
1721
1727
|
|
|
1722
1728
|
this.setLoaded();
|
|
1723
1729
|
}
|
|
@@ -1786,7 +1792,10 @@ export class Container
|
|
|
1786
1792
|
this.submitMessage(MessageType.Propose, JSON.stringify({ key, value })),
|
|
1787
1793
|
);
|
|
1788
1794
|
|
|
1789
|
-
const protocolLogger =
|
|
1795
|
+
const protocolLogger = createChildLogger({
|
|
1796
|
+
logger: this.subLogger,
|
|
1797
|
+
namespace: "ProtocolHandler",
|
|
1798
|
+
});
|
|
1790
1799
|
|
|
1791
1800
|
protocol.quorum.on("error", (error) => {
|
|
1792
1801
|
protocolLogger.sendErrorEvent(error);
|
|
@@ -1897,7 +1906,7 @@ export class Container
|
|
|
1897
1906
|
const serviceProvider = () => this.service;
|
|
1898
1907
|
const deltaManager = new DeltaManager<ConnectionManager>(
|
|
1899
1908
|
serviceProvider,
|
|
1900
|
-
|
|
1909
|
+
createChildLogger({ logger: this.subLogger, namespace: "DeltaManager" }),
|
|
1901
1910
|
() => this.activeConnection(),
|
|
1902
1911
|
(props: IConnectionManagerFactoryArgs) =>
|
|
1903
1912
|
new ConnectionManager(
|
|
@@ -1905,7 +1914,7 @@ export class Container
|
|
|
1905
1914
|
() => this.isDirty,
|
|
1906
1915
|
this.client,
|
|
1907
1916
|
this._canReconnect,
|
|
1908
|
-
|
|
1917
|
+
createChildLogger({ logger: this.subLogger, namespace: "ConnectionManager" }),
|
|
1909
1918
|
props,
|
|
1910
1919
|
),
|
|
1911
1920
|
);
|
|
@@ -2004,7 +2013,7 @@ export class Container
|
|
|
2004
2013
|
if (value === ConnectionState.Connected) {
|
|
2005
2014
|
durationFromDisconnected =
|
|
2006
2015
|
time - this.connectionTransitionTimes[ConnectionState.Disconnected];
|
|
2007
|
-
durationFromDisconnected =
|
|
2016
|
+
durationFromDisconnected = formatTick(durationFromDisconnected);
|
|
2008
2017
|
} else if (value === ConnectionState.CatchingUp) {
|
|
2009
2018
|
// This info is of most interesting while Catching Up.
|
|
2010
2019
|
checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
|
|
@@ -2038,6 +2047,7 @@ export class Container
|
|
|
2038
2047
|
: undefined,
|
|
2039
2048
|
checkpointSequenceNumber,
|
|
2040
2049
|
quorumSize: this._protocolHandler?.quorum.getMembers().size,
|
|
2050
|
+
isDirty: this.isDirty,
|
|
2041
2051
|
...this._deltaManager.connectionProps,
|
|
2042
2052
|
},
|
|
2043
2053
|
error,
|
|
@@ -2061,26 +2071,11 @@ export class Container
|
|
|
2061
2071
|
}
|
|
2062
2072
|
const state = this.connectionState === ConnectionState.Connected;
|
|
2063
2073
|
|
|
2064
|
-
const logOpsOnReconnect: boolean =
|
|
2065
|
-
this.connectionState === ConnectionState.Connected &&
|
|
2066
|
-
!this.firstConnection &&
|
|
2067
|
-
this.connectionMode === "write";
|
|
2068
|
-
if (logOpsOnReconnect) {
|
|
2069
|
-
this.messageCountAfterDisconnection = 0;
|
|
2070
|
-
}
|
|
2071
|
-
|
|
2072
2074
|
// Both protocol and context should not be undefined if we got so far.
|
|
2073
2075
|
|
|
2074
2076
|
this.setContextConnectedState(state, this.readOnlyInfo.readonly ?? false);
|
|
2075
2077
|
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
2076
2078
|
raiseConnectedEvent(this.mc.logger, this, state, this.clientId, disconnectedReason);
|
|
2077
|
-
|
|
2078
|
-
if (logOpsOnReconnect) {
|
|
2079
|
-
this.mc.logger.sendTelemetryEvent({
|
|
2080
|
-
eventName: "OpsSentOnReconnect",
|
|
2081
|
-
count: this.messageCountAfterDisconnection,
|
|
2082
|
-
});
|
|
2083
|
-
}
|
|
2084
2079
|
}
|
|
2085
2080
|
|
|
2086
2081
|
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
@@ -2156,7 +2151,6 @@ export class Container
|
|
|
2156
2151
|
return -1;
|
|
2157
2152
|
}
|
|
2158
2153
|
|
|
2159
|
-
this.messageCountAfterDisconnection += 1;
|
|
2160
2154
|
this.noopHeuristic?.notifyMessageSent();
|
|
2161
2155
|
return this._deltaManager.submit(
|
|
2162
2156
|
type,
|
|
@@ -2174,28 +2168,6 @@ export class Container
|
|
|
2174
2168
|
}
|
|
2175
2169
|
const local = this.clientId === message.clientId;
|
|
2176
2170
|
|
|
2177
|
-
// Check and report if we're getting messages from a clientId that we previously
|
|
2178
|
-
// flagged should have left, or from a client that's not in the quorum but should be
|
|
2179
|
-
if (message.clientId != null) {
|
|
2180
|
-
const client = this.protocolHandler.quorum.getMember(message.clientId);
|
|
2181
|
-
|
|
2182
|
-
if (client === undefined && message.type !== MessageType.ClientJoin) {
|
|
2183
|
-
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
2184
|
-
throw new Error("Remote message's clientId is missing from the quorum");
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2187
|
-
// Here checking canBeCoalescedByService is used as an approximation of "is benign to process despite being unexpected".
|
|
2188
|
-
// It's still not good to see these messages from unexpected clientIds, but since they don't harm the integrity of the
|
|
2189
|
-
// document we don't need to blow up aggressively.
|
|
2190
|
-
if (
|
|
2191
|
-
this.clientsWhoShouldHaveLeft.has(message.clientId) &&
|
|
2192
|
-
!canBeCoalescedByService(message)
|
|
2193
|
-
) {
|
|
2194
|
-
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
2195
|
-
throw new Error("Remote message's clientId already should have left");
|
|
2196
|
-
}
|
|
2197
|
-
}
|
|
2198
|
-
|
|
2199
2171
|
// Allow the protocol handler to process the message
|
|
2200
2172
|
const result = this.protocolHandler.processMessage(message, local);
|
|
2201
2173
|
|
|
@@ -2280,17 +2252,7 @@ export class Container
|
|
|
2280
2252
|
return { snapshot, versionId: version?.id };
|
|
2281
2253
|
}
|
|
2282
2254
|
|
|
2283
|
-
private async
|
|
2284
|
-
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
2285
|
-
if (codeDetails === undefined) {
|
|
2286
|
-
throw new Error("pkg should be provided in create flow!!");
|
|
2287
|
-
}
|
|
2288
|
-
|
|
2289
|
-
await this.instantiateContext(existing, codeDetails, snapshot);
|
|
2290
|
-
}
|
|
2291
|
-
|
|
2292
|
-
private async instantiateContext(
|
|
2293
|
-
existing: boolean,
|
|
2255
|
+
private async instantiateRuntime(
|
|
2294
2256
|
codeDetails: IFluidCodeDetails,
|
|
2295
2257
|
snapshot: ISnapshotTree | undefined,
|
|
2296
2258
|
pendingLocalState?: unknown,
|
|
@@ -2327,6 +2289,8 @@ export class Container
|
|
|
2327
2289
|
(this.protocolHandler.quorum.get("code") ??
|
|
2328
2290
|
this.protocolHandler.quorum.get("code2")) as IFluidCodeDetails | undefined;
|
|
2329
2291
|
|
|
2292
|
+
const existing = snapshot !== undefined;
|
|
2293
|
+
|
|
2330
2294
|
const context = new ContainerContext(
|
|
2331
2295
|
this.options,
|
|
2332
2296
|
this.scope,
|
|
@@ -2371,8 +2335,6 @@ export class Container
|
|
|
2371
2335
|
this._lifecycleEvents.emit("runtimeInstantiated");
|
|
2372
2336
|
|
|
2373
2337
|
this._loadedCodeDetails = codeDetails;
|
|
2374
|
-
|
|
2375
|
-
this.emit("contextChanged", codeDetails);
|
|
2376
2338
|
}
|
|
2377
2339
|
|
|
2378
2340
|
private readonly updateDirtyContainerState = (dirty: boolean) => {
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ITelemetryBaseEvent,
|
|
8
|
+
ITelemetryBaseLogger,
|
|
9
|
+
ITelemetryProperties,
|
|
10
|
+
} from "@fluidframework/core-interfaces";
|
|
11
|
+
import { performance } from "@fluidframework/common-utils";
|
|
12
|
+
import { debug as registerDebug, IDebugger } from "debug";
|
|
13
|
+
import {
|
|
14
|
+
ITelemetryLoggerExt,
|
|
15
|
+
ITelemetryLoggerPropertyBags,
|
|
16
|
+
createMultiSinkLogger,
|
|
17
|
+
eventNamespaceSeparator,
|
|
18
|
+
formatTick,
|
|
19
|
+
} from "@fluidframework/telemetry-utils";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Implementation of debug logger
|
|
23
|
+
*/
|
|
24
|
+
export class DebugLogger implements ITelemetryBaseLogger {
|
|
25
|
+
/**
|
|
26
|
+
* Mix in debug logger with another logger.
|
|
27
|
+
* Returned logger will output events to both newly created debug logger, as well as base logger
|
|
28
|
+
* @param namespace - Telemetry event name prefix to add to all events
|
|
29
|
+
* @param properties - Base properties to add to all events
|
|
30
|
+
* @param propertyGetters - Getters to add additional properties to all events
|
|
31
|
+
* @param baseLogger - Base logger to output events (in addition to debug logger being created). Can be undefined.
|
|
32
|
+
*/
|
|
33
|
+
public static mixinDebugLogger(
|
|
34
|
+
namespace: string,
|
|
35
|
+
baseLogger?: ITelemetryBaseLogger,
|
|
36
|
+
properties?: ITelemetryLoggerPropertyBags,
|
|
37
|
+
): ITelemetryLoggerExt {
|
|
38
|
+
// Setup base logger upfront, such that host can disable it (if needed)
|
|
39
|
+
const debug = registerDebug(namespace);
|
|
40
|
+
|
|
41
|
+
// Create one for errors that is always enabled
|
|
42
|
+
// It can be silenced by replacing console.error if the debug namespace is not enabled.
|
|
43
|
+
const debugErr = registerDebug(namespace);
|
|
44
|
+
debugErr.log = function (...args) {
|
|
45
|
+
if (debug.enabled === true) {
|
|
46
|
+
// if the namespace is enabled, just use the default logger
|
|
47
|
+
registerDebug.log(...args);
|
|
48
|
+
} else {
|
|
49
|
+
// other wise, use the console logger (which could be replaced and silenced)
|
|
50
|
+
console.error(...args);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
debugErr.enabled = true;
|
|
54
|
+
|
|
55
|
+
return createMultiSinkLogger({
|
|
56
|
+
namespace,
|
|
57
|
+
loggers: [baseLogger, new DebugLogger(debug, debugErr)],
|
|
58
|
+
properties,
|
|
59
|
+
tryInheritProperties: true,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private constructor(private readonly debug: IDebugger, private readonly debugErr: IDebugger) {}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Send an event to debug loggers
|
|
67
|
+
*
|
|
68
|
+
* @param event - the event to send
|
|
69
|
+
*/
|
|
70
|
+
public send(event: ITelemetryBaseEvent): void {
|
|
71
|
+
const newEvent: ITelemetryProperties = { ...event };
|
|
72
|
+
const isError = newEvent.category === "error";
|
|
73
|
+
let logger = isError ? this.debugErr : this.debug;
|
|
74
|
+
|
|
75
|
+
// Use debug's coloring schema for base of the event
|
|
76
|
+
const index = event.eventName.lastIndexOf(eventNamespaceSeparator);
|
|
77
|
+
const name = event.eventName.substring(index + 1);
|
|
78
|
+
if (index > 0) {
|
|
79
|
+
logger = logger.extend(event.eventName.substring(0, index));
|
|
80
|
+
}
|
|
81
|
+
newEvent.eventName = undefined;
|
|
82
|
+
|
|
83
|
+
let tick = "";
|
|
84
|
+
tick = `tick=${formatTick(performance.now())}`;
|
|
85
|
+
|
|
86
|
+
// Extract stack to put it last, but also to avoid escaping '\n' in it by JSON.stringify below
|
|
87
|
+
const stack = newEvent.stack ?? "";
|
|
88
|
+
newEvent.stack = undefined;
|
|
89
|
+
|
|
90
|
+
// Watch out for circular references - they can come from two sources
|
|
91
|
+
// 1) error object - we do not control it and should remove it and retry
|
|
92
|
+
// 2) properties supplied by telemetry caller - that's a bug that should be addressed!
|
|
93
|
+
let payload: string;
|
|
94
|
+
try {
|
|
95
|
+
payload = JSON.stringify(newEvent);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
newEvent.error = undefined;
|
|
98
|
+
payload = JSON.stringify(newEvent);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (payload === "{}") {
|
|
102
|
+
payload = "";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Force errors out, to help with diagnostics
|
|
106
|
+
if (isError) {
|
|
107
|
+
logger.enabled = true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Print multi-line.
|
|
111
|
+
logger(`${name} ${payload} ${tick} ${stack}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
package/src/deltaManager.ts
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { default as AbortController } from "abort-controller";
|
|
7
6
|
import { v4 as uuid } from "uuid";
|
|
8
7
|
import { IEventProvider } from "@fluidframework/common-definitions";
|
|
9
8
|
import { ITelemetryProperties, ITelemetryErrorEvent } from "@fluidframework/core-interfaces";
|
|
@@ -93,6 +92,17 @@ function isClientMessage(message: ISequencedDocumentMessage | IDocumentMessage):
|
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Type is used to cast AbortController to represent new version of DOM API and prevent build issues
|
|
97
|
+
* TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
|
|
98
|
+
*/
|
|
99
|
+
type AbortControllerReal = AbortController & { abort(reason?: any): void };
|
|
100
|
+
/**
|
|
101
|
+
* Type is used to cast AbortSignal to represent new version of DOM API and prevent build issues
|
|
102
|
+
* TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
|
|
103
|
+
*/
|
|
104
|
+
type AbortSignalReal = AbortSignal & { reason: any };
|
|
105
|
+
|
|
96
106
|
/**
|
|
97
107
|
* Manages the flow of both inbound and outbound messages. This class ensures that shared objects receive delta
|
|
98
108
|
* messages in order regardless of possible network conditions or timings causing out of order delivery.
|
|
@@ -624,7 +634,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
624
634
|
// This is useless for known ranges (to is defined) as it means request is over either way.
|
|
625
635
|
// And it will cancel unbound request too early, not allowing us to learn where the end of the file is.
|
|
626
636
|
if (!opsFromFetch && cancelFetch(op)) {
|
|
627
|
-
|
|
637
|
+
// TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
|
|
638
|
+
(controller as AbortControllerReal).abort("DeltaManager getDeltas fetch cancelled");
|
|
628
639
|
this._inbound.off("push", opListener);
|
|
629
640
|
}
|
|
630
641
|
};
|
|
@@ -632,7 +643,11 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
632
643
|
try {
|
|
633
644
|
this._inbound.on("push", opListener);
|
|
634
645
|
assert(this.closeAbortController.signal.onabort === null, 0x1e8 /* "reentrancy" */);
|
|
635
|
-
this.closeAbortController.signal.onabort = () =>
|
|
646
|
+
this.closeAbortController.signal.onabort = () =>
|
|
647
|
+
// TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
|
|
648
|
+
(controller as AbortControllerReal).abort(
|
|
649
|
+
(this.closeAbortController.signal as AbortSignalReal).reason,
|
|
650
|
+
);
|
|
636
651
|
|
|
637
652
|
const stream = this.deltaStorage.fetchMessages(
|
|
638
653
|
from, // inclusive
|
|
@@ -656,6 +671,14 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
656
671
|
}
|
|
657
672
|
}
|
|
658
673
|
} finally {
|
|
674
|
+
if (controller.signal.aborted) {
|
|
675
|
+
this.logger.sendTelemetryEvent({
|
|
676
|
+
eventName: "DeltaManager_GetDeltasAborted",
|
|
677
|
+
fetchReason,
|
|
678
|
+
// TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
|
|
679
|
+
reason: (controller.signal as AbortSignalReal).reason,
|
|
680
|
+
});
|
|
681
|
+
}
|
|
659
682
|
this.closeAbortController.signal.onabort = null;
|
|
660
683
|
this._inbound.off("push", opListener);
|
|
661
684
|
assert(!opsFromFetch, 0x289 /* "logic error" */);
|
|
@@ -710,7 +733,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
710
733
|
}
|
|
711
734
|
|
|
712
735
|
private clearQueues() {
|
|
713
|
-
|
|
736
|
+
// TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
|
|
737
|
+
(this.closeAbortController as AbortControllerReal).abort("DeltaManager is closed");
|
|
714
738
|
|
|
715
739
|
this._inbound.clear();
|
|
716
740
|
this._inboundSignal.clear();
|
package/src/loader.ts
CHANGED
|
@@ -6,14 +6,12 @@
|
|
|
6
6
|
import { v4 as uuid } from "uuid";
|
|
7
7
|
import {
|
|
8
8
|
ITelemetryLoggerExt,
|
|
9
|
-
ChildLogger,
|
|
10
|
-
DebugLogger,
|
|
11
9
|
IConfigProviderBase,
|
|
12
|
-
loggerToMonitoringContext,
|
|
13
10
|
mixinMonitoringContext,
|
|
14
11
|
MonitoringContext,
|
|
15
12
|
PerformanceEvent,
|
|
16
13
|
sessionStorageConfigProvider,
|
|
14
|
+
createChildMonitoringContext,
|
|
17
15
|
} from "@fluidframework/telemetry-utils";
|
|
18
16
|
import {
|
|
19
17
|
ITelemetryBaseLogger,
|
|
@@ -44,6 +42,7 @@ import { Container, IPendingContainerState } from "./container";
|
|
|
44
42
|
import { IParsedUrl, parseUrl } from "./utils";
|
|
45
43
|
import { pkgVersion } from "./packageVersion";
|
|
46
44
|
import { ProtocolHandlerBuilder } from "./protocol";
|
|
45
|
+
import { DebugLogger } from "./debugLogger";
|
|
47
46
|
|
|
48
47
|
function canUseCache(request: IRequest): boolean {
|
|
49
48
|
if (request.headers === undefined) {
|
|
@@ -343,7 +342,10 @@ export class Loader implements IHostLoader {
|
|
|
343
342
|
protocolHandlerBuilder,
|
|
344
343
|
subLogger: subMc.logger,
|
|
345
344
|
};
|
|
346
|
-
this.mc =
|
|
345
|
+
this.mc = createChildMonitoringContext({
|
|
346
|
+
logger: this.services.subLogger,
|
|
347
|
+
namespace: "Loader",
|
|
348
|
+
});
|
|
347
349
|
}
|
|
348
350
|
|
|
349
351
|
public get IFluidRouter(): IFluidRouter {
|
|
@@ -407,16 +409,23 @@ export class Loader implements IHostLoader {
|
|
|
407
409
|
this.containers.set(key, containerP);
|
|
408
410
|
containerP
|
|
409
411
|
.then((container) => {
|
|
410
|
-
// If the container is closed or becomes closed after we resolve it,
|
|
411
|
-
|
|
412
|
+
// If the container is closed/disposed or becomes closed/disposed after we resolve it,
|
|
413
|
+
// remove it from the cache.
|
|
414
|
+
if (container.closed || container.disposed) {
|
|
412
415
|
this.containers.delete(key);
|
|
413
416
|
} else {
|
|
414
417
|
container.once("closed", () => {
|
|
415
418
|
this.containers.delete(key);
|
|
416
419
|
});
|
|
420
|
+
container.once("disposed", () => {
|
|
421
|
+
this.containers.delete(key);
|
|
422
|
+
});
|
|
417
423
|
}
|
|
418
424
|
})
|
|
419
|
-
.catch((error) => {
|
|
425
|
+
.catch((error) => {
|
|
426
|
+
// If an error occured while resolving the container request, then remove it from the cache.
|
|
427
|
+
this.containers.delete(key);
|
|
428
|
+
});
|
|
420
429
|
}
|
|
421
430
|
|
|
422
431
|
private async resolveCore(
|
package/src/packageVersion.ts
CHANGED