@fluidframework/container-loader 2.0.0-internal.5.3.2 → 2.0.0-internal.5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +6 -3
  3. package/dist/audience.d.ts +1 -0
  4. package/dist/audience.d.ts.map +1 -1
  5. package/dist/audience.js +3 -1
  6. package/dist/audience.js.map +1 -1
  7. package/dist/connectionManager.d.ts.map +1 -1
  8. package/dist/connectionManager.js +6 -11
  9. package/dist/connectionManager.js.map +1 -1
  10. package/dist/container.d.ts +2 -3
  11. package/dist/container.d.ts.map +1 -1
  12. package/dist/container.js +57 -75
  13. package/dist/container.js.map +1 -1
  14. package/dist/debugLogger.d.ts +30 -0
  15. package/dist/debugLogger.d.ts.map +1 -0
  16. package/dist/debugLogger.js +96 -0
  17. package/dist/debugLogger.js.map +1 -0
  18. package/dist/deltaManager.d.ts +0 -1
  19. package/dist/deltaManager.d.ts.map +1 -1
  20. package/dist/deltaManager.js +17 -9
  21. package/dist/deltaManager.js.map +1 -1
  22. package/dist/loader.d.ts.map +1 -1
  23. package/dist/loader.js +16 -5
  24. package/dist/loader.js.map +1 -1
  25. package/dist/packageVersion.d.ts +1 -1
  26. package/dist/packageVersion.js +1 -1
  27. package/dist/packageVersion.js.map +1 -1
  28. package/dist/quorum.d.ts +4 -1
  29. package/dist/quorum.d.ts.map +1 -1
  30. package/dist/quorum.js +1 -13
  31. package/dist/quorum.js.map +1 -1
  32. package/lib/audience.d.ts +1 -0
  33. package/lib/audience.d.ts.map +1 -1
  34. package/lib/audience.js +3 -1
  35. package/lib/audience.js.map +1 -1
  36. package/lib/connectionManager.d.ts.map +1 -1
  37. package/lib/connectionManager.js +7 -9
  38. package/lib/connectionManager.js.map +1 -1
  39. package/lib/container.d.ts +2 -3
  40. package/lib/container.d.ts.map +1 -1
  41. package/lib/container.js +59 -77
  42. package/lib/container.js.map +1 -1
  43. package/lib/debugLogger.d.ts +30 -0
  44. package/lib/debugLogger.d.ts.map +1 -0
  45. package/lib/debugLogger.js +92 -0
  46. package/lib/debugLogger.js.map +1 -0
  47. package/lib/deltaManager.d.ts +0 -1
  48. package/lib/deltaManager.d.ts.map +1 -1
  49. package/lib/deltaManager.js +15 -4
  50. package/lib/deltaManager.js.map +1 -1
  51. package/lib/loader.d.ts.map +1 -1
  52. package/lib/loader.js +16 -5
  53. package/lib/loader.js.map +1 -1
  54. package/lib/packageVersion.d.ts +1 -1
  55. package/lib/packageVersion.js +1 -1
  56. package/lib/packageVersion.js.map +1 -1
  57. package/lib/quorum.d.ts +4 -1
  58. package/lib/quorum.d.ts.map +1 -1
  59. package/lib/quorum.js +0 -11
  60. package/lib/quorum.js.map +1 -1
  61. package/package.json +12 -12
  62. package/src/audience.ts +6 -0
  63. package/src/connectionManager.ts +7 -12
  64. package/src/container.ts +65 -92
  65. package/src/debugLogger.ts +113 -0
  66. package/src/deltaManager.ts +28 -4
  67. package/src/loader.ts +16 -7
  68. package/src/packageVersion.ts +1 -1
  69. package/src/quorum.ts +0 -10
package/src/container.ts CHANGED
@@ -85,17 +85,17 @@ import {
85
85
  SummaryType,
86
86
  } from "@fluidframework/protocol-definitions";
87
87
  import {
88
- ChildLogger,
88
+ createChildLogger,
89
89
  EventEmitterWithErrorHandling,
90
90
  PerformanceEvent,
91
91
  raiseConnectedEvent,
92
- TelemetryLogger,
93
92
  connectedEventName,
94
93
  normalizeError,
95
94
  MonitoringContext,
96
- loggerToMonitoringContext,
95
+ createChildMonitoringContext,
97
96
  wrapError,
98
97
  ITelemetryLoggerExt,
98
+ formatTick,
99
99
  } from "@fluidframework/telemetry-utils";
