@fluidframework/container-loader 0.54.2 → 0.55.0-48551

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/lib/container.js CHANGED
@@ -12,7 +12,7 @@ import { DataCorruptionError, extractSafePropertiesFromMessage, GenericError, Us
12
12
  import { readAndParse, OnlineStatus, isOnline, ensureFluidResolvedUrl, combineAppAndProtocolSummary, runWithRetry, isFluidResolvedUrl, } from "@fluidframework/driver-utils";
13
13
  import { isSystemMessage, ProtocolOpHandler, } from "@fluidframework/protocol-base";
14
14
  import { FileMode, MessageType, TreeEntry, SummaryType, } from "@fluidframework/protocol-definitions";
15
- import { ChildLogger, EventEmitterWithErrorHandling, PerformanceEvent, raiseConnectedEvent, TelemetryLogger, connectedEventName, disconnectedEventName, normalizeError, } from "@fluidframework/telemetry-utils";
15
+ import { ChildLogger, EventEmitterWithErrorHandling, PerformanceEvent, raiseConnectedEvent, TelemetryLogger, connectedEventName, disconnectedEventName, normalizeError, loggerToMonitoringContext, } from "@fluidframework/telemetry-utils";
16
16
  import { Audience } from "./audience";
17
17
  import { ContainerContext } from "./containerContext";
18
18
  import { ReconnectMode } from "./contracts";
@@ -104,9 +104,9 @@ const getCodeProposal =
104
104
  (quorum) => { var _a; return (_a = quorum.get("code")) !== null && _a !== void 0 ? _a : quorum.get("code2"); };
105
105
  export class Container extends EventEmitterWithErrorHandling {
106
106
  constructor(loader, config) {
107
- var _a, _b;
107
+ var _a, _b, _c;
108
108
  super((name, error) => {
109
- this.logger.sendErrorEvent({
109
+ this.mc.logger.sendErrorEvent({
110
110
  eventName: "ContainerEventHandlerException",
111
111
  name: typeof name === "string" ? name : undefined,
112
112
  }, error);
@@ -147,7 +147,7 @@ export class Container extends EventEmitterWithErrorHandling {
147
147
  containerLifecycleState: () => this._lifecycleState,
148
148
  containerConnectionState: () => ConnectionState[this.connectionState],
149
149
  },
150
- // we need to be judicious with our logging here to avoid generting too much data
150
+ // we need to be judicious with our logging here to avoid generating too much data
151
151
  // all data logged here should be broadly applicable, and not specific to a
152
152
  // specific error or class of errors
153
153
  error: {
@@ -160,12 +160,13 @@ export class Container extends EventEmitterWithErrorHandling {
160
160
  dmLastMsqSeqNumber: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.sequenceNumber; },
161
161
  dmLastMsqSeqTimestamp: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.timestamp; },
162
162
  dmLastMsqSeqClientId: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientId; },
163
- connectionState: () => ConnectionState[this.connectionState],
164
163
  connectionStateDuration: () => performance.now() - this.connectionTransitionTimes[this.connectionState],
165
164
  },
166
165
  });
167
166
  // Prefix all events in this file with container-loader
168
- this.logger = ChildLogger.create(this.subLogger, "Container");
167
+ this.mc = loggerToMonitoringContext(ChildLogger.create(this.subLogger, "Container"));
168
+ const summarizeProtocolTree = (_c = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree")) !== null && _c !== void 0 ? _c : this.loader.services.options.summarizeProtocolTree;
169
+ this.options = Object.assign(Object.assign({}, this.loader.services.options), { summarizeProtocolTree });
169
170
  this.connectionStateHandler = new ConnectionStateHandler({
170
171
  protocolHandler: () => this._protocolHandler,
171
172
  logConnectionStateChangeTelemetry: (value, oldState, reason) => this.logConnectionStateChangeTelemetry(value, oldState, reason),
@@ -184,14 +185,14 @@ export class Container extends EventEmitterWithErrorHandling {
184
185
  this.propagateConnectionState();
185
186
  }
186
187
  },
187
- }, this.logger);
188
+ }, this.mc.logger);
188
189
  this._deltaManager = this.createDeltaManager();
189
190
  this._storage = new ContainerStorageAdapter(() => {
190
191
  if (this.attachState !== AttachState.Attached) {
191
192
  if (this.loader.services.detachedBlobStorage !== undefined) {
192
- return new BlobOnlyStorage(this.loader.services.detachedBlobStorage, this.logger);
193
+ return new BlobOnlyStorage(this.loader.services.detachedBlobStorage, this.mc.logger);
193
194
  }
194
- this.logger.sendErrorEvent({
195
+ this.mc.logger.sendErrorEvent({
195
196
  eventName: "NoRealStorageInDetachedContainer",
196
197
  });
197
198
  throw new Error("Real storage calls not allowed in Unattached container");
@@ -205,7 +206,7 @@ export class Container extends EventEmitterWithErrorHandling {
205
206
  // keep track of last time page was visible for telemetry
206
207
  if (isDomAvailable) {
207
208
  this.lastVisible = document.hidden ? performance.now() : undefined;
208
- document.addEventListener("visibilitychange", () => {
209
+ this.visibilityEventHandler = () => {
209
210
  if (document.hidden) {
210
211
  this.lastVisible = performance.now();
211
212
  }
@@ -213,7 +214,8 @@ export class Container extends EventEmitterWithErrorHandling {
213
214
  // settimeout so this will hopefully fire after disconnect event if being hidden caused it
214
215
  setTimeout(() => this.lastVisible = undefined, 0);
215
216
  }
216
- });
217
+ };
218
+ document.addEventListener("visibilitychange", this.visibilityEventHandler);
217
219
  }
218
220
  // We observed that most users of platform do not check Container.connected event on load, causing bugs.
219
221
  // As such, we are raising events when new listener pops up.
@@ -246,7 +248,7 @@ export class Container extends EventEmitterWithErrorHandling {
246
248
  default:
247
249
  }
248
250
  }).catch((error) => {
249
- this.logger.sendErrorEvent({ eventName: "RaiseConnectedEventError" }, error);
251
+ this.mc.logger.sendErrorEvent({ eventName: "RaiseConnectedEventError" }, error);
250
252
  });
251
253
  });
252
254
  }
@@ -259,7 +261,7 @@ export class Container extends EventEmitterWithErrorHandling {
259
261
  resolvedUrl: loadOptions.resolvedUrl,
260
262
  canReconnect: loadOptions.canReconnect,
261
263
  });
262
- return PerformanceEvent.timedExecAsync(container.logger, { eventName: "Load" }, async (event) => new Promise((res, rej) => {
264
+ return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
263
265
  var _a;
264
266
  container._lifecycleState = "loading";
265
267
  const version = loadOptions.version;
@@ -269,7 +271,7 @@ export class Container extends EventEmitterWithErrorHandling {
269
271
  assert(pendingLocalState === undefined || loadOptions.loadMode === undefined, 0x1e1 /* "pending state requires immediate connection!" */);
270
272
  const mode = (_a = loadOptions.loadMode) !== null && _a !== void 0 ? _a : defaultMode;
271
273
  const onClosed = (err) => {
272
- rej(err !== null && err !== void 0 ? err : new GenericError("containerClosedWithoutErrorDuringLoad"));
274
+ reject(err !== null && err !== void 0 ? err : new GenericError("containerClosedWithoutErrorDuringLoad"));
273
275
  };
274
276
  container.on("closed", onClosed);
275
277
  container.load(version, mode, pendingLocalState)
@@ -278,7 +280,7 @@ export class Container extends EventEmitterWithErrorHandling {
278
280
  })
279
281
  .then((props) => {
280
282
  event.end(Object.assign(Object.assign({}, props), loadOptions.loadMode));
281
- res(container);
283
+ resolve(container);
282
284
  }, (error) => {
283
285
  const err = normalizeError(error);
284
286
  // Depending where error happens, we can be attempting to connect to web socket
@@ -294,7 +296,7 @@ export class Container extends EventEmitterWithErrorHandling {
294
296
  */
295
297
  static async createDetached(loader, codeDetails) {
296
298
  const container = new Container(loader, {});
297
- return PerformanceEvent.timedExecAsync(container.logger, { eventName: "CreateDetached" }, async (_event) => {
299
+ return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "CreateDetached" }, async (_event) => {
298
300
  container._lifecycleState = "loading";
299
301
  await container.createDetached(codeDetails);
300
302
  return container;
@@ -306,7 +308,7 @@ export class Container extends EventEmitterWithErrorHandling {
306
308
  */
307
309
  static async rehydrateDetachedFromSnapshot(loader, snapshot) {
308
310
  const container = new Container(loader, {});
309
- return PerformanceEvent.timedExecAsync(container.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
311
+ return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
310
312
  const deserializedSummary = JSON.parse(snapshot);
311
313
  container._lifecycleState = "loading";
312
314
  await container.rehydrateDetachedFromSnapshot(deserializedSummary);
@@ -445,7 +447,6 @@ export class Container extends EventEmitterWithErrorHandling {
445
447
  }
446
448
  get serviceFactory() { return this.loader.services.documentServiceFactory; }
447
449
  get urlResolver() { return this.loader.services.urlResolver; }
448
- get options() { return this.loader.services.options; }
449
450
  get scope() { return this.loader.services.scope; }
450
451
  get codeLoader() { return this.loader.services.codeLoader; }
451
452
  /**
@@ -475,14 +476,17 @@ export class Container extends EventEmitterWithErrorHandling {
475
476
  (_d = this.service) === null || _d === void 0 ? void 0 : _d.dispose(error);
476
477
  }
477
478
  catch (exception) {
478
- this.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
479
+ this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
479
480
  }
480
- this.logger.sendTelemetryEvent({
481
+ this.mc.logger.sendTelemetryEvent({
481
482
  eventName: "ContainerClose",
482
483
  category: error === undefined ? "generic" : "error",
483
484
  }, error);
484
485
  this.emit("closed", error);
485
486
  this.removeAllListeners();
487
+ if (this.visibilityEventHandler !== undefined) {
488
+ document.removeEventListener("visibilitychange", this.visibilityEventHandler);
489
+ }
486
490
  }
487
491
  finally {
488
492
  this._lifecycleState = "closed";
@@ -515,7 +519,7 @@ export class Container extends EventEmitterWithErrorHandling {
515
519
  return JSON.stringify(combinedSummary);
516
520
  }
517
521
  async attach(request) {
518
- await PerformanceEvent.timedExecAsync(this.logger, { eventName: "Attach" }, async () => {
522
+ await PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
519
523
  if (this._lifecycleState !== "loaded") {
520
524
  throw new UsageError(`containerNotValidForAttach [${this._lifecycleState}]`);
521
525
  }
@@ -545,7 +549,7 @@ export class Container extends EventEmitterWithErrorHandling {
545
549
  const createNewResolvedUrl = await this.urlResolver.resolve(request);
546
550
  ensureFluidResolvedUrl(createNewResolvedUrl);
547
551
  if (this.service === undefined) {
548
- this.service = await runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger), "containerAttach", this.logger, {});
552
+ this.service = await runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger), "containerAttach", this.mc.logger, {});
549
553
  }
550
554
  const resolvedUrl = this.service.resolvedUrl;
551
555
  ensureFluidResolvedUrl(resolvedUrl);
@@ -599,12 +603,12 @@ export class Container extends EventEmitterWithErrorHandling {
599
603
  }, { start: true, end: true, cancel: "generic" });
600
604
  }
601
605
  async request(path) {
602
- return PerformanceEvent.timedExecAsync(this.logger, { eventName: "Request" }, async () => this.context.request(path), { end: true, cancel: "error" });
606
+ return PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.context.request(path), { end: true, cancel: "error" });
603
607
  }
604
608
  async snapshot(tagMessage, fullTree = false) {
605
609
  // Only snapshot once a code quorum has been established
606
610
  if (!this.protocolHandler.quorum.has("code") && !this.protocolHandler.quorum.has("code2")) {
607
- this.logger.sendTelemetryEvent({ eventName: "SkipSnapshot" });
611
+ this.mc.logger.sendTelemetryEvent({ eventName: "SkipSnapshot" });
608
612
  return;
609
613
  }
610
614
  // Stop inbound message processing while we complete the snapshot
@@ -613,7 +617,7 @@ export class Container extends EventEmitterWithErrorHandling {
613
617
  await this.snapshotCore(tagMessage, fullTree);
614
618
  }
615
619
  catch (ex) {
616
- this.logger.sendErrorEvent({ eventName: "SnapshotExceptionError" }, ex);
620
+ this.mc.logger.sendErrorEvent({ eventName: "SnapshotExceptionError" }, ex);
617
621
  throw ex;
618
622
  }
619
623
  finally {
@@ -632,7 +636,7 @@ export class Container extends EventEmitterWithErrorHandling {
632
636
  const now = performance.now();
633
637
  const duration = now - this.setAutoReconnectTime;
634
638
  this.setAutoReconnectTime = now;
635
- this.logger.sendTelemetryEvent({
639
+ this.mc.logger.sendTelemetryEvent({
636
640
  eventName: reconnect ? "AutoReconnectEnabled" : "AutoReconnectDisabled",
637
641
  connectionMode: this.connectionMode,
638
642
  connectionState: ConnectionState[this.connectionState],
@@ -694,12 +698,12 @@ export class Container extends EventEmitterWithErrorHandling {
694
698
  throw new Error("Provided codeDetails are not IFluidCodeDetails");
695
699
  }
696
700
  if (this.codeLoader.IFluidCodeDetailsComparer) {
697
- const comparision = await this.codeLoader.IFluidCodeDetailsComparer.compare(codeDetails, this.getCodeDetailsFromQuorum());
698
- if (comparision !== undefined && comparision <= 0) {
701
+ const comparison = await this.codeLoader.IFluidCodeDetailsComparer.compare(codeDetails, this.getCodeDetailsFromQuorum());
702
+ if (comparison !== undefined && comparison <= 0) {
699
703
  throw new Error("Proposed code details should be greater than the current");
700
704
  }
701
705
  }
702
- return this.getQuorum().propose("code", codeDetails)
706
+ return this.protocolHandler.quorum.propose("code", codeDetails)
703
707
  .then(() => true)
704
708
  .catch(() => false);
705
709
  }
@@ -954,8 +958,9 @@ export class Container extends EventEmitterWithErrorHandling {
954
958
  assert(this.service !== undefined, 0x1ef /* "services must be defined" */);
955
959
  const storageService = await this.service.connectToStorage();
956
960
  this._storageService =
957
- new RetriableDocumentStorageService(storageService, this.logger);
961
+ new RetriableDocumentStorageService(storageService, this.mc.logger);
958
962
  if (this.options.summarizeProtocolTree === true) {
963
+ this.mc.logger.sendTelemetryEvent({ eventName: "summarizeProtocolTreeEnabled" });
959
964
  this._storageService =
960
965
  new ProtocolTreeStorageService(this._storageService, () => this.captureProtocolSummary());
961
966
  }
@@ -1017,7 +1022,7 @@ export class Container extends EventEmitterWithErrorHandling {
1017
1022
  protocol.quorum.on("approveProposal", (sequenceNumber, key, value) => {
1018
1023
  if (key === "code" || key === "code2") {
1019
1024
  if (!isFluidCodeDetails(value)) {
1020
- this.logger.sendErrorEvent({
1025
+ this.mc.logger.sendErrorEvent({
1021
1026
  eventName: "CodeProposalNotIFluidCodeDetails",
1022
1027
  });
1023
1028
  }
@@ -1171,7 +1176,7 @@ export class Container extends EventEmitterWithErrorHandling {
1171
1176
  connectionInitiationReason = "AutoReconnect";
1172
1177
  }
1173
1178
  }
1174
- this.logger.sendPerformanceEvent(Object.assign({ eventName: `ConnectionStateChange_${ConnectionState[value]}`, from: ConnectionState[oldState], duration,
1179
+ this.mc.logger.sendPerformanceEvent(Object.assign({ eventName: `ConnectionStateChange_${ConnectionState[value]}`, from: ConnectionState[oldState], duration,
1175
1180
  durationFromDisconnected,
1176
1181
  reason,
1177
1182
  connectionInitiationReason, pendingClientId: this.connectionStateHandler.pendingClientId, clientId: this.clientId, autoReconnect,
@@ -1194,9 +1199,9 @@ export class Container extends EventEmitterWithErrorHandling {
1194
1199
  }
1195
1200
  assert(this.protocolHandler !== undefined, 0x0dc /* "Protocol handler should be set here" */);
1196
1201
  this.protocolHandler.quorum.setConnectionState(state, this.clientId);
1197
- raiseConnectedEvent(this.logger, this, state, this.clientId);
1202
+ raiseConnectedEvent(this.mc.logger, this, state, this.clientId);
1198
1203
  if (logOpsOnReconnect) {
1199
- this.logger.sendTelemetryEvent({ eventName: "OpsSentOnReconnect", count: this.messageCountAfterDisconnection });
1204
+ this.mc.logger.sendTelemetryEvent({ eventName: "OpsSentOnReconnect", count: this.messageCountAfterDisconnection });
1200
1205
  }
1201
1206
  }
1202
1207
  submitContainerMessage(type, contents, batch, metadata) {
@@ -1226,7 +1231,7 @@ export class Container extends EventEmitterWithErrorHandling {
1226
1231
  }
1227
1232
  submitMessage(type, contents, batch, metadata) {
1228
1233
  if (this.connectionState !== ConnectionState.Connected) {
1229
- this.logger.sendErrorEvent({ eventName: "SubmitMessageWithNoConnection", type });
1234
+ this.mc.logger.sendErrorEvent({ eventName: "SubmitMessageWithNoConnection", type });
1230
1235
  return -1;
1231
1236
  }
1232
1237
  this.messageCountAfterDisconnection += 1;
@@ -1292,12 +1297,12 @@ export class Container extends EventEmitterWithErrorHandling {
1292
1297
  const version = await this.getVersion(specifiedVersion !== null && specifiedVersion !== void 0 ? specifiedVersion : this.id);
1293
1298
  if (version === undefined && specifiedVersion !== undefined) {
1294
1299
  // We should have a defined version to load from if specified version requested
1295
- this.logger.sendErrorEvent({ eventName: "NoVersionFoundWhenSpecified", id: specifiedVersion });
1300
+ this.mc.logger.sendErrorEvent({ eventName: "NoVersionFoundWhenSpecified", id: specifiedVersion });
1296
1301
  }
1297
1302
  this._loadedFromVersion = version;
1298
1303
  const snapshot = (_a = await this.storageService.getSnapshotTree(version)) !== null && _a !== void 0 ? _a : undefined;
1299
1304
  if (snapshot === undefined && version !== undefined) {
1300
- this.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
1305
+ this.mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
1301
1306
  }
1302
1307
  return { snapshot, versionId: version === null || version === void 0 ? void 0 : version.id };
1303
1308
  }
@@ -1313,7 +1318,7 @@ export class Container extends EventEmitterWithErrorHandling {
1313
1318
  assert(((_a = this._context) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing context not disposed" */);
1314
1319
  // If this assert fires, our state tracking is likely not synchronized between COntainer & runtime.
1315
1320
  if (this._dirtyContainer) {
1316
- this.logger.sendErrorEvent({ eventName: "DirtyContainerReloadContainer" });
1321
+ this.mc.logger.sendErrorEvent({ eventName: "DirtyContainerReloadContainer" });
1317
1322
  }
1318
1323
  // The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
1319
1324
  // are set. Global requests will still go directly to the loader
@@ -1327,7 +1332,7 @@ export class Container extends EventEmitterWithErrorHandling {
1327
1332
  // Please avoid calling it directly.
1328
1333
  // raiseContainerWarning() is the right flow for most cases
1329
1334
  logContainerError(warning) {
1330
- this.logger.sendErrorEvent({ eventName: "ContainerWarning" }, warning);
1335
+ this.mc.logger.sendErrorEvent({ eventName: "ContainerWarning" }, warning);
1331
1336
  }
1332
1337
  }
1333
1338
  Container.version = "^0.1.0";