@fluidframework/container-runtime 1.2.2 → 2.0.0-internal.1.0.0.82159

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 (135) hide show
  1. package/dist/blobManager.d.ts +81 -25
  2. package/dist/blobManager.d.ts.map +1 -1
  3. package/dist/blobManager.js +301 -100
  4. package/dist/blobManager.js.map +1 -1
  5. package/dist/containerRuntime.d.ts +65 -11
  6. package/dist/containerRuntime.d.ts.map +1 -1
  7. package/dist/containerRuntime.js +101 -82
  8. package/dist/containerRuntime.js.map +1 -1
  9. package/dist/dataStore.d.ts +1 -1
  10. package/dist/dataStore.d.ts.map +1 -1
  11. package/dist/dataStore.js +32 -26
  12. package/dist/dataStore.js.map +1 -1
  13. package/dist/dataStoreContext.d.ts +3 -4
  14. package/dist/dataStoreContext.d.ts.map +1 -1
  15. package/dist/dataStoreContext.js +16 -23
  16. package/dist/dataStoreContext.js.map +1 -1
  17. package/dist/dataStores.d.ts +5 -2
  18. package/dist/dataStores.d.ts.map +1 -1
  19. package/dist/dataStores.js +11 -3
  20. package/dist/dataStores.js.map +1 -1
  21. package/dist/garbageCollection.d.ts.map +1 -1
  22. package/dist/garbageCollection.js +17 -12
  23. package/dist/garbageCollection.js.map +1 -1
  24. package/dist/opProperties.d.ts +7 -0
  25. package/dist/opProperties.d.ts.map +1 -0
  26. package/dist/opProperties.js +20 -0
  27. package/dist/opProperties.js.map +1 -0
  28. package/dist/packageVersion.d.ts +1 -1
  29. package/dist/packageVersion.d.ts.map +1 -1
  30. package/dist/packageVersion.js +1 -1
  31. package/dist/packageVersion.js.map +1 -1
  32. package/dist/runningSummarizer.d.ts +14 -4
  33. package/dist/runningSummarizer.d.ts.map +1 -1
  34. package/dist/runningSummarizer.js +68 -26
  35. package/dist/runningSummarizer.js.map +1 -1
  36. package/dist/summarizer.d.ts +0 -2
  37. package/dist/summarizer.d.ts.map +1 -1
  38. package/dist/summarizer.js +1 -12
  39. package/dist/summarizer.js.map +1 -1
  40. package/dist/summarizerHeuristics.d.ts +26 -4
  41. package/dist/summarizerHeuristics.d.ts.map +1 -1
  42. package/dist/summarizerHeuristics.js +95 -18
  43. package/dist/summarizerHeuristics.js.map +1 -1
  44. package/dist/summarizerTypes.d.ts +30 -10
  45. package/dist/summarizerTypes.d.ts.map +1 -1
  46. package/dist/summarizerTypes.js.map +1 -1
  47. package/dist/summaryCollection.js +1 -1
  48. package/dist/summaryCollection.js.map +1 -1
  49. package/dist/summaryFormat.d.ts +0 -5
  50. package/dist/summaryFormat.d.ts.map +1 -1
  51. package/dist/summaryFormat.js.map +1 -1
  52. package/dist/summaryGenerator.d.ts +1 -0
  53. package/dist/summaryGenerator.d.ts.map +1 -1
  54. package/dist/summaryGenerator.js +11 -9
  55. package/dist/summaryGenerator.js.map +1 -1
  56. package/lib/blobManager.d.ts +81 -25
  57. package/lib/blobManager.d.ts.map +1 -1
  58. package/lib/blobManager.js +302 -101
  59. package/lib/blobManager.js.map +1 -1
  60. package/lib/containerRuntime.d.ts +65 -11
  61. package/lib/containerRuntime.d.ts.map +1 -1
  62. package/lib/containerRuntime.js +103 -84
  63. package/lib/containerRuntime.js.map +1 -1
  64. package/lib/dataStore.d.ts +1 -1
  65. package/lib/dataStore.d.ts.map +1 -1
  66. package/lib/dataStore.js +32 -26
  67. package/lib/dataStore.js.map +1 -1
  68. package/lib/dataStoreContext.d.ts +3 -4
  69. package/lib/dataStoreContext.d.ts.map +1 -1
  70. package/lib/dataStoreContext.js +17 -24
  71. package/lib/dataStoreContext.js.map +1 -1
  72. package/lib/dataStores.d.ts +5 -2
  73. package/lib/dataStores.d.ts.map +1 -1
  74. package/lib/dataStores.js +11 -3
  75. package/lib/dataStores.js.map +1 -1
  76. package/lib/garbageCollection.d.ts.map +1 -1
  77. package/lib/garbageCollection.js +17 -12
  78. package/lib/garbageCollection.js.map +1 -1
  79. package/lib/opProperties.d.ts +7 -0
  80. package/lib/opProperties.d.ts.map +1 -0
  81. package/lib/opProperties.js +16 -0
  82. package/lib/opProperties.js.map +1 -0
  83. package/lib/packageVersion.d.ts +1 -1
  84. package/lib/packageVersion.d.ts.map +1 -1
  85. package/lib/packageVersion.js +1 -1
  86. package/lib/packageVersion.js.map +1 -1
  87. package/lib/runningSummarizer.d.ts +14 -4
  88. package/lib/runningSummarizer.d.ts.map +1 -1
  89. package/lib/runningSummarizer.js +68 -26
  90. package/lib/runningSummarizer.js.map +1 -1
  91. package/lib/summarizer.d.ts +0 -2
  92. package/lib/summarizer.d.ts.map +1 -1
  93. package/lib/summarizer.js +1 -12
  94. package/lib/summarizer.js.map +1 -1
  95. package/lib/summarizerHeuristics.d.ts +26 -4
  96. package/lib/summarizerHeuristics.d.ts.map +1 -1
  97. package/lib/summarizerHeuristics.js +95 -18
  98. package/lib/summarizerHeuristics.js.map +1 -1
  99. package/lib/summarizerTypes.d.ts +30 -10
  100. package/lib/summarizerTypes.d.ts.map +1 -1
  101. package/lib/summarizerTypes.js.map +1 -1
  102. package/lib/summaryCollection.js +1 -1
  103. package/lib/summaryCollection.js.map +1 -1
  104. package/lib/summaryFormat.d.ts +0 -5
  105. package/lib/summaryFormat.d.ts.map +1 -1
  106. package/lib/summaryFormat.js.map +1 -1
  107. package/lib/summaryGenerator.d.ts +1 -0
  108. package/lib/summaryGenerator.d.ts.map +1 -1
  109. package/lib/summaryGenerator.js +11 -9
  110. package/lib/summaryGenerator.js.map +1 -1
  111. package/package.json +45 -20
  112. package/src/blobManager.ts +360 -119
  113. package/src/containerRuntime.ts +203 -103
  114. package/src/dataStore.ts +53 -38
  115. package/src/dataStoreContext.ts +16 -23
  116. package/src/dataStores.ts +14 -3
  117. package/src/garbageCollection.ts +13 -7
  118. package/src/opProperties.ts +19 -0
  119. package/src/packageVersion.ts +1 -1
  120. package/src/runningSummarizer.ts +75 -22
  121. package/src/summarizer.ts +1 -18
  122. package/src/summarizerHeuristics.ts +133 -19
  123. package/src/summarizerTypes.ts +37 -10
  124. package/src/summaryCollection.ts +1 -1
  125. package/src/summaryFormat.ts +0 -6
  126. package/src/summaryGenerator.ts +40 -22
  127. package/dist/opTelemetry.d.ts +0 -22
  128. package/dist/opTelemetry.d.ts.map +0 -1
  129. package/dist/opTelemetry.js +0 -59
  130. package/dist/opTelemetry.js.map +0 -1
  131. package/lib/opTelemetry.d.ts +0 -22
  132. package/lib/opTelemetry.d.ts.map +0 -1
  133. package/lib/opTelemetry.js +0 -55
  134. package/lib/opTelemetry.js.map +0 -1
  135. package/src/opTelemetry.ts +0 -71
