@fluidframework/container-runtime 2.0.0-dev.1.4.5.105745 → 2.0.0-dev.2.2.0.111723

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 (168) hide show
  1. package/.eslintrc.js +1 -1
  2. package/dist/batchManager.d.ts +11 -6
  3. package/dist/batchManager.d.ts.map +1 -1
  4. package/dist/batchManager.js +23 -13
  5. package/dist/batchManager.js.map +1 -1
  6. package/dist/containerRuntime.d.ts +74 -20
  7. package/dist/containerRuntime.d.ts.map +1 -1
  8. package/dist/containerRuntime.js +190 -137
  9. package/dist/containerRuntime.js.map +1 -1
  10. package/dist/dataStore.d.ts.map +1 -1
  11. package/dist/dataStore.js +6 -0
  12. package/dist/dataStore.js.map +1 -1
  13. package/dist/dataStoreContext.d.ts +14 -21
  14. package/dist/dataStoreContext.d.ts.map +1 -1
  15. package/dist/dataStoreContext.js +71 -57
  16. package/dist/dataStoreContext.js.map +1 -1
  17. package/dist/dataStoreContexts.js +1 -1
  18. package/dist/dataStoreContexts.js.map +1 -1
  19. package/dist/dataStores.d.ts +11 -10
  20. package/dist/dataStores.d.ts.map +1 -1
  21. package/dist/dataStores.js +50 -20
  22. package/dist/dataStores.js.map +1 -1
  23. package/dist/garbageCollection.d.ts +36 -19
  24. package/dist/garbageCollection.d.ts.map +1 -1
  25. package/dist/garbageCollection.js +207 -121
  26. package/dist/garbageCollection.js.map +1 -1
  27. package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
  28. package/dist/gcSweepReadyUsageDetection.js +3 -12
  29. package/dist/gcSweepReadyUsageDetection.js.map +1 -1
  30. package/dist/index.d.ts +4 -6
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +3 -5
  33. package/dist/index.js.map +1 -1
  34. package/dist/opCompressor.d.ts +18 -0
  35. package/dist/opCompressor.d.ts.map +1 -0
  36. package/dist/opCompressor.js +50 -0
  37. package/dist/opCompressor.js.map +1 -0
  38. package/dist/opDecompressor.d.ts +20 -0
  39. package/dist/opDecompressor.d.ts.map +1 -0
  40. package/dist/opDecompressor.js +72 -0
  41. package/dist/opDecompressor.js.map +1 -0
  42. package/dist/packageVersion.d.ts +1 -1
  43. package/dist/packageVersion.js +1 -1
  44. package/dist/packageVersion.js.map +1 -1
  45. package/dist/pendingStateManager.d.ts +6 -26
  46. package/dist/pendingStateManager.d.ts.map +1 -1
  47. package/dist/pendingStateManager.js +42 -62
  48. package/dist/pendingStateManager.js.map +1 -1
  49. package/dist/runningSummarizer.d.ts +3 -2
  50. package/dist/runningSummarizer.d.ts.map +1 -1
  51. package/dist/runningSummarizer.js +10 -3
  52. package/dist/runningSummarizer.js.map +1 -1
  53. package/dist/scheduleManager.js.map +1 -1
  54. package/dist/summarizer.js +7 -2
  55. package/dist/summarizer.js.map +1 -1
  56. package/dist/summarizerClientElection.js +1 -1
  57. package/dist/summarizerClientElection.js.map +1 -1
  58. package/dist/summarizerHeuristics.d.ts.map +1 -1
  59. package/dist/summarizerHeuristics.js +0 -3
  60. package/dist/summarizerHeuristics.js.map +1 -1
  61. package/dist/summarizerTypes.d.ts +19 -2
  62. package/dist/summarizerTypes.d.ts.map +1 -1
  63. package/dist/summarizerTypes.js.map +1 -1
  64. package/dist/summaryFormat.d.ts +4 -2
  65. package/dist/summaryFormat.d.ts.map +1 -1
  66. package/dist/summaryFormat.js.map +1 -1
  67. package/dist/summaryGenerator.d.ts.map +1 -1
  68. package/dist/summaryGenerator.js +3 -2
  69. package/dist/summaryGenerator.js.map +1 -1
  70. package/dist/summaryManager.d.ts.map +1 -1
  71. package/dist/summaryManager.js +10 -6
  72. package/dist/summaryManager.js.map +1 -1
  73. package/garbageCollection.md +27 -22
  74. package/lib/batchManager.d.ts +11 -6
  75. package/lib/batchManager.d.ts.map +1 -1
  76. package/lib/batchManager.js +23 -13
  77. package/lib/batchManager.js.map +1 -1
  78. package/lib/containerRuntime.d.ts +74 -20
  79. package/lib/containerRuntime.d.ts.map +1 -1
  80. package/lib/containerRuntime.js +189 -136
  81. package/lib/containerRuntime.js.map +1 -1
  82. package/lib/dataStore.d.ts.map +1 -1
  83. package/lib/dataStore.js +6 -0
  84. package/lib/dataStore.js.map +1 -1
  85. package/lib/dataStoreContext.d.ts +14 -21
  86. package/lib/dataStoreContext.d.ts.map +1 -1
  87. package/lib/dataStoreContext.js +75 -61
  88. package/lib/dataStoreContext.js.map +1 -1
  89. package/lib/dataStoreContexts.js +1 -1
  90. package/lib/dataStoreContexts.js.map +1 -1
  91. package/lib/dataStores.d.ts +11 -10
  92. package/lib/dataStores.d.ts.map +1 -1
  93. package/lib/dataStores.js +53 -23
  94. package/lib/dataStores.js.map +1 -1
  95. package/lib/garbageCollection.d.ts +36 -19
  96. package/lib/garbageCollection.d.ts.map +1 -1
  97. package/lib/garbageCollection.js +208 -122
  98. package/lib/garbageCollection.js.map +1 -1
  99. package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
  100. package/lib/gcSweepReadyUsageDetection.js +3 -12
  101. package/lib/gcSweepReadyUsageDetection.js.map +1 -1
  102. package/lib/index.d.ts +4 -6
  103. package/lib/index.d.ts.map +1 -1
  104. package/lib/index.js +2 -4
  105. package/lib/index.js.map +1 -1
  106. package/lib/opCompressor.d.ts +18 -0
  107. package/lib/opCompressor.d.ts.map +1 -0
  108. package/lib/opCompressor.js +46 -0
  109. package/lib/opCompressor.js.map +1 -0
  110. package/lib/opDecompressor.d.ts +20 -0
  111. package/lib/opDecompressor.d.ts.map +1 -0
  112. package/lib/opDecompressor.js +68 -0
  113. package/lib/opDecompressor.js.map +1 -0
  114. package/lib/packageVersion.d.ts +1 -1
  115. package/lib/packageVersion.js +1 -1
  116. package/lib/packageVersion.js.map +1 -1
  117. package/lib/pendingStateManager.d.ts +6 -26
  118. package/lib/pendingStateManager.d.ts.map +1 -1
  119. package/lib/pendingStateManager.js +42 -62
  120. package/lib/pendingStateManager.js.map +1 -1
  121. package/lib/runningSummarizer.d.ts +3 -2
  122. package/lib/runningSummarizer.d.ts.map +1 -1
  123. package/lib/runningSummarizer.js +10 -3
  124. package/lib/runningSummarizer.js.map +1 -1
  125. package/lib/scheduleManager.js.map +1 -1
  126. package/lib/summarizer.js +7 -2
  127. package/lib/summarizer.js.map +1 -1
  128. package/lib/summarizerClientElection.js +1 -1
  129. package/lib/summarizerClientElection.js.map +1 -1
  130. package/lib/summarizerHeuristics.d.ts.map +1 -1
  131. package/lib/summarizerHeuristics.js +0 -3
  132. package/lib/summarizerHeuristics.js.map +1 -1
  133. package/lib/summarizerTypes.d.ts +19 -2
  134. package/lib/summarizerTypes.d.ts.map +1 -1
  135. package/lib/summarizerTypes.js.map +1 -1
  136. package/lib/summaryFormat.d.ts +4 -2
  137. package/lib/summaryFormat.d.ts.map +1 -1
  138. package/lib/summaryFormat.js.map +1 -1
  139. package/lib/summaryGenerator.d.ts.map +1 -1
  140. package/lib/summaryGenerator.js +3 -2
  141. package/lib/summaryGenerator.js.map +1 -1
  142. package/lib/summaryManager.d.ts.map +1 -1
  143. package/lib/summaryManager.js +10 -6
  144. package/lib/summaryManager.js.map +1 -1
  145. package/package.json +37 -63
  146. package/prettier.config.cjs +8 -0
  147. package/src/batchManager.ts +32 -15
  148. package/src/containerRuntime.ts +260 -156
  149. package/src/dataStore.ts +13 -1
  150. package/src/dataStoreContext.ts +100 -76
  151. package/src/dataStoreContexts.ts +1 -1
  152. package/src/dataStores.ts +61 -23
  153. package/src/garbageCollection.ts +257 -126
  154. package/src/gcSweepReadyUsageDetection.ts +2 -10
  155. package/src/index.ts +4 -4
  156. package/src/opCompressor.ts +59 -0
  157. package/src/opDecompressor.ts +82 -0
  158. package/src/packageVersion.ts +1 -1
  159. package/src/pendingStateManager.ts +57 -96
  160. package/src/runningSummarizer.ts +11 -3
  161. package/src/scheduleManager.ts +1 -0
  162. package/src/summarizer.ts +6 -6
  163. package/src/summarizerClientElection.ts +1 -1
  164. package/src/summarizerHeuristics.ts +0 -3
  165. package/src/summarizerTypes.ts +20 -7
  166. package/src/summaryFormat.ts +4 -2
  167. package/src/summaryGenerator.ts +3 -2
  168. package/src/summaryManager.ts +18 -7
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.unpackRuntimeMessage = exports.isRuntimeMessage = exports.RuntimeMessage = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = exports.ContainerMessageType = void 0;
3
+ exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.unpackRuntimeMessage = exports.isRuntimeMessage = exports.RuntimeMessage = exports.CompressionAlgorithms = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = exports.ContainerMessageType = void 0;
4
4
  const container_definitions_1 = require("@fluidframework/container-definitions");