100
100
  import { Audience } from "./audience";
101
101
  import { ContainerContext } from "./containerContext";
@@ -111,7 +111,7 @@ import {
111
111
  } from "./containerStorageAdapter";
112
112
  import { IConnectionStateHandler, createConnectionStateHandler } from "./connectionStateHandler";
113
113
  import { getProtocolSnapshotTree, getSnapshotTreeFromSerializedContainer } from "./utils";
114
- import { initQuorumValuesFromCodeDetails, getCodeDetailsFromQuorumValues } from "./quorum";
114
+ import { initQuorumValuesFromCodeDetails } from "./quorum";
115
115
  import { NoopHeuristic } from "./noopHeuristic";
116
116
  import { ConnectionManager } from "./connectionManager";
117
117
  import { ConnectionState } from "./connectionState";
@@ -473,7 +473,7 @@ export class Container
473
473
  private readonly codeLoader: ICodeDetailsLoader;
474
474
  private readonly options: ILoaderOptions;
475
475
  private readonly scope: FluidObject;
476
- private readonly subLogger: TelemetryLogger;
476
+ private readonly subLogger: ITelemetryLoggerExt;
477
477
  private readonly detachedBlobStorage: IDetachedBlobStorage | undefined;
478
478
  private readonly protocolHandlerBuilder: ProtocolHandlerBuilder;
479
479
 
@@ -523,13 +523,14 @@ export class Container
523
523
 
524
524
  public get closed(): boolean {
525
525
  return (
526
- this._lifecycleState === "closing" ||
527
- this._lifecycleState === "closed" ||
528
- this._lifecycleState === "disposing" ||
529
- this._lifecycleState === "disposed"
526
+ this._lifecycleState === "closing" || this._lifecycleState === "closed" || this.disposed
530
527
  );
531
528
  }
532
529
 
530
+ public get disposed(): boolean {
531
+ return this._lifecycleState === "disposing" || this._lifecycleState === "disposed";
532
+ }
533
+
533
534
  private _attachState = AttachState.Detached;
534
535
 
535
536
  private readonly storageAdapter: ContainerStorageAdapter;
@@ -556,7 +557,6 @@ export class Container
556
557
  private inboundQueuePausedFromInit = true;
557
558
  private firstConnection = true;
558
559
  private readonly connectionTransitionTimes: number[] = [];
559
- private messageCountAfterDisconnection: number = 0;
560
560
  private _loadedFromVersion: IVersion | undefined;
561
561
  private attachStarted = false;
562
562
  private _dirtyContainer = false;
