@fluidframework/container-loader 2.0.0-internal.6.1.1 → 2.0.0-internal.6.3.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 (130) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +4 -3
  3. package/dist/audience.js +2 -2
  4. package/dist/audience.js.map +1 -1
  5. package/dist/catchUpMonitor.js +2 -2
  6. package/dist/catchUpMonitor.js.map +1 -1
  7. package/dist/connectionManager.d.ts +1 -1
  8. package/dist/connectionManager.d.ts.map +1 -1
  9. package/dist/connectionManager.js +38 -37
  10. package/dist/connectionManager.js.map +1 -1
  11. package/dist/connectionStateHandler.js +24 -24
  12. package/dist/connectionStateHandler.js.map +1 -1
  13. package/dist/container.d.ts +8 -1
  14. package/dist/container.d.ts.map +1 -1
  15. package/dist/container.js +117 -98
  16. package/dist/container.js.map +1 -1
  17. package/dist/containerStorageAdapter.d.ts.map +1 -1
  18. package/dist/containerStorageAdapter.js +10 -9
  19. package/dist/containerStorageAdapter.js.map +1 -1
  20. package/dist/contracts.d.ts +2 -2
  21. package/dist/contracts.d.ts.map +1 -1
  22. package/dist/contracts.js.map +1 -1
  23. package/dist/debugLogger.js +2 -2
  24. package/dist/debugLogger.js.map +1 -1
  25. package/dist/deltaManager.d.ts +3 -4
  26. package/dist/deltaManager.d.ts.map +1 -1
  27. package/dist/deltaManager.js +39 -38
  28. package/dist/deltaManager.js.map +1 -1
  29. package/dist/deltaQueue.d.ts +1 -1
  30. package/dist/deltaQueue.d.ts.map +1 -1
  31. package/dist/deltaQueue.js +9 -8
  32. package/dist/deltaQueue.js.map +1 -1
  33. package/dist/disposal.d.ts +2 -2
  34. package/dist/disposal.d.ts.map +1 -1
  35. package/dist/disposal.js +1 -1
  36. package/dist/disposal.js.map +1 -1
  37. package/dist/error.d.ts +23 -0
  38. package/dist/error.d.ts.map +1 -0
  39. package/dist/error.js +32 -0
  40. package/dist/error.js.map +1 -0
  41. package/dist/loader.d.ts +9 -2
  42. package/dist/loader.d.ts.map +1 -1
  43. package/dist/loader.js +12 -7
  44. package/dist/loader.js.map +1 -1
  45. package/dist/noopHeuristic.d.ts +2 -2
  46. package/dist/noopHeuristic.d.ts.map +1 -1
  47. package/dist/noopHeuristic.js +6 -5
  48. package/dist/noopHeuristic.js.map +1 -1
  49. package/dist/packageVersion.d.ts +1 -1
  50. package/dist/packageVersion.js +1 -1
  51. package/dist/packageVersion.js.map +1 -1
  52. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  53. package/dist/retriableDocumentStorageService.js +4 -4
  54. package/dist/retriableDocumentStorageService.js.map +1 -1
  55. package/dist/utils.d.ts.map +1 -1
  56. package/dist/utils.js +9 -8
  57. package/dist/utils.js.map +1 -1
  58. package/lib/audience.js +1 -1
  59. package/lib/audience.js.map +1 -1
  60. package/lib/catchUpMonitor.js +1 -1
  61. package/lib/catchUpMonitor.js.map +1 -1
  62. package/lib/connectionManager.d.ts +1 -1
  63. package/lib/connectionManager.d.ts.map +1 -1
  64. package/lib/connectionManager.js +10 -9
  65. package/lib/connectionManager.js.map +1 -1
  66. package/lib/connectionStateHandler.js +1 -1
  67. package/lib/connectionStateHandler.js.map +1 -1
  68. package/lib/container.d.ts +8 -1
  69. package/lib/container.d.ts.map +1 -1
  70. package/lib/container.js +73 -54
  71. package/lib/container.js.map +1 -1
  72. package/lib/containerStorageAdapter.d.ts.map +1 -1
  73. package/lib/containerStorageAdapter.js +2 -1
  74. package/lib/containerStorageAdapter.js.map +1 -1
  75. package/lib/contracts.d.ts +2 -2
  76. package/lib/contracts.d.ts.map +1 -1
  77. package/lib/contracts.js.map +1 -1
  78. package/lib/debugLogger.js +1 -1
  79. package/lib/debugLogger.js.map +1 -1
  80. package/lib/deltaManager.d.ts +3 -4
  81. package/lib/deltaManager.d.ts.map +1 -1
  82. package/lib/deltaManager.js +6 -5
  83. package/lib/deltaManager.js.map +1 -1
  84. package/lib/deltaQueue.d.ts +1 -1
  85. package/lib/deltaQueue.d.ts.map +1 -1
  86. package/lib/deltaQueue.js +2 -1
  87. package/lib/deltaQueue.js.map +1 -1
  88. package/lib/disposal.d.ts +2 -2
  89. package/lib/disposal.d.ts.map +1 -1
  90. package/lib/disposal.js +1 -1
  91. package/lib/disposal.js.map +1 -1
  92. package/lib/error.d.ts +23 -0
  93. package/lib/error.d.ts.map +1 -0
  94. package/lib/error.js +28 -0
  95. package/lib/error.js.map +1 -0
  96. package/lib/loader.d.ts +9 -2
  97. package/lib/loader.d.ts.map +1 -1
  98. package/lib/loader.js +11 -6
  99. package/lib/loader.js.map +1 -1
  100. package/lib/noopHeuristic.d.ts +2 -2
  101. package/lib/noopHeuristic.d.ts.map +1 -1
  102. package/lib/noopHeuristic.js +2 -1
  103. package/lib/noopHeuristic.js.map +1 -1
  104. package/lib/packageVersion.d.ts +1 -1
  105. package/lib/packageVersion.js +1 -1
  106. package/lib/packageVersion.js.map +1 -1
  107. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  108. package/lib/retriableDocumentStorageService.js +2 -2
  109. package/lib/retriableDocumentStorageService.js.map +1 -1
  110. package/lib/utils.d.ts.map +1 -1
  111. package/lib/utils.js +4 -3
  112. package/lib/utils.js.map +1 -1
  113. package/package.json +21 -27
  114. package/src/audience.ts +1 -1
  115. package/src/catchUpMonitor.ts +1 -1
  116. package/src/connectionManager.ts +20 -10
  117. package/src/connectionStateHandler.ts +1 -1
  118. package/src/container.ts +120 -79
  119. package/src/containerStorageAdapter.ts +2 -1
  120. package/src/contracts.ts +1 -2
  121. package/src/debugLogger.ts +1 -1
  122. package/src/deltaManager.ts +16 -13
  123. package/src/deltaQueue.ts +2 -1
  124. package/src/disposal.ts +2 -2
  125. package/src/error.ts +44 -0
  126. package/src/loader.ts +30 -5
  127. package/src/noopHeuristic.ts +3 -2
  128. package/src/packageVersion.ts +1 -1
  129. package/src/retriableDocumentStorageService.ts +2 -4
  130. package/src/utils.ts +4 -8