5
5
  const common_utils_1 = require("@fluidframework/common-utils");
6
6
  const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
@@ -33,6 +33,7 @@ const dataStore_1 = require("./dataStore");
33
33
  const batchTracker_1 = require("./batchTracker");
34
34
  const serializedSnapshotStorage_1 = require("./serializedSnapshotStorage");
35
35
  const scheduleManager_1 = require("./scheduleManager");
36
+ const opDecompressor_1 = require("./opDecompressor");
36
37
  var ContainerMessageType;
37
38
  (function (ContainerMessageType) {
38
39
  // An op to be delivered to store
@@ -61,6 +62,7 @@ exports.DefaultSummaryConfiguration = {
61
62
  summarizerClientElection: false,
62
63
  nonRuntimeOpWeight: 0.1,
63
64
  runtimeOpWeight: 1.0,
65
+ nonRuntimeHeuristicThreshold: 20,
64
66
  };
65
67
  /**
66
68
  * Accepted header keys for requests coming to the runtime.
@@ -77,8 +79,20 @@ var RuntimeHeaders;
77
79
  /** True if the request is coming from an IFluidHandle. */
78
80
  RuntimeHeaders["viaHandle"] = "viaHandle";
79
81
  })(RuntimeHeaders = exports.RuntimeHeaders || (exports.RuntimeHeaders = {}));