@@ -769,42 +769,45 @@ export class Container
769
769
  }`;
770
770
  // Need to use the property getter for docId because for detached flow we don't have the docId initially.
771
771
  // We assign the id later so property getter is used.
772
- this.subLogger = ChildLogger.create(subLogger, undefined, {
773
- all: {
774
- clientType, // Differentiating summarizer container from main container
775
- containerId: uuid(),
776
- docId: () => this.resolvedUrl?.id,
777
- containerAttachState: () => this._attachState,
778
- containerLifecycleState: () => this._lifecycleState,
779
- containerConnectionState: () => ConnectionState[this.connectionState],
780
- serializedContainer: pendingLocalState !== undefined,
781
- },
782
- // we need to be judicious with our logging here to avoid generating too much data
783
- // all data logged here should be broadly applicable, and not specific to a
784
- // specific error or class of errors
785
- error: {
786
- // load information to associate errors with the specific load point
787
- dmInitialSeqNumber: () => this._deltaManager?.initialSequenceNumber,
788
- dmLastProcessedSeqNumber: () => this._deltaManager?.lastSequenceNumber,
789
- dmLastKnownSeqNumber: () => this._deltaManager?.lastKnownSeqNumber,
790
- containerLoadedFromVersionId: () => this._loadedFromVersion?.id,
791
- containerLoadedFromVersionDate: () => this._loadedFromVersion?.date,
792
- // message information to associate errors with the specific execution state
793
- // dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
794
- dmLastMsqSeqNumber: () => this.deltaManager?.lastMessage?.sequenceNumber,
795
- dmLastMsqSeqTimestamp: () => this.deltaManager?.lastMessage?.timestamp,
796
- dmLastMsqSeqClientId: () =>
797
- this.deltaManager?.lastMessage?.clientId === null
798
- ? "null"
799
- : this.deltaManager?.lastMessage?.clientId,
800
- dmLastMsgClientSeq: () => this.deltaManager?.lastMessage?.clientSequenceNumber,
801
- connectionStateDuration: () =>
802
- performance.now() - this.connectionTransitionTimes[this.connectionState],
772
+ this.subLogger = createChildLogger({
773
+ logger: subLogger,
774
+ properties: {
775
+ all: {
776
+ clientType, // Differentiating summarizer container from main container
777
+ containerId: uuid(),
778
+ docId: () => this.resolvedUrl?.id,
779
+ containerAttachState: () => this._attachState,
780
+ containerLifecycleState: () => this._lifecycleState,
781
+ containerConnectionState: () => ConnectionState[this.connectionState],
782
+ serializedContainer: pendingLocalState !== undefined,
783
+ },
784
+ // we need to be judicious with our logging here to avoid generating too much data
785
+ // all data logged here should be broadly applicable, and not specific to a
786
+ // specific error or class of errors
787
+ error: {
788
+ // load information to associate errors with the specific load point
789
+ dmInitialSeqNumber: () => this._deltaManager?.initialSequenceNumber,
790
+ dmLastProcessedSeqNumber: () => this._deltaManager?.lastSequenceNumber,
791
+ dmLastKnownSeqNumber: () => this._deltaManager?.lastKnownSeqNumber,
792
+ containerLoadedFromVersionId: () => this._loadedFromVersion?.id,
793
+ containerLoadedFromVersionDate: () => this._loadedFromVersion?.date,
794
+ // message information to associate errors with the specific execution state
795
+ // dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
796
+ dmLastMsqSeqNumber: () => this.deltaManager?.lastMessage?.sequenceNumber,
797
+ dmLastMsqSeqTimestamp: () => this.deltaManager?.lastMessage?.timestamp,
798
+ dmLastMsqSeqClientId: () =>
799
+ this.deltaManager?.lastMessage?.clientId === null
800
+ ? "null"
801
+ : this.deltaManager?.lastMessage?.clientId,
802
+ dmLastMsgClientSeq: () => this.deltaManager?.lastMessage?.clientSequenceNumber,
803
+ connectionStateDuration: () =>
804
+ performance.now() - this.connectionTransitionTimes[this.connectionState],
805
+ },
803
806
  },
804
807
  });
805
808
 
806
809
  // Prefix all events in this file with container-loader
807
- this.mc = loggerToMonitoringContext(ChildLogger.create(this.subLogger, "Container"));
810
+ this.mc = createChildMonitoringContext({ logger: this.subLogger, namespace: "Container" });
808
811
 
809
812
  this._deltaManager = this.createDeltaManager();
810
813
 
@@ -900,8 +903,8 @@ export class Container
900
903
  document !== null &&
901
904
  typeof document.addEventListener === "function" &&
902
905
  document.addEventListener !== null;
903
- // keep track of last time page was visible for telemetry
904
- if (isDomAvailable) {
906
+ // keep track of last time page was visible for telemetry (on interactive clients only)
907
+ if (isDomAvailable && interactive) {
905
908
  this.lastVisible = document.hidden ? performance.now() : undefined;
906
909
  this.visibilityEventHandler = () => {
907
910
  if (document.hidden) {
@@ -1565,8 +1568,7 @@ export class Container
1565
1568
  await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
1566
1569
 
1567
1570
  const codeDetails = this.getCodeDetailsFromQuorum();
1568
- await this.instantiateContext(
1569
- true, // existing
1571
+ await this.instantiateRuntime(
1570
1572
  codeDetails,
1571
1573
  snapshot,
1572
1574
  pendingLocalState?.pendingRuntimeState,
@@ -1654,7 +1656,7 @@ export class Container
1654
1656
  };
1655
1657
  }
1656
1658
 
1657
- private async createDetached(source: IFluidCodeDetails) {
1659
+ private async createDetached(codeDetails: IFluidCodeDetails) {
1658
1660
  const attributes: IDocumentAttributes = {
1659
1661
  sequenceNumber: detachedContainerRefSeqNumber,
1660
1662
  term: OnlyValidTermValue,
@@ -1664,7 +1666,7 @@ export class Container
1664
1666
  await this.attachDeltaManagerOpHandler(attributes);
1665
1667
 
1666
1668
  // Need to just seed the source data in the code quorum. Quorum itself is empty
1667
- const qValues = initQuorumValuesFromCodeDetails(source);
1669
+ const qValues = initQuorumValuesFromCodeDetails(codeDetails);
1668
1670
  this.initializeProtocolState(
1669
1671
  attributes,
1670
1672
  {
@@ -1674,10 +1676,7 @@ export class Container
1674
1676
  }, // IQuorumSnapShot
1675
1677
  );
1676
1678
 
1677
- // The load context - given we seeded the quorum - will be great
1678
- await this.instantiateContextDetached(
1679
- false, // existing
1680
- );
1679
+ await this.instantiateRuntime(codeDetails, undefined);
1681
1680
 
1682
1681
  this.setLoaded();
1683
1682
  }
@@ -1703,21 +1702,17 @@ export class Container
1703
1702
  this.storageAdapter,
1704
1703
  baseTree.blobs.quorumValues,
1705
1704
  );
1706
- const codeDetails = getCodeDetailsFromQuorumValues(qValues);
1707
1705
  this.initializeProtocolState(
1708
1706
  attributes,
1709
1707
  {
1710
1708
  members: [],
1711
1709
  proposals: [],
1712
- values:
1713
- codeDetails !== undefined ? initQuorumValuesFromCodeDetails(codeDetails) : [],
1710
+ values: qValues,
1714
1711
  }, // IQuorumSnapShot
1715
1712
  );
1713
+ const codeDetails = this.getCodeDetailsFromQuorum();
1716
1714
 
1717
- await this.instantiateContextDetached(
1718
- true, // existing
1719
- snapshotTree,
1720
- );
1715
+ await this.instantiateRuntime(codeDetails, snapshotTree);
1721
1716
 
1722
1717
  this.setLoaded();
1723
1718
  }
@@ -1786,7 +1781,10 @@ export class Container
1786
1781
  this.submitMessage(MessageType.Propose, JSON.stringify({ key, value })),
1787
1782
  );
1788
1783
 
1789
- const protocolLogger = ChildLogger.create(this.subLogger, "ProtocolHandler");
1784
+ const protocolLogger = createChildLogger({
1785
+ logger: this.subLogger,
1786
+ namespace: "ProtocolHandler",
1787
+ });
1790
1788
 
1791
1789
  protocol.quorum.on("error", (error) => {
1792
1790
  protocolLogger.sendErrorEvent(error);
@@ -1897,7 +1895,7 @@ export class Container
1897
1895
  const serviceProvider = () => this.service;
1898
1896
  const deltaManager = new DeltaManager<ConnectionManager>(
1899
1897
  serviceProvider,
1900
- ChildLogger.create(this.subLogger, "DeltaManager"),
1898
+ createChildLogger({ logger: this.subLogger, namespace: "DeltaManager" }),
1901
1899
  () => this.activeConnection(),
1902
1900
  (props: IConnectionManagerFactoryArgs) =>
1903
1901
  new ConnectionManager(
@@ -1905,7 +1903,7 @@ export class Container
1905
1903
  () => this.isDirty,
1906
1904
  this.client,
1907
1905
  this._canReconnect,
1908
- ChildLogger.create(this.subLogger, "ConnectionManager"),
1906
+ createChildLogger({ logger: this.subLogger, namespace: "ConnectionManager" }),
1909
1907
  props,
1910
1908
  ),
1911
1909
  );
@@ -2004,7 +2002,7 @@ export class Container
2004
2002
  if (value === ConnectionState.Connected) {
2005
2003
  durationFromDisconnected =
2006
2004
  time - this.connectionTransitionTimes[ConnectionState.Disconnected];
2007
- durationFromDisconnected = TelemetryLogger.formatTick(durationFromDisconnected);
2005
+ durationFromDisconnected = formatTick(durationFromDisconnected);
2008
2006
  } else if (value === ConnectionState.CatchingUp) {
2009
2007
  // This info is of most interesting while Catching Up.
2010
2008
  checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
@@ -2038,6 +2036,7 @@ export class Container
2038
2036
  : undefined,
2039
2037
  checkpointSequenceNumber,
2040
2038
  quorumSize: this._protocolHandler?.quorum.getMembers().size,
2039
+ isDirty: this.isDirty,
2041
2040
  ...this._deltaManager.connectionProps,
2042
2041
  },
2043
2042
  error,
@@ -2061,26 +2060,11 @@ export class Container
2061
2060
  }
2062
2061
  const state = this.connectionState === ConnectionState.Connected;
2063
2062
 
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
2063
  // Both protocol and context should not be undefined if we got so far.
2073
2064
 
2074
2065
  this.setContextConnectedState(state, this.readOnlyInfo.readonly ?? false);
2075
2066
  this.protocolHandler.setConnectionState(state, this.clientId);
2076
2067
  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
2068
  }
2085
2069
 
2086
2070
  // back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
@@ -2156,7 +2140,6 @@ export class Container
2156
2140
  return -1;
2157
2141
  }
2158
2142
 
2159
- this.messageCountAfterDisconnection += 1;
2160
2143
  this.noopHeuristic?.notifyMessageSent();
2161
2144
  return this._deltaManager.submit(
2162
2145
  type,
@@ -2280,17 +2263,7 @@ export class Container
2280
2263
  return { snapshot, versionId: version?.id };
2281
2264
  }
2282
2265
 
2283
- private async instantiateContextDetached(existing: boolean, snapshot?: ISnapshotTree) {
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,
2266
+ private async instantiateRuntime(
2294
2267
  codeDetails: IFluidCodeDetails,
2295
2268
  snapshot: ISnapshotTree | undefined,
2296
2269
  pendingLocalState?: unknown,
@@ -2327,6 +2300,8 @@ export class Container
2327
2300
  (this.protocolHandler.quorum.get("code") ??
2328
2301
  this.protocolHandler.quorum.get("code2")) as IFluidCodeDetails | undefined;
2329
2302
 
2303
+ const existing = snapshot !== undefined;
2304
+
2330
2305
  const context = new ContainerContext(
2331
2306
  this.options,
2332
2307
  this.scope,
@@ -2371,8 +2346,6 @@ export class Container
2371
2346
  this._lifecycleEvents.emit("runtimeInstantiated");
2372
2347
 
2373
2348
  this._loadedCodeDetails = codeDetails;
2374
-
2375
- this.emit("contextChanged", codeDetails);
2376
2349
  }
2377
2350
 
2378
2351
  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
+ }
@@ -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
- controller.abort();
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 = () => controller.abort();
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
- this.closeAbortController.abort();
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 = loggerToMonitoringContext(ChildLogger.create(this.services.subLogger, "Loader"));
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, remove it from the cache.
411
- if (container.closed) {
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(
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-loader";
9
- export const pkgVersion = "2.0.0-internal.5.3.2";
9
+ export const pkgVersion = "2.0.0-internal.5.4.0";
package/src/quorum.ts CHANGED
@@ -2,19 +2,9 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { assert } from "@fluidframework/common-utils";
6
5
  import { IFluidCodeDetails } from "@fluidframework/core-interfaces";
7
6
  import { ICommittedProposal } from "@fluidframework/protocol-definitions";
8
7
 
9
- export function getCodeDetailsFromQuorumValues(
10
- quorumValues: [string, ICommittedProposal][],
11
- ): IFluidCodeDetails {
12
- const qValuesMap = new Map(quorumValues);
13
- const proposal = qValuesMap.get("code");
14
- assert(proposal !== undefined, 0x2dc /* "Cannot find code proposal" */);
15
- return proposal?.value as IFluidCodeDetails;
16
- }
17
-
18
8
  export function initQuorumValuesFromCodeDetails(
19
9
  source: IFluidCodeDetails,
20
10
  ): [string, ICommittedProposal][] {