@@ -4,13 +4,13 @@
4
4
  */
5
5
 
6
6
  import { IDisposable, ITelemetryProperties } from "@fluidframework/core-interfaces";
7
- import { assert, performance, TypedEventEmitter } from "@fluidframework/common-utils";
7
+ import { assert } from "@fluidframework/core-utils";
8
+ import { performance, TypedEventEmitter } from "@fluid-internal/client-utils";
8
9
  import {
9
10
  ICriticalContainerError,
10
11
  IDeltaQueue,
11
12
  ReadOnlyInfo,
12
13
  } from "@fluidframework/container-definitions";
13
- import { GenericError, UsageError } from "@fluidframework/container-utils";
14
14
  import {
15
15
  IAnyDriverError,
16
16
  IDocumentService,
@@ -42,7 +42,13 @@ import {
42
42
  ScopeType,
43
43
  ISequencedDocumentSystemMessage,
44
44
  } from "@fluidframework/protocol-definitions";
45
- import { ITelemetryLoggerExt, formatTick, normalizeError } from "@fluidframework/telemetry-utils";
45
+ import {
46
+ formatTick,
47
+ GenericError,
48
+ ITelemetryLoggerExt,
49
+ normalizeError,
50
+ UsageError,
51
+ } from "@fluidframework/telemetry-utils";
46
52
  import {
47
53
  ReconnectMode,
48
54
  IConnectionManager,
@@ -356,7 +362,7 @@ export class ConnectionManager implements IConnectionManager {
356
362
  constructor(
357
363
  private readonly serviceProvider: () => IDocumentService | undefined,
358
364
  public readonly containerDirty: () => boolean,
359
- private client: IClient,
365
+ private readonly client: IClient,
360
366
  reconnectAllowed: boolean,
361
367
  private readonly logger: ITelemetryLoggerExt,
362
368
  private readonly props: IConnectionManagerFactoryArgs,
@@ -395,6 +401,7 @@ export class ConnectionManager implements IConnectionManager {
395
401
  error,
396
402
  };
397
403
 
404
+ const oldReadonlyValue = this.readonly;
398
405
  // This raises "disconnect" event if we have active connection.
399
406
  this.disconnectFromDeltaStream(disconnectReason);
400
407
 
@@ -402,7 +409,7 @@ export class ConnectionManager implements IConnectionManager {
402
409
  // Notify everyone we are in read-only state.
403
410
  // Useful for data stores in case we hit some critical error,
404
411
  // to switch to a mode where user edits are not accepted
405
- this.set_readonlyPermissions(true);
412
+ this.set_readonlyPermissions(true, oldReadonlyValue);
406
413
  }
407
414
  }
408
415
 
@@ -477,10 +484,12 @@ export class ConnectionManager implements IConnectionManager {
477
484
  }
478
485
  }
479
486
 
480
- private set_readonlyPermissions(readonly: boolean) {
481
- const oldValue = this.readonly;
482
- this._readonlyPermissions = readonly;
483
- if (oldValue !== this.readonly) {
487
+ private set_readonlyPermissions(
488
+ newReadonlyValue: boolean,
489
+ oldReadonlyValue: boolean | undefined,
490
+ ) {
491
+ this._readonlyPermissions = newReadonlyValue;
492
+ if (oldReadonlyValue !== this.readonly) {
484
493
  this.props.readonlyChangeHandler(this.readonly);
485
494
  }
486
495
  }
@@ -770,6 +779,7 @@ export class ConnectionManager implements IConnectionManager {
770
779
 
771
780
  this.pendingConnection = undefined;
772
781
 
782
+ const oldReadonlyValue = this.readonly;
773
783
  this.connection = connection;
774
784
 
775
785
  // Does information in scopes & mode matches?
@@ -795,7 +805,7 @@ export class ConnectionManager implements IConnectionManager {
795
805
  0x0e8 /* "readonly perf with write connection" */,
796
806
  );
797
807
 
798
- this.set_readonlyPermissions(readonly);
808
+ this.set_readonlyPermissions(readonly, oldReadonlyValue);
799
809
 
800
810
  if (this._disposed) {
801
811
  // Raise proper events, Log telemetry event and close connection.
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { ITelemetryProperties, TelemetryEventCategory } from "@fluidframework/core-interfaces";
7
- import { assert, Timer } from "@fluidframework/common-utils";
7
+ import { assert, Timer } from "@fluidframework/core-utils";
8
8
  import { IDeltaManager } from "@fluidframework/container-definitions";
9
9
  import { ISequencedClient, IClient } from "@fluidframework/protocol-definitions";
10
10
  import {
package/src/container.ts CHANGED
@@ -7,20 +7,17 @@
7
7
  import merge from "lodash/merge";
8
8
 
9
9
  import { v4 as uuid } from "uuid";
10
- import { IEvent } from "@fluidframework/common-definitions";
11
- import {
12
- TypedEventEmitter,
13
- assert,
14
- performance,
15
- unreachableCase,
16
- } from "@fluidframework/common-utils";
10
+ import { assert, unreachableCase } from "@fluidframework/core-utils";
11
+ import { TypedEventEmitter, performance } from "@fluid-internal/client-utils";
17
12
  import {
13
+ IEvent,
18
14
  ITelemetryProperties,
19
15
  TelemetryEventCategory,
20
16
  IRequest,
21
17
  IResponse,
22
18
  IFluidRouter,
23
19
  FluidObject,
20
+ LogLevel,
24
21
  } from "@fluidframework/core-interfaces";
25
22
  import {
26
23
  AttachState,
@@ -43,7 +40,6 @@ import {
43
40
  ReadOnlyInfo,
44
41
  isFluidCodeDetails,
45
42
  } from "@fluidframework/container-definitions";
46
- import { GenericError, UsageError } from "@fluidframework/container-utils";
47
43
  import {
48
44
  IDocumentService,
49
45
  IDocumentServiceFactory,
@@ -92,6 +88,8 @@ import {
92
88
  wrapError,
93
89
  ITelemetryLoggerExt,
94
90
  formatTick,
91
+ GenericError,
92
+ UsageError,
95
93
  } from "@fluidframework/telemetry-utils";
96
94
  import { Audience } from "./audience";
97
95
  import { ContainerContext } from "./containerContext";
@@ -217,6 +215,10 @@ export interface IContainerCreateProps {
217
215
  */
218
216
  readonly detachedBlobStorage?: IDetachedBlobStorage;
219
217
 
218
+ /**
219
+ * Optional property for allowing the container to use a custom
220
+ * protocol implementation for handling the quorum and/or the audience.
221
+ */
220
222
  readonly protocolHandlerBuilder?: ProtocolHandlerBuilder;
221
223
  }
222
224
 
@@ -424,7 +426,11 @@ export class Container
424
426
  // Depending where error happens, we can be attempting to connect to web socket
425
427
  // and continuously retrying (consider offline mode)
426
428
  // Host has no container to close, so it's prudent to do it here
429
+ // Note: We could only dispose the container instead of just close but that would
430
+ // the telemetry where users sometimes search for ContainerClose event to look
431
+ // for load failures. So not removing this at this time.
427
432
  container.close(err);
433
+ container.dispose(err);
428
434
  onClosed(err);
429
435
  },
430
436
  );
@@ -747,6 +753,7 @@ export class Container
747
753
 
748
754
  this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
749
755
  const pendingLocalState = loadProps?.pendingLocalState;
756
+ this._clientId = pendingLocalState?.clientId;
750
757
 
751
758
  this._canReconnect = canReconnect ?? true;
752
759
  this.clientDetailsOverride = clientDetailsOverride;
@@ -1094,38 +1101,50 @@ export class Container
1094
1101
  }
1095
1102
 
1096
1103
  private async getPendingLocalStateCore(props: { notifyImminentClosure: boolean }) {
1097
- if (!this.offlineLoadEnabled) {
1098
- throw new UsageError("Can't get pending local state unless offline load is enabled");
1099
- }
1100
- if (this.closed || this._disposed) {
1101
- throw new UsageError(
1102
- "Pending state cannot be retried if the container is closed or disposed",
1103
- );
1104
- }
1105
- assert(
1106
- this.attachState === AttachState.Attached,
1107
- 0x0d1 /* "Container should be attached before close" */,
1108
- );
1109
- assert(
1110
- this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid",
1111
- 0x0d2 /* "resolved url should be valid Fluid url" */,
1112
- );
1113
- assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
1114
- assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
1115
- const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
1116
- const pendingState: IPendingContainerState = {
1117
- pendingRuntimeState,
1118
- baseSnapshot: this.baseSnapshot,
1119
- snapshotBlobs: this.baseSnapshotBlobs,
1120
- savedOps: this.savedOps,
1121
- url: this.resolvedUrl.url,
1122
- term: OnlyValidTermValue,
1123
- clientId: this.clientId,
1124
- };
1125
-
1126
- this.mc.logger.sendTelemetryEvent({ eventName: "GetPendingLocalState" });
1104
+ return PerformanceEvent.timedExecAsync(
1105
+ this.mc.logger,
1106
+ {
1107
+ eventName: "getPendingLocalState",
1108
+ notifyImminentClosure: props.notifyImminentClosure,
1109
+ savedOpsSize: this.savedOps.length,
1110
+ clientId: this.clientId,
1111
+ },
1112
+ async () => {
1113
+ if (!this.offlineLoadEnabled) {
1114
+ throw new UsageError(
1115
+ "Can't get pending local state unless offline load is enabled",
1116
+ );
1117
+ }
1118
+ if (this.closed || this._disposed) {
1119
+ throw new UsageError(
1120
+ "Pending state cannot be retried if the container is closed or disposed",
1121
+ );
1122
+ }
1123
+ assert(
1124
+ this.attachState === AttachState.Attached,
1125
+ 0x0d1 /* "Container should be attached before close" */,
1126
+ );
1127
+ assert(
1128
+ this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid",
1129
+ 0x0d2 /* "resolved url should be valid Fluid url" */,
1130
+ );
1131
+ assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
1132
+ assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
1133
+ const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
1134
+ const pendingState: IPendingContainerState = {
1135
+ pendingRuntimeState,
1136
+ baseSnapshot: this.baseSnapshot,
1137
+ snapshotBlobs: this.baseSnapshotBlobs,
1138
+ savedOps: this.savedOps,
1139
+ url: this.resolvedUrl.url,
1140
+ term: OnlyValidTermValue,
1141
+ // no need to save this if there is no pending runtime state
1142
+ clientId: pendingRuntimeState !== undefined ? this.clientId : undefined,
1143
+ };
1127
1144
 
1128
- return JSON.stringify(pendingState);
1145
+ return JSON.stringify(pendingState);
1146
+ },
1147
+ );
1129
1148
  }
1130
1149
 
1131
1150
  public get attachState(): AttachState {
@@ -1151,7 +1170,10 @@ export class Container
1151
1170
  return JSON.stringify(combinedSummary);
1152
1171
  }
1153
1172
 
1154
- public async attach(request: IRequest): Promise<void> {
1173
+ public async attach(
1174
+ request: IRequest,
1175
+ attachProps?: { deltaConnection?: "none" | "delayed" },
1176
+ ): Promise<void> {
1155
1177
  await PerformanceEvent.timedExecAsync(
1156
1178
  this.mc.logger,
1157
1179
  { eventName: "Attach" },
@@ -1277,10 +1299,13 @@ export class Container
1277
1299
  this.emit("attached");
1278
1300
 
1279
1301
  if (!this.closed) {
1280
- this.resumeInternal({
1281
- fetchOpsFromStorage: false,
1282
- reason: { text: "createDetached" },
1283
- });
1302
+ this.handleDeltaConnectionArg(
1303
+ {
1304
+ fetchOpsFromStorage: false,
1305
+ reason: { text: "createDetached" },
1306
+ },
1307
+ attachProps?.deltaConnection,
1308
+ );
1284
1309
  }
1285
1310
  } catch (error) {
1286
1311
  // add resolved URL on error object so that host has the ability to find this document and delete it
@@ -1505,6 +1530,7 @@ export class Container
1505
1530
  pendingLocalState: IPendingContainerState | undefined,
1506
1531
  loadToSequenceNumber: number | undefined,
1507
1532
  ) {
1533
+ const timings: Record<string, number> = { phase1: performance.now() };
1508
1534
  this.service = await this.serviceFactory.createDocumentService(
1509
1535
  resolvedUrl,
1510
1536
  this.subLogger,
@@ -1543,6 +1569,7 @@ export class Container
1543
1569
 
1544
1570
  this._attachState = AttachState.Attached;
1545
1571
 
1572
+ timings.phase2 = performance.now();
1546
1573
  // Fetch specified snapshot.
1547
1574
  const { snapshot, versionId } =
1548
1575
  pendingLocalState === undefined
@@ -1640,13 +1667,12 @@ export class Container
1640
1667
  );
1641
1668
  break;
1642
1669
  case "sequenceNumber":
1643
- opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "sequenceNumber");
1644
- break;
1645
1670
  case "cached":
1646
- opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
1647
- break;
1648
1671
  case "all":
1649
- opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "all");
1672
+ opsBeforeReturnP = this.attachDeltaManagerOpHandler(
1673
+ dmAttributes,
1674
+ loadMode.opsBeforeReturn,
1675
+ );
1650
1676
  break;
1651
1677
  default:
1652
1678
  unreachableCase(loadMode.opsBeforeReturn);
@@ -1656,11 +1682,13 @@ export class Container
1656
1682
  // Initialize the protocol handler
1657
1683
  await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
1658
1684
 
1685
+ timings.phase3 = performance.now();
1659
1686
  const codeDetails = this.getCodeDetailsFromQuorum();
1660
1687
  await this.instantiateRuntime(
1661
1688
  codeDetails,
1662
1689
  snapshot,
1663
- pendingLocalState?.pendingRuntimeState,
1690
+ // give runtime a dummy value so it knows we're loading from a stash blob
1691
+ pendingLocalState ? pendingLocalState?.pendingRuntimeState ?? {} : undefined,
1664
1692
  );
1665
1693
 
1666
1694
  // replay saved ops
@@ -1672,13 +1700,6 @@ export class Container
1672
1700
  await this.runtime.notifyOpReplay?.(message);
1673
1701
  }
1674
1702
  pendingLocalState.savedOps = [];
1675
-
1676
- // now set clientId to stashed clientId so live ops are correctly processed as local
1677
- assert(
1678
- this.clientId === undefined,
1679
- 0x5d6 /* Unexpected clientId when setting stashed clientId */,
1680
- );
1681
- this._clientId = pendingLocalState?.clientId;
1682
1703
  }
1683
1704
 
1684
1705
  // We might have hit some failure that did not manifest itself in exception in this flow,
@@ -1702,27 +1723,11 @@ export class Container
1702
1723
  this._deltaManager.inbound.pause();
1703
1724
  }
1704
1725
 
1705
- switch (loadMode.deltaConnection) {
1706
- case undefined:
1707
- if (pendingLocalState) {
1708
- // connect to delta stream now since we did not before
1709
- this.connectToDeltaStream(connectionArgs);
1710
- }
1711
- // intentional fallthrough
1712
- case "delayed":
1713
- assert(
1714
- this.inboundQueuePausedFromInit,
1715
- 0x346 /* inboundQueuePausedFromInit should be true */,
1716
- );
1717
- this.inboundQueuePausedFromInit = false;
1718
- this._deltaManager.inbound.resume();
1719
- this._deltaManager.inboundSignal.resume();
1720
- break;
1721
- case "none":
1722
- break;
1723
- default:
1724
- unreachableCase(loadMode.deltaConnection);
1725
- }
1726
+ this.handleDeltaConnectionArg(
1727
+ connectionArgs,
1728
+ loadMode.deltaConnection,
1729
+ pendingLocalState !== undefined,
1730
+ );
1726
1731
  }
1727
1732
 
1728
1733
  // If we have not yet reached `loadToSequenceNumber`, we will wait for ops to arrive until we reach it
@@ -1752,7 +1757,15 @@ export class Container
1752
1757
 
1753
1758
  // Internal context is fully loaded at this point
1754
1759
  this.setLoaded();
1755
-
1760
+ timings.end = performance.now();
1761
+ this.subLogger.sendTelemetryEvent(
1762
+ {
1763
+ eventName: "LoadStagesTimings",
1764
+ details: JSON.stringify(timings),
1765
+ },
1766
+ undefined,
1767
+ LogLevel.verbose,
1768
+ );
1756
1769
  return {
1757
1770
  sequenceNumber: attributes.sequenceNumber,
1758
1771
  version: versionId,
@@ -2454,6 +2467,34 @@ export class Container
2454
2467
  this.runtime.setConnectionState(state && !readonly, this.clientId);
2455
2468
  }
2456
2469
  }
2470
+
2471
+ private handleDeltaConnectionArg(
2472
+ connectionArgs: IConnectionArgs,
2473
+ deltaConnectionArg?: "none" | "delayed",
2474
+ canConnect: boolean = true,
2475
+ ) {
2476
+ switch (deltaConnectionArg) {
2477
+ case undefined:
2478
+ if (canConnect) {
2479
+ // connect to delta stream now since we did not before
2480
+ this.connectToDeltaStream(connectionArgs);
2481
+ }
2482
+ // intentional fallthrough
2483
+ case "delayed":
2484
+ assert(
2485
+ this.inboundQueuePausedFromInit,
2486
+ 0x346 /* inboundQueuePausedFromInit should be true */,
2487
+ );
2488
+ this.inboundQueuePausedFromInit = false;
2489
+ this._deltaManager.inbound.resume();
2490
+ this._deltaManager.inboundSignal.resume();
2491
+ break;
2492
+ case "none":
2493
+ break;
2494
+ default:
2495
+ unreachableCase(deltaConnectionArg);
2496
+ }
2497
+ }
2457
2498
  }
2458
2499
 
2459
2500
  /**
@@ -5,7 +5,8 @@
5
5
 
6
6
  import { IDisposable } from "@fluidframework/core-interfaces";
7
7
  import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
8
- import { assert, bufferToString, stringToBuffer } from "@fluidframework/common-utils";
8
+ import { bufferToString, stringToBuffer } from "@fluid-internal/client-utils";
9
+ import { assert } from "@fluidframework/core-utils";
9
10
  import { ISnapshotTreeWithBlobContents } from "@fluidframework/container-definitions";
10
11
  import {
11
12
  FetchSource,
package/src/contracts.ts CHANGED
@@ -3,12 +3,11 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { ITelemetryProperties } from "@fluidframework/core-interfaces";
6
+ import { IErrorBase, ITelemetryProperties } from "@fluidframework/core-interfaces";
7
7
  import {
8
8
  IConnectionDetails,
9
9
  ICriticalContainerError,
10
10
  IDeltaQueue,
11
- IErrorBase,
12
11
  IFluidCodeDetails,
13
12
  isFluidPackage,
14
13
  ReadOnlyInfo,
@@ -8,7 +8,7 @@ import {
8
8
  ITelemetryBaseLogger,
9
9
  ITelemetryProperties,
10
10
  } from "@fluidframework/core-interfaces";
11
- import { performance } from "@fluidframework/common-utils";
11
+ import { performance } from "@fluid-internal/client-utils";
12
12
  import { debug as registerDebug, IDebugger } from "debug";
13
13
  import {
14
14
  ITelemetryLoggerExt,
@@ -4,27 +4,35 @@
4
4
  */
5
5
 
6
6
  import { v4 as uuid } from "uuid";
7
- import { IEventProvider } from "@fluidframework/common-definitions";
8
- import { ITelemetryProperties, ITelemetryErrorEvent } from "@fluidframework/core-interfaces";
7
+ import {
8
+ IThrottlingWarning,
9
+ IEventProvider,
10
+ ITelemetryProperties,
11
+ ITelemetryErrorEvent,
12
+ } from "@fluidframework/core-interfaces";
9
13
  import {
10
14
  ICriticalContainerError,
11
15
  IDeltaManager,
12
16
  IDeltaManagerEvents,
13
17
  IDeltaQueue,
14
- IThrottlingWarning,
15
18
  } from "@fluidframework/container-definitions";
16
- import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
19
+ import { TypedEventEmitter } from "@fluid-internal/client-utils";
20
+ import { assert } from "@fluidframework/core-utils";
17
21
  import {
22
+ DataProcessingError,
23
+ extractSafePropertiesFromMessage,
18
24
  normalizeError,
19
25
  logIfFalse,
20
26
  safeRaiseEvent,
21
27
  isFluidError,
22
28
  ITelemetryLoggerExt,
29
+ DataCorruptionError,
30
+ UsageError,
23
31
  } from "@fluidframework/telemetry-utils";
24
32
  import {
25
33
  IDocumentDeltaStorageService,
26
34
  IDocumentService,
27
- DriverErrorType,
35
+ DriverErrorTypes,
28
36
  } from "@fluidframework/driver-definitions";
29
37
  import {
30
38
  IDocumentMessage,
@@ -34,13 +42,7 @@ import {
34
42
  ConnectionMode,
35
43
  } from "@fluidframework/protocol-definitions";
36
44
  import { NonRetryableError, isRuntimeMessage, MessageType2 } from "@fluidframework/driver-utils";
37
- import {
38
- ThrottlingWarning,
39
- DataCorruptionError,
40
- extractSafePropertiesFromMessage,
41
- DataProcessingError,
42
- UsageError,
43
- } from "@fluidframework/container-utils";
45
+
44
46
  import {
45
47
  IConnectionDetailsInternal,
46
48
  IConnectionManager,
@@ -49,6 +51,7 @@ import {
49
51
  } from "./contracts";
50
52
  import { DeltaQueue } from "./deltaQueue";
51
53
  import { OnlyValidTermValue } from "./protocol";
54
+ import { ThrottlingWarning } from "./error";
52
55
 
53
56
  export interface IConnectionArgs {
54
57
  mode?: ConnectionMode;
@@ -940,7 +943,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
940
943
  // pre-0.58 error message: twoMessagesWithSameSeqNumAndDifferentPayload
941
944
  "Found two messages with the same sequenceNumber but different payloads. Likely to be a " +
942
945
  "service issue",
943
- DriverErrorType.fileOverwrittenInStorage,
946
+ DriverErrorTypes.fileOverwrittenInStorage,
944
947
  {
945
948
  clientId: this.connectionManager.clientId,
946
949
  sequenceNumber: message.sequenceNumber,
package/src/deltaQueue.ts CHANGED
@@ -4,7 +4,8 @@
4
4
  */
5
5
 
6
6
  import { IDeltaQueue, IDeltaQueueEvents } from "@fluidframework/container-definitions";
7
- import { assert, performance, TypedEventEmitter } from "@fluidframework/common-utils";
7
+ import { assert } from "@fluidframework/core-utils";
8
+ import { performance, TypedEventEmitter } from "@fluid-internal/client-utils";
8
9
  import Deque from "double-ended-queue";
9
10
 
10
11
  export interface IDeltaQueueWriter<T> {
package/src/disposal.ts CHANGED
@@ -3,11 +3,11 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { IDisposable } from "@fluidframework/common-definitions";
6
+ import { IDisposable } from "@fluidframework/core-interfaces";
7
7
 
8
8
  /**
9
9
  * Returns a wrapper around the provided function, which will only invoke the inner function if the provided
10
- * {@link @fluidframework/common-definitions#IDisposable | disposable} object has not yet been disposed.
10
+ * {@link @fluidframework/core-interfaces#IDisposable | disposable} object has not yet been disposed.
11
11
  *
12
12
  * @throws Will throw an error if the item has already been disposed.
13
13
  */
package/src/error.ts ADDED
@@ -0,0 +1,44 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { ITelemetryProperties, IThrottlingWarning } from "@fluidframework/core-interfaces";
7
+ import { ContainerErrorTypes } from "@fluidframework/container-definitions";
8
+ import {
9
+ IFluidErrorBase,
10
+ ITelemetryLoggerExt,
11
+ LoggingError,
12
+ wrapErrorAndLog,
13
+ } from "@fluidframework/telemetry-utils";
14
+
15
+ /**
16
+ * Warning emitted when requests to storage are being throttled.
17
+ */
18
+ export class ThrottlingWarning extends LoggingError implements IThrottlingWarning, IFluidErrorBase {
19
+ /**
20
+ * {@inheritDoc @fluidframework/telemetry-utils#IFluidErrorBase.errorType}
21
+ */
22
+ public readonly errorType = ContainerErrorTypes.throttlingError;
23
+
24
+ private constructor(
25
+ message: string,
26
+ readonly retryAfterSeconds: number,
27
+ props?: ITelemetryProperties,
28
+ ) {
29
+ super(message, props);
30
+ }
31
+
32
+ /**
33
+ * Wrap the given error as a ThrottlingWarning
34
+ * Only preserves the error message, and applies the given retry after to the new warning object
35
+ */
36
+ public static wrap(
37
+ error: unknown,
38
+ retryAfterSeconds: number,
39
+ logger: ITelemetryLoggerExt,
40
+ ): IThrottlingWarning {
41
+ const newErrorFn = (errMsg: string) => new ThrottlingWarning(errMsg, retryAfterSeconds);
42
+ return wrapErrorAndLog(error, newErrorFn, logger);
43
+ }
44
+ }
package/src/loader.ts CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  PerformanceEvent,
13
13
  sessionStorageConfigProvider,
14
14
  createChildMonitoringContext,
15
+ UsageError,
15
16
  } from "@fluidframework/telemetry-utils";
16
17
  import {
17
18
  ITelemetryBaseLogger,
@@ -37,7 +38,7 @@ import {
37
38
  IResolvedUrl,
38
39
  IUrlResolver,
39
40
  } from "@fluidframework/driver-definitions";
40
- import { UsageError } from "@fluidframework/container-utils";
41
+ import { IClientDetails } from "@fluidframework/protocol-definitions";
41
42
  import { Container, IPendingContainerState } from "./container";
42
43
  import { IParsedUrl, parseUrl } from "./utils";
43
44
  import { pkgVersion } from "./packageVersion";
@@ -357,8 +358,20 @@ export class Loader implements IHostLoader {
357
358
  return this;
358
359
  }
359
360
 
360
- public async createDetachedContainer(codeDetails: IFluidCodeDetails): Promise<IContainer> {
361
- const container = await Container.createDetached(this.services, codeDetails);
361
+ public async createDetachedContainer(
362
+ codeDetails: IFluidCodeDetails,
363
+ createDetachedProps?: {
364
+ canReconnect?: boolean;
365
+ clientDetailsOverride?: IClientDetails;
366
+ },
367
+ ): Promise<IContainer> {
368
+ const container = await Container.createDetached(
369
+ {
370
+ ...createDetachedProps,
371
+ ...this.services,
372
+ },
373
+ codeDetails,
374
+ );
362
375
 
363
376
  if (this.cachingEnabled) {
364
377
  container.once("attached", () => {
@@ -373,8 +386,20 @@ export class Loader implements IHostLoader {
373
386
  return container;
374
387
  }
375
388
 
376
- public async rehydrateDetachedContainerFromSnapshot(snapshot: string): Promise<IContainer> {
377
- return Container.rehydrateDetachedFromSnapshot(this.services, snapshot);
389
+ public async rehydrateDetachedContainerFromSnapshot(
390
+ snapshot: string,
391
+ createDetachedProps?: {
392
+ canReconnect?: boolean;
393
+ clientDetailsOverride?: IClientDetails;
394
+ },
395
+ ): Promise<IContainer> {
396
+ return Container.rehydrateDetachedFromSnapshot(
397
+ {
398
+ ...createDetachedProps,
399
+ ...this.services,
400
+ },
401
+ snapshot,
402
+ );
378
403
  }
379
404
 
380
405
  public async resolve(request: IRequest, pendingLocalState?: string): Promise<IContainer> {
@@ -3,10 +3,11 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { assert, Timer, TypedEventEmitter } from "@fluidframework/common-utils";
6
+ import { TypedEventEmitter } from "@fluid-internal/client-utils";
7
+ import { assert, Timer } from "@fluidframework/core-utils";
7
8
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
8
9
  import { isRuntimeMessage } from "@fluidframework/driver-utils";
9
- import { IEvent } from "@fluidframework/common-definitions";
10
+ import { IEvent } from "@fluidframework/core-interfaces";
10
11
 
11
12
  const defaultNoopTimeFrequency = 2000;
12
13
  const defaultNoopCountFrequency = 50;