82
+ /**
83
+ * Available compression algorithms for op compression.
84
+ */
85
+ var CompressionAlgorithms;
86
+ (function (CompressionAlgorithms) {
87
+ CompressionAlgorithms["lz4"] = "lz4";
88
+ })(CompressionAlgorithms = exports.CompressionAlgorithms || (exports.CompressionAlgorithms = {}));
80
89
  const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
81
90
  const defaultFlushMode = runtime_definitions_1.FlushMode.TurnBased;
91
+ // The actual limit is 1Mb (socket.io and Kafka limits)
92
+ // We can't estimate it fully, as we
93
+ // - do not know what properties relay service will add
94
+ // - we do not stringify final op, thus we do not know how much escaping will be added.
95
+ const defaultMaxBatchSizeInBytes = 950 * 1024;
82
96
  /**
83
97
  * @deprecated - use ContainerRuntimeMessage instead
84
98
  */
@@ -96,16 +110,13 @@ var RuntimeMessage;
96
110
  * @deprecated - please use version in driver-utils
97
111
  */
98
112
  function isRuntimeMessage(message) {
99
- if (Object.values(RuntimeMessage).includes(message.type)) {
100
- return true;
101
- }
102
- return false;
113
+ return Object.values(RuntimeMessage).includes(message.type);
103
114
  }
104
115
  exports.isRuntimeMessage = isRuntimeMessage;
105
116
  /**
106
117
  * Unpacks runtime messages
107
118
  *
108
- * @remarks This API makes no promises regarding backward-compatability. This is internal API.
119
+ * @remarks This API makes no promises regarding backward-compatibility. This is internal API.
109
120
  * @param message - message (as it observed in storage / service)
110
121
  * @returns unpacked runtime message
111
122
  *
@@ -120,7 +131,6 @@ function unpackRuntimeMessage(message) {
120
131
  else {
121
132
  // new format
122
133
  const innerContents = message.contents;
123
- (0, common_utils_1.assert)(innerContents.type !== undefined, 0x121 /* "Undefined inner contents type!" */);
124
134
  message.type = innerContents.type;
125
135
  message.contents = innerContents.contents;
126
136
  }
@@ -162,7 +172,7 @@ exports.getDeviceSpec = getDeviceSpec;
162
172
  */
163
173
  class ContainerRuntime extends common_utils_1.TypedEventEmitter {
164
174
  constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
165
- var _a, _b, _c, _d;
175
+ var _a, _b, _c, _d, _e, _f;
166
176
  if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, exports.DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
167
177
  super();
168
178
  this.context = context;
@@ -173,9 +183,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
173
183
  this._storage = _storage;
174
184
  this.requestHandler = requestHandler;
175
185
  this.summaryConfiguration = summaryConfiguration;
186
+ this.opDecompressor = new opDecompressor_1.OpDecompressor();
176
187
  this.defaultMaxConsecutiveReconnects = 7;
177
188
  this._orderSequentiallyCalls = 0;
178
- this.flushTrigger = false;
189
+ this.flushMicroTaskExists = false;
179
190
  this.savedOps = [];
180
191
  this.consecutiveReconnects = 0;
181
192
  this._disposed = false;
@@ -187,12 +198,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
187
198
  signalTimestamp: 0,
188
199
  trackingSignalSequenceNumber: undefined,
189
200
  };
190
- // Provide lower soft limit - we want to have some number of ops to get efficiency in compression & bandwidth usage,
191
- // but at the same time we want to send these ops sooner, to reduce overall latency of processing a batch.
192
- // So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
193
- // payloads. That number represents final (compressed) bits (once compression is implemented).
194
- this.pendingAttachBatch = new batchManager_1.BatchManager(64 * 1024);
195
- this.pendingBatch = new batchManager_1.BatchManager();
196
201
  this.summarizeOnDemand = (...args) => {
197
202
  if (this.clientDetails.type === summarizerClientElection_1.summarizerClientType) {
198
203
  return this.summarizer.summarizeOnDemand(...args);
@@ -221,6 +226,26 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
221
226
  throw new container_utils_1.UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
222
227
  }
223
228
  };