@@ -43,9 +43,13 @@ import {
43
43
  TaggedLoggerAdapter,
44
44
  MonitoringContext,
45
45
  loggerToMonitoringContext,
46
- TelemetryDataTag,
47
46
  } from "@fluidframework/telemetry-utils";
48
- import { DriverHeader, IDocumentStorageService, ISummaryContext } from "@fluidframework/driver-definitions";
47
+ import {
48
+ DriverHeader,
49
+ FetchSource,
50
+ IDocumentStorageService,
51
+ ISummaryContext,
52
+ } from "@fluidframework/driver-definitions";
49
53
  import { readAndParse, isUnpackedRuntimeMessage } from "@fluidframework/driver-utils";
50
54
  import {
51
55
  DataCorruptionError,
@@ -160,7 +164,6 @@ import {
160
164
  } from "./dataStore";
161
165
  import { BindBatchTracker } from "./batchTracker";
162
166
  import { ISerializedBaseSnapshotBlobs, SerializedSnapshotStorage } from "./serializedSnapshotStorage";
163
- import { OpTracker } from "./opTelemetry";
164
167
 
165
168
  export enum ContainerMessageType {
166
169
  // An op to be delivered to store
@@ -224,12 +227,14 @@ export interface ISummaryBaseConfiguration {
224
227
  export interface ISummaryConfigurationHeuristics extends ISummaryBaseConfiguration {
225
228
  state: "enabled";
226
229
  /**
227
- * Defines the maximum allowed time in between summarizations.
230
+ * @deprecated - please move all implementation to minIdleTime and maxIdleTime
228
231
  */
229
232
  idleTime: number;
230
233
  /**
231
- * Defines the maximum allowed time, since the last received Ack, before running the summary
234
+ * Defines the maximum allowed time, since the last received Ack, before running the summary
232
235
  * with reason maxTime.
236
+ * For example, say we receive ops one by one just before the idle time is triggered.
237
+ * In this case, we still want to run a summary since it's been a while since the last summary.
233
238
  */
234
239
  maxTime: number;
235
240
  /**
@@ -242,6 +247,34 @@ export interface ISummaryConfigurationHeuristics extends ISummaryBaseConfigurati
242
247
  * before running the last summary.
243
248
  */
244
249
  minOpsForLastSummaryAttempt: number;
250
+ /**
251
+ * Defines the lower boundary for the allowed time in between summarizations.
252
+ * Pairs with maxIdleTime to form a range.
253
+ * For example, if we only receive 1 op, we don't want to have the same idle time as say 100 ops.
254
+ * Based on the boundaries we set in minIdleTime and maxIdleTime, the idle time will change
255
+ * linearly depending on the number of ops we receive.
256
+ */
257
+ minIdleTime: number;
258
+ /**
259
+ * Defines the upper boundary for the allowed time in between summarizations.
260
+ * Pairs with minIdleTime to form a range.
261
+ * For example, if we only receive 1 op, we don't want to have the same idle time as say 100 ops.
262
+ * Based on the boundaries we set in minIdleTime and maxIdleTime, the idle time will change
263
+ * linearly depending on the number of ops we receive.
264
+ */
265
+ maxIdleTime: number;
266
+ /**
267
+ * Runtime op weight to use in heuristic summarizing.
268
+ * This number is a multiplier on the number of runtime ops we process when running summarize heuristics.
269
+ * For example: (multiplier) * (number of runtime ops) = weighted number of runtime ops
270
+ */
271
+ runtimeOpWeight: number;
272
+ /**
273
+ * Non-runtime op weight to use in heuristic summarizing
274
+ * This number is a multiplier on the number of non-runtime ops we process when running summarize heuristics.
275
+ * For example: (multiplier) * (number of non-runtime ops) = weighted number of non-runtime ops
276
+ */
277
+ nonRuntimeOpWeight: number;
245
278
  }
246
279
 
247
280
  export interface ISummaryConfigurationDisableSummarizer {
@@ -260,44 +293,57 @@ export type ISummaryConfiguration =
260
293
  export const DefaultSummaryConfiguration: ISummaryConfiguration = {
261
294
  state: "enabled",
262
295
 
263
- idleTime: 5000 * 3,
296
+ idleTime: 15 * 1000, // 15 secs.
297
+
298
+ minIdleTime: 0,
299
+
300
+ maxIdleTime: 30 * 1000, // 30 secs.
264
301
 
265
- maxTime: 5000 * 12,
302
+ maxTime: 60 * 1000, // 1 min.
266
303
 
267
- maxOps: 100, // Summarize if 100 ops received since last snapshot.
304
+ maxOps: 100, // Summarize if 100 weighted ops received since last snapshot.
268
305
 
269
306
  minOpsForLastSummaryAttempt: 10,
270
307
 
271
- maxAckWaitTime: 6 * 10 * 1000, // 6 min.
308
+ maxAckWaitTime: 10 * 60 * 1000, // 10 mins.
272
309
 
273
310
  maxOpsSinceLastSummary: 7000,
274
311
 
275
- initialSummarizerDelayMs: 5000, // 5 secs.
312
+ initialSummarizerDelayMs: 5 * 1000, // 5 secs.
276
313
 
277
314
  summarizerClientElection: false,
315
+
316
+ nonRuntimeOpWeight: 0.1,
317
+
318
+ runtimeOpWeight: 1.0,
278
319
  };
279
320
 
280
321
  export interface IGCRuntimeOptions {
281
322
  /**
282
- * Flag that if true, will enable running garbage collection (GC) in a container. GC has mark phase and sweep phase.
283
- * In mark phase, unreferenced objects are identified and marked as such in the summary. This option enables the
284
- * mark phase.
323
+ * Flag that if true, will enable running garbage collection (GC) for a new container.
324
+ *
325
+ * GC has mark phase and sweep phase. In mark phase, unreferenced objects are identified
326
+ * and marked as such in the summary. This option enables the mark phase.
285
327
  * In sweep phase, unreferenced objects are eventually deleted from the container if they meet certain conditions.
286
328
  * Sweep phase can be enabled via the "sweepAllowed" option.
287
- * Note: This setting becomes part of the container's summary and cannot be changed.
329
+ *
330
+ * Note: This setting is persisted in the container's summary and cannot be changed.
288
331
  */
289
332
  gcAllowed?: boolean;
290
333
 
291
334
  /**
292
- * Flag that if true, enables GC's sweep phase which will eventually delete unreferenced objects from the container.
335
+ * Flag that if true, enables GC's sweep phase for a new container.
336
+ *
337
+ * This will allow GC to eventually delete unreferenced objects from the container.
293
338
  * This flag should only be set to true if "gcAllowed" is true.
294
- * Note: This setting becomes part of the container's summary and cannot be changed.
339
+ *
340
+ * Note: This setting is persisted in the container's summary and cannot be changed.
295
341
  */
296
342
  sweepAllowed?: boolean;
297
343
 
298
344
  /**
299
- * Flag that will disable garbage collection if set to true. Can be used to disable running GC on container where
300
- * is allowed via the gcAllowed option.
345
+ * Flag that if true, will disable garbage collection for the session.
346
+ * Can be used to disable running GC on containers where it is allowed via the gcAllowed option.
301
347
  */
302
348
  disableGC?: boolean;
303
349
 
@@ -307,6 +353,13 @@ export interface IGCRuntimeOptions {
307
353
  */
308
354
  runFullGC?: boolean;
309
355
 
356
+ /**
357
+ * Maximum session duration for a new container. If not present, a default value will be used.
358
+ *
359
+ * Note: This setting is persisted in the container's summary and cannot be changed.
360
+ */
361
+ sessionExpiryTimeoutMs?: number;
362
+
310
363
  /**
311
364
  * Allows additional GC options to be passed.
312
365
  */
@@ -318,9 +371,12 @@ export interface ISummaryRuntimeOptions {
318
371
  /** Override summary configurations set by the server. */
319
372
  summaryConfigOverrides?: ISummaryConfiguration;
320
373
 
321
- // Flag that disables putting channels in isolated subtrees for each data store
322
- // and the root node when generating a summary if set to true.
323
- // Defaults to FALSE (enabled) for now.
374
+ /**
375
+ * @deprecated - this option will not be supported on the next versions.
376
+ * Flag that disables putting channels in isolated subtrees for each data store
377
+ * and the root node when generating a summary if set to true.
378
+ * Defaults to FALSE (enabled) for now.
379
+ */
324
380
  disableIsolatedChannels?: boolean;
325
381
 
326
382
  /**
@@ -466,11 +522,6 @@ const maxOpSizeInBytesKey = "Fluid.ContainerRuntime.MaxOpSizeInBytes";
466
522
  // to not reach the 1MB limits in socket.io and Kafka.
467
523
  const defaultMaxOpSizeInBytes = 768000;
468
524
 
469
- // By default, the size of the contents for the incoming ops is tracked.
470
- // However, in certain situations, this may incur a performance hit.
471
- // The feature-gate below can be used to disable this feature.
472
- const disableOpTrackingKey = "Fluid.ContainerRuntime.DisableOpTracking";
473
-
474
525
  const defaultFlushMode = FlushMode.TurnBased;
475
526
 
476
527
  export enum RuntimeMessage {
@@ -1036,6 +1087,12 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1036
1087
 
1037
1088
  private consecutiveReconnects = 0;
1038
1089
 
1090
+ /**
1091
+ * Used to delay transition to "connected" state while we upload
1092
+ * attachment blobs that were added while disconnected
1093
+ */
1094
+ private delayConnectClientId?: string;
1095
+
1039
1096
  public get connected(): boolean {
1040
1097
  return this._connected;
1041
1098
  }
@@ -1160,7 +1217,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1160
1217
  * a summary is generated.
1161
1218
  */
1162
1219
  private nextSummaryNumber: number;
1163
- private readonly opTracker: OpTracker;
1164
1220
 
1165
1221
  private constructor(
1166
1222
  private readonly context: IContainerContext,
@@ -1197,6 +1253,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1197
1253
  this.mc = loggerToMonitoringContext(
1198
1254
  ChildLogger.create(this.logger, "ContainerRuntime"));
1199
1255
 
1256
+ if (this.summaryConfiguration.state === "enabled") {
1257
+ this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
1258
+ }
1259
+
1200
1260
  this.summariesDisabled = this.isSummariesDisabled();
1201
1261
  this.heuristicsDisabled = this.isHeuristicsDisabled();
1202
1262
  this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
@@ -1288,10 +1348,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1288
1348
  this.handleContext,
1289
1349
  blobManagerSnapshot,
1290
1350
  () => this.storage,
1291
- (blobId: string) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }),
1351
+ (blobId, localId) => this.submit(
1352
+ ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId }),
1292
1353
  (blobPath: string) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"),
1293
1354
  this,
1294
- this.logger,
1295
1355
  );
1296
1356
 
1297
1357
  this.scheduleManager = new ScheduleManager(
@@ -1442,9 +1502,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1442
1502
  createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
1443
1503
  createContainerTimestamp: metadata?.createContainerTimestamp,
1444
1504
  };
1445
- // back-compat 0.59.3000 - Older document may either write summaryCount or not write it at all. If it does
1446
- // not write it, initialize summaryNumber to 0.
1447
- loadSummaryNumber = metadata?.summaryNumber ?? metadata?.summaryCount ?? 0;
1505
+ // summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
1506
+ // the count is reset to 0.
1507
+ loadSummaryNumber = metadata?.summaryNumber ?? 0;
1448
1508
  } else {
1449
1509
  this.createContainerMetadata = {
1450
1510
  createContainerRuntimeVersion: pkgVersion,
@@ -1466,7 +1526,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1466
1526
 
1467
1527
  ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
1468
1528
  BindBatchTracker(this, this.logger);
1469
- this.opTracker = new OpTracker(this.deltaManager, this.mc.config.getBoolean(disableOpTrackingKey) === true);
1470
1529
  }
1471
1530
 
1472
1531
  public dispose(error?: Error): void {
@@ -1546,12 +1605,12 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1546
1605
  }
1547
1606
 
1548
1607
  if (id === BlobManager.basePath && requestParser.isLeaf(2)) {
1549
- const handle = await this.blobManager.getBlob(requestParser.pathParts[1]);
1550
- if (handle) {
1608
+ const blob = await this.blobManager.getBlob(requestParser.pathParts[1]);
1609
+ if (blob) {
1551
1610
  return {
1552
1611
  status: 200,
1553
1612
  mimeType: "fluid/object",
1554
- value: handle.get(),
1613
+ value: blob,
1555
1614
  };
1556
1615
  } else {
1557
1616
  return create404Response(request);
@@ -1573,7 +1632,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1573
1632
  }
1574
1633
 
1575
1634
  private internalId(maybeAlias: string): string {
1576
- return this.dataStores.aliases().get(maybeAlias) ?? maybeAlias;
1635
+ return this.dataStores.aliases.get(maybeAlias) ?? maybeAlias;
1577
1636
  }
1578
1637
 
1579
1638
  private async getDataStoreFromRequest(id: string, request: IRequest): Promise<IFluidRouter> {
@@ -1581,6 +1640,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1581
1640
  ? request.headers?.[RuntimeHeaders.wait]
1582
1641
  : true;
1583
1642
 
1643
+ await this.dataStores.waitIfPendingAlias(id);
1584
1644
  const internalId = this.internalId(id);
1585
1645
  const dataStoreContext = await this.dataStores.getDataStore(internalId, wait);
1586
1646
 
@@ -1620,8 +1680,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1620
1680
  private addMetadataToSummary(summaryTree: ISummaryTreeWithStats) {
1621
1681
  const metadata: IContainerRuntimeMetadata = {
1622
1682
  ...this.createContainerMetadata,
1623
- // back-compat 0.59.3000: This is renamed to summaryNumber. Can be removed when 0.59.3000 saturates.
1624
- summaryCount: this.nextSummaryNumber,
1625
1683
  // Increment the summary number for the next summary that will be generated.
1626
1684
  summaryNumber: this.nextSummaryNumber++,
1627
1685
  summaryFormatVersion: 1,
@@ -1647,7 +1705,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1647
1705
  addBlobToSummary(summaryTree, chunksBlobName, content);
1648
1706
  }
1649
1707
 
1650
- const dataStoreAliases = this.dataStores.aliases();
1708
+ const dataStoreAliases = this.dataStores.aliases;
1651
1709
  if (dataStoreAliases.size > 0) {
1652
1710
  addBlobToSummary(summaryTree, aliasBlobName, JSON.stringify([...dataStoreAliases]));
1653
1711
  }
@@ -1757,6 +1815,38 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1757
1815
  }
1758
1816
 
1759
1817
  public setConnectionState(connected: boolean, clientId?: string) {
1818
+ if (connected === false && this.delayConnectClientId !== undefined) {
1819
+ this.delayConnectClientId = undefined;
1820
+ this.mc.logger.sendTelemetryEvent({
1821
+ eventName: "UnsuccessfulConnectedTransition",
1822
+ });
1823
+ // Don't propagate "disconnected" event because we didn't propagate the previous "connected" event
1824
+ return;
1825
+ }
1826
+
1827
+ // If attachment blobs were added while disconnected, we need to delay
1828
+ // propagation of the "connected" event until we have uploaded them to
1829
+ // ensure we don't submit ops referencing a blob that has not been uploaded
1830
+ const connecting = connected && !this._connected && !this.deltaManager.readOnlyInfo.readonly;
1831
+ if (connecting && this.blobManager.hasPendingOfflineUploads) {
1832
+ assert(!this.delayConnectClientId, "Connect event delay must be canceled before subsequent connect event");
1833
+ assert(!!clientId, "Must have clientId when connecting");
1834
+ this.delayConnectClientId = clientId;
1835
+ this.blobManager.onConnected().then(() => {
1836
+ // make sure we didn't reconnect before the promise resolved
1837
+ if (this.delayConnectClientId === clientId && !this.disposed) {
1838
+ this.delayConnectClientId = undefined;
1839
+ this.setConnectionStateCore(connected, clientId);
1840
+ }
1841
+ }, (error) => this.closeFn(error));
1842
+ return;
1843
+ }
1844
+
1845
+ this.setConnectionStateCore(connected, clientId);
1846
+ }
1847
+
1848
+ private setConnectionStateCore(connected: boolean, clientId?: string) {
1849
+ assert(!this.delayConnectClientId, "connect event delay must be cleared before propagating connect event");
1760
1850
  this.verifyNotClosed();
1761
1851
 
1762
1852
  // There might be no change of state due to Container calling this API after loading runtime.
@@ -1854,8 +1944,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1854
1944
  this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
1855
1945
  break;
1856
1946
  case ContainerMessageType.BlobAttach:
1857
- assert(message?.metadata?.blobId, 0x12a /* "Missing blob id on metadata" */);
1858
- this.blobManager.processBlobAttachOp(message.metadata.blobId, local);
1947
+ this.blobManager.processBlobAttachOp(message, local);
1859
1948
  break;
1860
1949
  default:
1861
1950
  }
@@ -1937,6 +2026,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1937
2026
  }
1938
2027
 
1939
2028
  public async getRootDataStore(id: string, wait = true): Promise<IFluidRouter> {
2029
+ return this.getRootDataStoreChannel(id, wait);
2030
+ }
2031
+
2032
+ private async getRootDataStoreChannel(id: string, wait = true): Promise<IFluidDataStoreChannel> {
2033
+ await this.dataStores.waitIfPendingAlias(id);
1940
2034
  const internalId = this.internalId(id);
1941
2035
  const context = await this.dataStores.getDataStore(internalId, wait);
1942
2036
  assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
@@ -2058,13 +2152,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2058
2152
  */
2059
2153
  private async createRootDataStoreLegacy(pkg: string | string[], rootDataStoreId: string): Promise<IFluidRouter> {
2060
2154
  const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
2061
- // back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel. For
2062
- // older versions, we still have to call bindToContext.
2063
- if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
2064
- fluidDataStore.makeVisibleAndAttachGraph();
2065
- } else {
2066
- fluidDataStore.bindToContext();
2067
- }
2155
+ fluidDataStore.makeVisibleAndAttachGraph();
2068
2156
  return fluidDataStore;
2069
2157
  }
2070
2158
 
@@ -2093,27 +2181,37 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2093
2181
  */
2094
2182
  private async createAndAliasDataStore(pkg: string | string[], alias: string, props?: any): Promise<IDataStore> {
2095
2183
  const internalId = uuid();
2096
- const dataStore = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
2097
- const aliasedDataStore = channelToDataStore(dataStore, internalId, this, this.dataStores, this.mc.logger);
2098
- const result = await aliasedDataStore.trySetAlias(alias);
2099
- if (result !== "Success") {
2100
- throw new GenericError(
2101
- "dataStoreAliasFailure",
2102
- undefined /* error */,
2103
- {
2104
- alias: {
2105
- value: alias,
2106
- tag: TelemetryDataTag.UserData,
2107
- },
2108
- internalId: {
2109
- value: internalId,
2110
- tag: TelemetryDataTag.PackageData,
2111
- },
2112
- aliasResult: result,
2113
- });
2114
- }
2115
2184
 
2116
- return aliasedDataStore;
2185
+ try {
2186
+ // A similar call may have been initiated by the same client, so we should try to get
2187
+ // a possible existing aliased datastore first.
2188
+ const existingDataStore = await this.getRootDataStoreChannel(alias, /* wait */ false);
2189
+ return channelToDataStore(
2190
+ existingDataStore,
2191
+ internalId,
2192
+ this,
2193
+ this.dataStores,
2194
+ this.mc.logger,
2195
+ true, // AlreadyAliased. This will block further alias attempts for the datastore
2196
+ );
2197
+ } catch (err) {
2198
+ const newChannel = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
2199
+ const newDataStore = channelToDataStore(newChannel, internalId, this, this.dataStores, this.mc.logger);
2200
+ const aliasResult = await newDataStore.trySetAlias(alias);
2201
+ if (aliasResult === "Success") {
2202
+ return newDataStore;
2203
+ }
2204
+
2205
+ const existingDataStore = await this.getRootDataStoreChannel(alias, /* wait */ false);
2206
+ return channelToDataStore(
2207
+ existingDataStore,
2208
+ internalId,
2209
+ this,
2210
+ this.dataStores,
2211
+ this.mc.logger,
2212
+ true, // AlreadyAliased. This will block further alias attempts for the datastore
2213
+ );
2214
+ }
2117
2215
  }
2118
2216
 
2119
2217
  public createDetachedRootDataStore(
@@ -2144,13 +2242,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2144
2242
  const fluidDataStore = await this.dataStores._createFluidDataStoreContext(
2145
2243
  Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
2146
2244
  if (isRoot) {
2147
- // back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel.
2148
- // For older versions, we still have to call bindToContext.
2149
- if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
2150
- fluidDataStore.makeVisibleAndAttachGraph();
2151
- } else {
2152
- fluidDataStore.bindToContext();
2153
- }
2245
+ fluidDataStore.makeVisibleAndAttachGraph();
2154
2246
  this.logger.sendTelemetryEvent({
2155
2247
  eventName: "Root datastore with props",
2156
2248
  hasProps: props !== undefined,
@@ -2560,15 +2652,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2560
2652
  const summaryRefSeqNum = this.deltaManager.lastSequenceNumber;
2561
2653
  const minimumSequenceNumber = this.deltaManager.minimumSequenceNumber;
2562
2654
  const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
2563
-
2564
- // We should be here is we haven't processed be here. If we are of if the last message's sequence number
2565
- // doesn't match the last processed sequence number, log an error.
2566
- if (summaryRefSeqNum !== this.deltaManager.lastMessage?.sequenceNumber) {
2567
- summaryNumberLogger.sendErrorEvent({
2568
- eventName: "LastSequenceMismatch",
2569
- error: message,
2570
- });
2571
- }
2655
+ const lastAck = this.summaryCollection.latestAck;
2572
2656
 
2573
2657
  this.summarizerNode.startSummary(summaryRefSeqNum, summaryNumberLogger);
2574
2658
 
@@ -2598,6 +2682,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2598
2682
  error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
2599
2683
  };
2600
2684
  }
2685
+ assert(summaryRefSeqNum === this.deltaManager.lastMessage?.sequenceNumber,
2686
+ "it's one and the same thing");
2687
+
2688
+ if (lastAck !== this.summaryCollection.latestAck) {
2689
+ return {
2690
+ continue: false,
2691
+ // eslint-disable-next-line max-len
2692
+ error: `Last summary changed while summarizing. ${this.summaryCollection.latestAck} !== ${lastAck}`,
2693
+ };
2694
+ }
2601
2695
  return { continue: true };
2602
2696
  };
2603
2697
 
@@ -2654,8 +2748,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2654
2748
  gcStateUpdatedDataStoreCount: summarizeResult.gcStats?.updatedDataStoreCount,
2655
2749
  gcBlobNodeCount: gcSummaryTreeStats?.blobNodeCount,
2656
2750
  gcTotalBlobsSize: gcSummaryTreeStats?.totalBlobSize,
2657
- opsSizesSinceLastSummary: this.opTracker.opsSizeAccumulator,
2658
- nonSystemOpsSinceLastSummary: this.opTracker.nonSystemOpCount,
2659
2751
  summaryNumber,
2660
2752
  ...partialStats,
2661
2753
  };
@@ -2673,7 +2765,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2673
2765
  return { stage: "generate", ...generateSummaryData, error: continueResult.error };
2674
2766
  }
2675
2767
 
2676
- const lastAck = this.summaryCollection.latestAck;
2677
2768
  const summaryContext: ISummaryContext =
2678
2769
  lastAck === undefined
2679
2770
  ? {
@@ -2728,7 +2819,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2728
2819
  } as const;
2729
2820
 
2730
2821
  this.summarizerNode.completeSummary(handle);
2731
- this.opTracker.reset();
2732
2822
  return submitData;
2733
2823
  } finally {
2734
2824
  // Cleanup wip summary in case of failure
@@ -2892,14 +2982,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2892
2982
  "OpTooLarge",
2893
2983
  /* error */ undefined,
2894
2984
  {
2895
- length: {
2896
- value: serializedContent.length,
2897
- tag: TelemetryDataTag.PackageData,
2898
- },
2899
- limit: {
2900
- value: this._maxOpSizeInBytes,
2901
- tag: TelemetryDataTag.PackageData,
2902
- },
2985
+ length: serializedContent.length,
2986
+ limit: this._maxOpSizeInBytes,
2903
2987
  }));
2904
2988
  return -1;
2905
2989
  }
@@ -3005,7 +3089,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
3005
3089
  case ContainerMessageType.ChunkedOp:
3006
3090
  throw new Error(`chunkedOp not expected here`);
3007
3091
  case ContainerMessageType.BlobAttach:
3008
- this.submit(type, content, localOpMetadata, opMetadata);
3092
+ this.blobManager.reSubmit(opMetadata);
3009
3093
  break;
3010
3094
  case ContainerMessageType.Rejoin:
3011
3095
  this.submit(type, content);
@@ -3064,9 +3148,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
3064
3148
  */
3065
3149
  private async refreshLatestSummaryAckFromServer(summaryLogger: ITelemetryLogger): Promise<number> {
3066
3150
  const snapshot = await this.fetchSnapshotFromStorage(null, summaryLogger, {
3067
- eventName: "RefreshLatestSummaryGetSnapshot",
3068
- fetchLatest: true,
3069
- });
3151
+ eventName: "RefreshLatestSummaryGetSnapshot",
3152
+ fetchLatest: true,
3153
+ },
3154
+ FetchSource.noCache,
3155
+ );
3070
3156
 
3071
3157
  const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
3072
3158
  const snapshotRefSeq = await seqFromTree(snapshot, readAndParseBlob);
@@ -3086,7 +3172,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
3086
3172
  }