229
+ let loadSummaryNumber;
230
+ // Get the container creation metadata. For new container, we initialize these. For existing containers,
231
+ // get the values from the metadata blob.
232
+ if (existing) {
233
+ this.createContainerMetadata = {
234
+ createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
235
+ createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
236
+ };
237
+ // summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
238
+ // the count is reset to 0.
239
+ loadSummaryNumber = (_b = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _b !== void 0 ? _b : 0;
240
+ }
241
+ else {
242
+ this.createContainerMetadata = {
243
+ createContainerRuntimeVersion: packageVersion_1.pkgVersion,
244
+ createContainerTimestamp: Date.now(),
245
+ };
246
+ loadSummaryNumber = 0;
247
+ }
248
+ this.nextSummaryNumber = loadSummaryNumber + 1;
224
249
  this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
225
250
  this._connected = this.context.connected;
226
251
  this.chunkMap = new Map(chunks);
@@ -235,10 +260,31 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
235
260
  this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
236
261
  this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
237
262
  this.maxConsecutiveReconnects =
238
- (_b = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _b !== void 0 ? _b : this.defaultMaxConsecutiveReconnects;
263
+ (_c = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _c !== void 0 ? _c : this.defaultMaxConsecutiveReconnects;
239
264
  this._flushMode = runtimeOptions.flushMode;
265
+ // Provide lower soft limit - we want to have some number of ops to get efficiency in compression
266
+ // & bandwidth usage, but at the same time we want to send these ops sooner, to reduce overall
267
+ // latency of processing a batch.
268
+ // So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
269
+ // payloads. That number represents final (compressed) bits (once compression is implemented).
270
+ this.pendingAttachBatch = new batchManager_1.BatchManager(this.mc.logger, {
271
+ hardLimit: runtimeOptions.maxBatchSizeInBytes,
272
+ softLimit: 64 * 1024,
273
+ compressionOptions: runtimeOptions.compressionOptions
274
+ });
275
+ this.pendingBatch = new batchManager_1.BatchManager(this.mc.logger, {
276
+ hardLimit: runtimeOptions.maxBatchSizeInBytes,
277
+ compressionOptions: runtimeOptions.compressionOptions
278
+ });
240
279
  const pendingRuntimeState = context.pendingLocalState;
241
- const baseSnapshot = (_c = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _c !== void 0 ? _c : context.baseSnapshot;
280
+ const baseSnapshot = (_d = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _d !== void 0 ? _d : context.baseSnapshot;
281
+ const maxSnapshotCacheDurationMs = (_f = (_e = this._storage) === null || _e === void 0 ? void 0 : _e.policies) === null || _f === void 0 ? void 0 : _f.maximumCacheDurationMs;
282
+ if (maxSnapshotCacheDurationMs !== undefined && maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
283
+ // This is a runtime enforcement of what's already explicit in the policy's type itself,
284
+ // which dictates the value is either undefined or exactly 5 days in ms.
285
+ // As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
286
+ throw new container_utils_1.UsageError("Driver's maximumCacheDurationMs policy cannot exceed 5 days");
287
+ }
242
288
  this.garbageCollector = garbageCollection_1.GarbageCollector.create({
243
289
  runtime: this,
244
290
  gcOptions: this.runtimeOptions.gcOptions,
@@ -246,6 +292,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
246
292
  baseLogger: this.mc.logger,
247
293
  existing,
248
294
  metadata,
295
+ createContainerMetadata: this.createContainerMetadata,
249
296
  isSummarizerClient: this.context.clientDetails.type === summarizerClientElection_1.summarizerClientType,
250
297
  getNodePackagePath: async (nodePath) => this.getGCNodePackagePath(nodePath),
251
298
  getLastSummaryTimestampMs: () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; },
@@ -271,9 +318,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
271
318
  gcDisabled: !this.garbageCollector.shouldRunGC,
272
319
  });
273
320
  if (baseSnapshot) {
274
- this.summarizerNode.loadBaseSummaryWithoutDifferential(baseSnapshot);
321
+ this.summarizerNode.updateBaseSummaryState(baseSnapshot);
275
322
  }
276
- this.dataStores = new dataStores_1.DataStores((0, dataStores_1.getSummaryForDatastores)(baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getBaseGCDetails(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap), this.garbageCollector.writeDataAtRoot);
323
+ this.dataStores = new dataStores_1.DataStores((0, dataStores_1.getSummaryForDatastores)(baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getBaseGCDetails(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap));
277
324
  this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId, localId) => {
278
325
  if (!this.disposed) {
279
326
  this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId });
@@ -287,10 +334,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
287
334
  close: this.closeFn,
288
335
  connected: () => this.connected,
289
336
  flush: this.flush.bind(this),
290
- flushMode: () => this.flushMode,
291
337
  reSubmit: this.reSubmit.bind(this),
292
- setFlushMode: (mode) => this.setFlushMode(mode),
293
- }, this._flushMode, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
338
+ rollback: this.rollback.bind(this),
339
+ orderSequentially: this.orderSequentially.bind(this),
340
+ }, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
294
341
  this.context.quorum.on("removeMember", (clientId) => {
295
342
  this.clearPartialChunks(clientId);
296
343
  });
@@ -314,7 +361,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
314
361
  // if summaries are enabled and we are not the summarizer client.
315
362
  const defaultAction = () => {
316
363
  if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
317
- this.logger.sendErrorEvent({ eventName: "SummaryStatus:Behind" });
364
+ this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
318
365
  // unregister default to no log on every op after falling behind
319
366
  // and register summary ack handler to re-register this handler
320
367
  // after successful summary
@@ -360,26 +407,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
360
407
  });
361
408
  // logging hardware telemetry
362
409
  logger.sendTelemetryEvent(Object.assign({ eventName: "DeviceSpec" }, getDeviceSpec()));
363
- let loadSummaryNumber;
364
- // Get the container creation metadata. For new container, we initialize these. For existing containers,
365
- // get the values from the metadata blob.
366
- if (existing) {
367
- this.createContainerMetadata = {
368
- createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
369
- createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
370
- };
371
- // summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
372
- // the count is reset to 0.
373
- loadSummaryNumber = (_d = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _d !== void 0 ? _d : 0;
374
- }
375
- else {
376
- this.createContainerMetadata = {
377
- createContainerRuntimeVersion: packageVersion_1.pkgVersion,
378
- createContainerTimestamp: Date.now(),
379
- };
380
- loadSummaryNumber = 0;
381
- }
382
- this.nextSummaryNumber = loadSummaryNumber + 1;
383
410
  this.logger.sendTelemetryEvent(Object.assign(Object.assign(Object.assign({ eventName: "ContainerLoadStats" }, this.createContainerMetadata), this.dataStores.containerLoadStats), { summaryNumber: loadSummaryNumber, summaryFormatVersion: metadata === null || metadata === void 0 ? void 0 : metadata.summaryFormatVersion, disableIsolatedChannels: metadata === null || metadata === void 0 ? void 0 : metadata.disableIsolatedChannels, gcVersion: metadata === null || metadata === void 0 ? void 0 : metadata.gcFeature }));
384
411
  (0, connectionTelemetry_1.ReportOpPerfTelemetry)(this.context.clientId, this.deltaManager, this.logger);
385
412
  (0, batchTracker_1.BindBatchTracker)(this, this.logger);
@@ -405,7 +432,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
405
432
  runtimeVersion: packageVersion_1.pkgVersion,
406
433
  },
407
434
  });
408
- const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, } = runtimeOptions;
435
+ const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = { minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
436
+ compressionAlgorithm: CompressionAlgorithms.lz4 }, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
409
437
  const pendingRuntimeState = context.pendingLocalState;
410
438
  const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
411
439
  const storage = !pendingRuntimeState ?
@@ -460,13 +488,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
460
488
  loadSequenceNumberVerification,
461
489
  flushMode,
462
490
  enableOfflineLoad,
491
+ compressionOptions,
492
+ maxBatchSizeInBytes,
463
493
  }, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
464
494
  if (pendingRuntimeState) {
465
495
  await runtime.processSavedOps(pendingRuntimeState);
466
496
  // delete these once runtime has seen them to save space
467
497
  pendingRuntimeState.savedOps = [];
468
498
  }
469
- await runtime.getSnapshotBlobs();
499
+ // Initialize the base state of the runtime before it's returned.
500
+ await runtime.initializeBaseState();
470
501
  return runtime;
471
502
  }
472
503
  get options() {
@@ -573,6 +604,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
573
604
  ? this.summaryConfiguration.initialSummarizerDelayMs
574
605
  : 0;
575
606
  }
607
+ /**
608
+ * Initializes the state from the base snapshot this container runtime loaded from.
609
+ */
610
+ async initializeBaseState() {
611
+ await this.initializeBaseSnapshotBlobs();
612
+ await this.garbageCollector.initializeBaseState();
613
+ }
576
614
  dispose(error) {
577
615
  var _a;
578
616
  if (this._disposed) {
@@ -671,13 +709,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
671
709
  return (_a = this.dataStores.aliases.get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
672
710
  }
673
711
  async getDataStoreFromRequest(id, request) {
674
- var _a, _b, _c;
712
+ var _a, _b, _c, _d, _e;
675
713
  const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
676
714
  ? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait]
677
715
  : true;
716
+ const viaHandle = typeof ((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.viaHandle]) === "boolean"
717
+ ? (_d = request.headers) === null || _d === void 0 ? void 0 : _d[RuntimeHeaders.viaHandle]
718
+ : false;
678
719
  await this.dataStores.waitIfPendingAlias(id);
679
720
  const internalId = this.internalId(id);
680
- const dataStoreContext = await this.dataStores.getDataStore(internalId, wait);
721
+ const dataStoreContext = await this.dataStores.getDataStore(internalId, wait, viaHandle);
681
722
  /**
682
723
  * If GC should run and this an external app request with "externalRequest" header, we need to return
683
724
  * an error if the data store being requested is marked as unreferenced as per the data store's base
@@ -686,7 +727,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
686
727
  * This is a workaround to handle scenarios where a data store shared with an external app is deleted
687
728
  * and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
688
729
  */
689
- if (((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
730
+ if (((_e = request.headers) === null || _e === void 0 ? void 0 : _e[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
690
731
  // The data store is referenced if used routes in the base summary has a route to self.
691
732
  // Older documents may not have used routes in the summary. They are considered referenced.
692
733
  const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
@@ -733,11 +774,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
733
774
  if (Object.keys(blobManagerSummary.summary.tree).length > 0) {
734
775
  (0, runtime_utils_1.addTreeToSummary)(summaryTree, summaryFormat_1.blobsTreeName, blobManagerSummary);
735
776
  }
736
- if (this.garbageCollector.writeDataAtRoot) {
737
- const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
738
- if (gcSummary !== undefined) {
739
- (0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree, garbageCollection_1.gcTreeKey, gcSummary);
740
- }
777
+ const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
778
+ if (gcSummary !== undefined) {
779
+ (0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree, garbageCollection_1.gcTreeKey, gcSummary);
741
780
  }
742
781
  }
743
782
  // Track how many times the container tries to reconnect with pending messages.
@@ -893,6 +932,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
893
932
  if (typeof message.contents === "string" && message.contents !== "") {
894
933
  message.contents = JSON.parse(message.contents);
895
934
  }
935
+ message = this.opDecompressor.processMessage(message);
896
936
  // Caveat: This will return false for runtime message in very old format, that are used in snapshot tests
897
937
  // This format was not shipped to production workflows.
898
938
  const runtimeMessage = unpackRuntimeMessage(message);
@@ -1010,27 +1050,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1010
1050
  async getRootDataStoreChannel(id, wait = true) {
1011
1051
  await this.dataStores.waitIfPendingAlias(id);
1012
1052
  const internalId = this.internalId(id);
1013
- const context = await this.dataStores.getDataStore(internalId, wait);
1053
+ const context = await this.dataStores.getDataStore(internalId, wait, false /* viaHandle */);
1014
1054
  (0, common_utils_1.assert)(await context.isRoot(), 0x12b /* "did not get root data store" */);
1015
1055
  return context.realize();
1016
1056
  }
1017
- setFlushMode(mode) {
1018
- if (mode === this._flushMode) {
1019
- return;
1020
- }
1021
- this.mc.logger.sendTelemetryEvent({
1022
- eventName: "FlushMode Updated",
1023
- old: this._flushMode,
1024
- new: mode,
1025
- });
1026
- // Flush any pending batches if switching to immediate
1027
- if (mode === runtime_definitions_1.FlushMode.Immediate) {
1028
- this.flush();
1029
- }
1030
- this._flushMode = mode;
1031
- // Let the PendingStateManager know that FlushMode has been updated.
1032
- this.pendingStateManager.onFlushModeUpdated(mode);
1033
- }
1057
+ /**
1058
+ * Flush the pending ops manually.
1059
+ * This method is expected to be called at the end of a batch.
1060
+ */
1034
1061
  flush() {
1035
1062
  (0, common_utils_1.assert)(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
1036
1063
  this.flushBatch(this.pendingAttachBatch.popBatch());
@@ -1038,6 +1065,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1038
1065
  (0, common_utils_1.assert)(this.emptyBatch, 0x3cf /* reentrancy */);
1039
1066
  }
1040
1067
  flushBatch(batch) {
1068
+ var _a;
1041
1069
  const length = batch.length;
1042
1070
  if (length > 1) {
1043
1071
  batch[0].metadata = Object.assign(Object.assign({}, batch[0].metadata), { batch: true });
@@ -1068,6 +1096,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1068
1096
  // Legacy path - supporting old loader versions. Can be removed only when LTS moves above
1069
1097
  // version that has support for batches (submitBatchFn)
1070
1098
  for (const message of batch) {
1099
+ // Legacy path doesn't support compressed payloads and will submit uncompressed payload anyways
1100
+ if ((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.compressed) {
1101
+ delete message.metadata.compressed;
1102
+ }
1071
1103
  clientSequenceNumber = this.context.submitFn(protocol_definitions_1.MessageType.Operation, message.deserializedContent, true, // batch
1072
1104
  message.metadata);
1073
1105
  }
@@ -1086,26 +1118,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1086
1118
  this.pendingStateManager.onFlush();
1087
1119
  }
1088
1120
  orderSequentially(callback) {
1089
- // If flush mode is already TurnBased we are either
1090
- // nested in another orderSequentially, or
1091
- // the app is flushing manually, in which
1092
- // case this invocation doesn't own
1093
- // flushing.
1094
- if (this.flushMode === runtime_definitions_1.FlushMode.TurnBased) {
1095
- this.trackOrderSequentiallyCalls(callback);
1096
- return;
1097
- }
1098
- const savedFlushMode = this.flushMode;
1099
- this.setFlushMode(runtime_definitions_1.FlushMode.TurnBased);
1100
- try {
1101
- this.trackOrderSequentiallyCalls(callback);
1102
- this.flush();
1103
- }
1104
- finally {
1105
- this.setFlushMode(savedFlushMode);
1106
- }
1107
- }
1108
- trackOrderSequentiallyCalls(callback) {
1109
1121
  let checkpoint;
1110
1122
  if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
1111
1123
  // Note: we are not touching this.pendingAttachBatch here, for two reasons:
@@ -1140,6 +1152,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1140
1152
  finally {
1141
1153
  this._orderSequentiallyCalls--;
1142
1154
  }
1155
+ if (this.flushMode === runtime_definitions_1.FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
1156
+ this.flush();
1157
+ }
1143
1158
  }
1144
1159
  async createDataStore(pkg) {
1145
1160
  const internalId = (0, uuid_1.v4)();
@@ -1166,6 +1181,12 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1166
1181
  canSendOps() {
1167
1182
  return this.connected && !this.deltaManager.readOnlyInfo.readonly;
1168
1183
  }
1184
+ /**
1185
+ * Are we in the middle of batching ops together?
1186
+ */
1187
+ currentlyBatching() {
1188
+ return this.flushMode === runtime_definitions_1.FlushMode.TurnBased || this._orderSequentiallyCalls !== 0;
1189
+ }
1169
1190
  getQuorum() {
1170
1191
  return this.context.quorum;
1171
1192
  }
@@ -1316,10 +1337,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1316
1337
  * Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
1317
1338
  * After GC has run, called to notify this container's nodes of routes that are used in it.
1318
1339
  * @param usedRoutes - The routes that are used in all nodes in this Container.
1319
- * @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
1320
- * unreferenced as part of this GC run, this should be used to update the time when it happens.
1321
1340
  */
1322
- updateUsedRoutes(usedRoutes, gcTimestamp) {
1341
+ updateUsedRoutes(usedRoutes) {
1323
1342
  // Update our summarizer node's used routes. Updating used routes in summarizer node before
1324
1343
  // summarizing is required and asserted by the the summarizer node. We are the root and are
1325
1344
  // always referenced, so the used routes is only self-route (empty string).
@@ -1330,14 +1349,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1330
1349
  dataStoreUsedRoutes.push(route);
1331
1350
  }
1332
1351
  }
1333
- return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes, gcTimestamp);
1352
+ return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
1334
1353
  }
1335
1354
  /**
1336
- * When running GC in test mode, this is called to delete objects whose routes are unused. This enables testing
1337
- * scenarios with accessing deleted content.
1355
+ * This is called to update objects whose routes are unused. The unused objects are either deleted or marked as
1356
+ * tombstones.
1338
1357
  * @param unusedRoutes - The routes that are unused in all data stores in this Container.
1358
+ * @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
1359
+ * are deleted.
1339
1360
  */
1340
- deleteUnusedRoutes(unusedRoutes) {
1361
+ updateUnusedRoutes(unusedRoutes, tombstone) {
1341
1362
  const blobManagerUnusedRoutes = [];
1342
1363
  const dataStoreUnusedRoutes = [];
1343
1364
  for (const route of unusedRoutes) {
@@ -1348,8 +1369,11 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1348
1369
  dataStoreUnusedRoutes.push(route);
1349
1370
  }
1350
1371
  }
1351
- this.blobManager.deleteUnusedRoutes(blobManagerUnusedRoutes);
1352
- this.dataStores.deleteUnusedRoutes(dataStoreUnusedRoutes);
1372
+ // Todo: Add tombstone for attachment blobs. For now, we ignore attachment blobs that should be tombstoned.
1373
+ if (!tombstone) {
1374
+ this.blobManager.deleteUnusedRoutes(blobManagerUnusedRoutes);
1375
+ }
1376
+ this.dataStores.updateUnusedRoutes(dataStoreUnusedRoutes, tombstone);
1353
1377
  }
1354
1378
  /**
1355
1379
  * Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
@@ -1435,15 +1459,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1435
1459
  const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(telemetry_utils_1.ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
1436
1460
  const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
1437
1461
  latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
1438
- if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
1439
- // We need to catch up to the latest summary's reference sequence number before pausing.
1440
- await telemetry_utils_1.PerformanceEvent.timedExecAsync(summaryNumberLogger, {
1441
- eventName: "WaitingForSeq",
1442
- lastSequenceNumber: this.deltaManager.lastSequenceNumber,
1443
- targetSequenceNumber: latestSnapshotRefSeq,
1444
- lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
1445
- }, async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq), { start: true, end: true, cancel: "error" });
1446
- }
1462
+ // We might need to catch up to the latest summary's reference sequence number before pausing.
1463
+ await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryNumberLogger);
1447
1464
  }
1448
1465
  try {
1449
1466
  await this.deltaManager.inbound.pause();
@@ -1680,6 +1697,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1680
1697
  return this.blobManager.createBlob(blob);
1681
1698
  }
1682
1699
  submit(type, contents, localOpMetadata = undefined, metadata = undefined) {
1700
+ var _a, _b, _c, _d;
1683
1701
  this.verifyNotClosed();
1684
1702
  // There should be no ops in detached container state!
1685
1703
  (0, common_utils_1.assert)(this.attachState !== container_definitions_1.AttachState.Detached, 0x132 /* "sending ops in detached container" */);
@@ -1716,8 +1734,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1716
1734
  // issue than sending.
1717
1735
  // Please note that this does not change file format, so it can be disabled in the future if this
1718
1736
  // optimization no longer makes sense (for example, batch compression may make it less appealing).
1719
- if (type === ContainerMessageType.Attach &&
1720
- this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
1737
+ if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
1738
+ this.mc.config.getBoolean("Fluid.ContainerRuntime.enableAttachOpReorder") === true) {
1721
1739
  if (!this.pendingAttachBatch.push(message)) {
1722
1740
  // BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
1723
1741
  // when queue is not empty.
@@ -1726,9 +1744,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1726
1744
  if (!this.pendingAttachBatch.push(message)) {
1727
1745
  throw new container_utils_1.GenericError("BatchTooLarge",
1728
1746
  /* error */ undefined, {
1729
- opSize: message.contents.length,
1747
+ opSize: (_b = ((_a = message.contents) === null || _a === void 0 ? void 0 : _a.length)) !== null && _b !== void 0 ? _b : 0,
1730
1748
  count: this.pendingAttachBatch.length,
1731
- limit: this.pendingAttachBatch.limit,
1749
+ limit: this.pendingAttachBatch.options.hardLimit,
1732
1750
  });
1733
1751
  }
1734
1752
  }
@@ -1737,23 +1755,23 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1737
1755
  if (!this.pendingBatch.push(message)) {
1738
1756
  throw new container_utils_1.GenericError("BatchTooLarge",
1739
1757
  /* error */ undefined, {
1740
- opSize: message.contents.length,
1758
+ opSize: (_d = ((_c = message.contents) === null || _c === void 0 ? void 0 : _c.length)) !== null && _d !== void 0 ? _d : 0,
1741
1759
  count: this.pendingBatch.length,
1742
- limit: this.pendingBatch.limit,
1760
+ limit: this.pendingBatch.options.hardLimit,
1743
1761
  });
1744
1762
  }
1745
- }
1746
- if (this._flushMode !== runtime_definitions_1.FlushMode.TurnBased) {
1747
- this.flush();
1748
- }
1749
- else if (!this.flushTrigger) {
1750
- this.flushTrigger = true;
1751
- // Queue a microtask to detect the end of the turn and force a flush.
1752
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
1753
- Promise.resolve().then(() => {
1754
- this.flushTrigger = false;
1763
+ if (!this.currentlyBatching()) {
1755
1764
  this.flush();
1756
- });
1765
+ }
1766
+ else if (!this.flushMicroTaskExists) {
1767
+ this.flushMicroTaskExists = true;
1768
+ // Queue a microtask to detect the end of the turn and force a flush.
1769
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
1770
+ Promise.resolve().then(() => {
1771
+ this.flushMicroTaskExists = false;
1772
+ this.flush();
1773
+ });
1774
+ }
1757
1775
  }
1758
1776
  }
1759
1777
  catch (error) {
@@ -1823,18 +1841,41 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1823
1841
  throw new Error(`Can't rollback ${type}`);
1824
1842
  }
1825
1843
  }
1844
+ async waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryLogger) {
1845
+ if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
1846
+ // We need to catch up to the latest summary's reference sequence number before proceeding.
1847
+ await telemetry_utils_1.PerformanceEvent.timedExecAsync(summaryLogger, {
1848
+ eventName: "WaitingForSeq",
1849
+ lastSequenceNumber: this.deltaManager.lastSequenceNumber,
1850
+ targetSequenceNumber: latestSnapshotRefSeq,
1851
+ lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
1852
+ }, async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq), { start: true, end: true, cancel: "error" });
1853
+ }
1854
+ }
1826
1855
  /** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
1827
- async refreshLatestSummaryAck(proposalHandle, ackHandle, summaryRefSeq, summaryLogger) {
1856
+ async refreshLatestSummaryAck(options) {
1857
+ const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
1828
1858
  const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
1829
1859
  // The call to fetch the snapshot is very expensive and not always needed.
1830
1860
  // It should only be done by the summarizerNode, if required.
1861
+ // When fetching from storage we will always get the latest version and do not use the ackHandle.
1831
1862
  const snapshotTreeFetcher = async () => {
1832
- const fetchResult = await this.fetchSnapshotFromStorage(ackHandle, summaryLogger, {
1863
+ const fetchResult = await this.fetchSnapshotFromStorage(null, summaryLogger, {
1833
1864
  eventName: "RefreshLatestSummaryGetSnapshot",
1834
1865
  ackHandle,
1835
1866
  summaryRefSeq,
1836
- fetchLatest: false,
1867
+ fetchLatest: true,
1868
+ });
1869
+ const latestSnapshotRefSeq = await (0, runtime_utils_1.seqFromTree)(fetchResult.snapshotTree, readAndParseBlob);
1870
+ summaryLogger.sendTelemetryEvent({
1871
+ eventName: "LatestSummaryRetrieved",
1872
+ ackHandle,
1873
+ lastSequenceNumber: latestSnapshotRefSeq,
1874
+ targetSequenceNumber: summaryRefSeq,
1837
1875
  });
1876
+ // In case we had to retrieve the latest snapshot and it is different than summaryRefSeq,
1877
+ // wait for the delta manager to catch up before refreshing the latest Summary.
1878
+ await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryLogger);
1838
1879
  return fetchResult.snapshotTree;
1839
1880
  };
1840
1881
  const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, snapshotTreeFetcher, readAndParseBlob, summaryLogger);
@@ -1879,7 +1920,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1879
1920
  this.baseSnapshotBlobs = serializedSnapshotStorage_1.SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
1880
1921
  }
1881
1922
  }
1882
- async getSnapshotBlobs() {
1923
+ async initializeBaseSnapshotBlobs() {
1883
1924
  var _a;
1884
1925
  if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) ||
1885
1926
  this.attachState !== container_definitions_1.AttachState.Attached || this.context.pendingLocalState) {
@@ -1897,6 +1938,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1897
1938
  // getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
1898
1939
  // to close current batch.
1899
1940
  this.flush();
1941
+ if (this._orderSequentiallyCalls !== 0) {
1942
+ throw new container_utils_1.UsageError("can't get state during orderSequentially");
1943
+ }
1900
1944
  const previousPendingState = this.context.pendingLocalState;
1901
1945
  if (previousPendingState) {
1902
1946
  return {
@@ -1964,6 +2008,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
1964
2008
  throw new container_utils_1.UsageError(`Summary heuristic configuration property "${prop}" cannot be less than 0`);
1965
2009
  }
1966
2010
  }
2011
+ if (configuration.minIdleTime > configuration.maxIdleTime) {
2012
+ throw new container_utils_1.UsageError(`"minIdleTime" [${configuration.minIdleTime}] cannot be greater than "maxIdleTime" [${configuration.maxIdleTime}]`);
2013
+ }
1967
2014
  }
1968
2015
  }
1969
2016
  exports.ContainerRuntime = ContainerRuntime;
@@ -1974,12 +2021,18 @@ exports.ContainerRuntime = ContainerRuntime;
1974
2021
  const waitForSeq = async (deltaManager, targetSeq) => new Promise((resolve, reject) => {
1975
2022
  // TODO: remove cast to any when actual event is determined
1976
2023
  deltaManager.on("closed", reject);
1977
- const handleOp = (message) => {
1978
- if (message.sequenceNumber >= targetSeq) {
1979
- resolve();
1980
- deltaManager.off("op", handleOp);
1981
- }
1982
- };
1983
- deltaManager.on("op", handleOp);
2024
+ // If we already reached target sequence number, simply resolve the promise.
2025
+ if (deltaManager.lastSequenceNumber >= targetSeq) {
2026
+ resolve();
2027
+ }
2028
+ else {
2029
+ const handleOp = (message) => {
2030
+ if (message.sequenceNumber >= targetSeq) {
2031
+ resolve();
2032
+ deltaManager.off("op", handleOp);
2033
+ }
2034
+ };
2035
+ deltaManager.on("op", handleOp);
2036
+ }
1984
2037
  });
1985
2038
  //# sourceMappingURL=containerRuntime.js.map