3087
3173
 
3088
3174
  private async fetchSnapshotFromStorage(
3089
- versionId: string | null, logger: ITelemetryLogger, event: ITelemetryGenericEvent) {
3175
+ versionId: string | null,
3176
+ logger: ITelemetryLogger,
3177
+ event: ITelemetryGenericEvent,
3178
+ fetchSource?: FetchSource,
3179
+ ) {
3090
3180
  return PerformanceEvent.timedExecAsync(
3091
3181
  logger, event, async (perfEvent: {
3092
3182
  end: (arg0: {
@@ -3097,7 +3187,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
3097
3187
  const stats: { getVersionDuration?: number; getSnapshotDuration?: number; } = {};
3098
3188
  const trace = Trace.start();
3099
3189
 
3100
- const versions = await this.storage.getVersions(versionId, 1);
3190
+ const versions = await this.storage.getVersions(
3191
+ versionId, 1, "refreshLatestSummaryAckFromServer", fetchSource);
3101
3192
  assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
3102
3193
  stats.getVersionDuration = trace.trace().duration;
3103
3194
 
@@ -3218,6 +3309,15 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
3218
3309
  // don't have any more saved ops
3219
3310
  await this.pendingStateManager.applyStashedOpsAt();
3220
3311
  }
3312
+
3313
+ private validateSummaryHeuristicConfiguration(configuration: ISummaryConfigurationHeuristics) {
3314
+ // eslint-disable-next-line no-restricted-syntax
3315
+ for (const prop in configuration) {
3316
+ if (typeof configuration[prop] === "number" && configuration[prop] < 0) {
3317
+ throw new UsageError(`Summary heuristic configuration property "${prop}" cannot be less than 0`);
3318
+ }
3319
+ }
3320
+ }
3221
3321
  }
3222
3322
 
3223
3323
  /**