@fluidframework/container-runtime 2.0.0-dev-rc.3.0.0.254866 → 2.0.0-dev-rc.4.0.0.261659

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 (293) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/api-report/container-runtime.api.md +33 -19
  3. package/dist/batchTracker.d.ts +1 -1
  4. package/dist/batchTracker.d.ts.map +1 -1
  5. package/dist/batchTracker.js.map +1 -1
  6. package/dist/channelCollection.d.ts +5 -3
  7. package/dist/channelCollection.d.ts.map +1 -1
  8. package/dist/channelCollection.js +67 -15
  9. package/dist/channelCollection.js.map +1 -1
  10. package/dist/connectionTelemetry.d.ts +1 -1
  11. package/dist/connectionTelemetry.d.ts.map +1 -1
  12. package/dist/connectionTelemetry.js +54 -5
  13. package/dist/connectionTelemetry.js.map +1 -1
  14. package/dist/containerRuntime.d.ts +17 -27
  15. package/dist/containerRuntime.d.ts.map +1 -1
  16. package/dist/containerRuntime.js +174 -143
  17. package/dist/containerRuntime.js.map +1 -1
  18. package/dist/dataStore.d.ts +1 -1
  19. package/dist/dataStore.d.ts.map +1 -1
  20. package/dist/dataStore.js.map +1 -1
  21. package/dist/dataStoreContext.d.ts.map +1 -1
  22. package/dist/dataStoreContext.js +1 -0
  23. package/dist/dataStoreContext.js.map +1 -1
  24. package/dist/dataStoreContexts.d.ts +2 -0
  25. package/dist/dataStoreContexts.d.ts.map +1 -1
  26. package/dist/dataStoreContexts.js +7 -0
  27. package/dist/dataStoreContexts.js.map +1 -1
  28. package/dist/deltaManagerSummarizerProxy.d.ts +18 -6
  29. package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -1
  30. package/dist/deltaManagerSummarizerProxy.js +38 -19
  31. package/dist/deltaManagerSummarizerProxy.js.map +1 -1
  32. package/dist/deltaScheduler.d.ts +1 -1
  33. package/dist/deltaScheduler.d.ts.map +1 -1
  34. package/dist/deltaScheduler.js.map +1 -1
  35. package/dist/gc/garbageCollection.d.ts +5 -12
  36. package/dist/gc/garbageCollection.d.ts.map +1 -1
  37. package/dist/gc/garbageCollection.js +45 -29
  38. package/dist/gc/garbageCollection.js.map +1 -1
  39. package/dist/gc/gcDefinitions.d.ts +27 -6
  40. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  41. package/dist/gc/gcDefinitions.js.map +1 -1
  42. package/dist/gc/gcHelpers.d.ts +5 -4
  43. package/dist/gc/gcHelpers.d.ts.map +1 -1
  44. package/dist/gc/gcHelpers.js +14 -2
  45. package/dist/gc/gcHelpers.js.map +1 -1
  46. package/dist/gc/gcTelemetry.d.ts +14 -4
  47. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  48. package/dist/gc/gcTelemetry.js +24 -21
  49. package/dist/gc/gcTelemetry.js.map +1 -1
  50. package/dist/gc/index.d.ts +2 -2
  51. package/dist/gc/index.d.ts.map +1 -1
  52. package/dist/gc/index.js +2 -2
  53. package/dist/gc/index.js.map +1 -1
  54. package/dist/index.d.ts +2 -2
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +2 -1
  57. package/dist/index.js.map +1 -1
  58. package/dist/{alpha.d.ts → legacy.d.ts} +3 -1
  59. package/dist/metadata.d.ts +2 -2
  60. package/dist/metadata.d.ts.map +1 -1
  61. package/dist/metadata.js.map +1 -1
  62. package/dist/opLifecycle/batchManager.d.ts +0 -1
  63. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  64. package/dist/opLifecycle/batchManager.js +0 -10
  65. package/dist/opLifecycle/batchManager.js.map +1 -1
  66. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  67. package/dist/opLifecycle/opDecompressor.js +6 -6
  68. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  69. package/dist/opLifecycle/opGroupingManager.js +2 -2
  70. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  71. package/dist/opLifecycle/opSplitter.js +1 -1
  72. package/dist/opLifecycle/opSplitter.js.map +1 -1
  73. package/dist/opLifecycle/outbox.d.ts +0 -4
  74. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  75. package/dist/opLifecycle/outbox.js +2 -34
  76. package/dist/opLifecycle/outbox.js.map +1 -1
  77. package/dist/packageVersion.d.ts +1 -1
  78. package/dist/packageVersion.js +1 -1
  79. package/dist/packageVersion.js.map +1 -1
  80. package/dist/pendingStateManager.d.ts +3 -2
  81. package/dist/pendingStateManager.d.ts.map +1 -1
  82. package/dist/pendingStateManager.js +17 -10
  83. package/dist/pendingStateManager.js.map +1 -1
  84. package/dist/public.d.ts +3 -0
  85. package/dist/scheduleManager.d.ts +1 -1
  86. package/dist/scheduleManager.d.ts.map +1 -1
  87. package/dist/scheduleManager.js.map +1 -1
  88. package/dist/summary/documentSchema.d.ts +3 -1
  89. package/dist/summary/documentSchema.d.ts.map +1 -1
  90. package/dist/summary/documentSchema.js +34 -16
  91. package/dist/summary/documentSchema.js.map +1 -1
  92. package/dist/summary/orderedClientElection.d.ts +1 -1
  93. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  94. package/dist/summary/orderedClientElection.js.map +1 -1
  95. package/dist/summary/summarizer.d.ts +1 -2
  96. package/dist/summary/summarizer.d.ts.map +1 -1
  97. package/dist/summary/summarizer.js.map +1 -1
  98. package/dist/summary/summarizerClientElection.d.ts +1 -1
  99. package/dist/summary/summarizerClientElection.d.ts.map +1 -1
  100. package/dist/summary/summarizerClientElection.js.map +1 -1
  101. package/dist/summary/summarizerHeuristics.d.ts +1 -1
  102. package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
  103. package/dist/summary/summarizerHeuristics.js.map +1 -1
  104. package/dist/summary/summarizerNode/summarizerNode.d.ts +4 -3
  105. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  106. package/dist/summary/summarizerNode/summarizerNode.js +4 -10
  107. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  108. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
  109. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  110. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  111. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -2
  112. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  113. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
  114. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  115. package/dist/summary/summarizerTypes.d.ts +2 -3
  116. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  117. package/dist/summary/summarizerTypes.js.map +1 -1
  118. package/dist/summary/summaryCollection.d.ts +1 -1
  119. package/dist/summary/summaryCollection.d.ts.map +1 -1
  120. package/dist/summary/summaryCollection.js.map +1 -1
  121. package/dist/summary/summaryGenerator.d.ts +1 -2
  122. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  123. package/dist/summary/summaryGenerator.js +3 -2
  124. package/dist/summary/summaryGenerator.js.map +1 -1
  125. package/dist/summary/summaryManager.d.ts.map +1 -1
  126. package/dist/summary/summaryManager.js.map +1 -1
  127. package/{dist/beta.d.ts → internal.d.ts} +2 -0
  128. package/{lib/beta.d.ts → legacy.d.ts} +2 -0
  129. package/lib/batchTracker.d.ts +1 -1
  130. package/lib/batchTracker.d.ts.map +1 -1
  131. package/lib/batchTracker.js.map +1 -1
  132. package/lib/channelCollection.d.ts +5 -3
  133. package/lib/channelCollection.d.ts.map +1 -1
  134. package/lib/channelCollection.js +69 -17
  135. package/lib/channelCollection.js.map +1 -1
  136. package/lib/connectionTelemetry.d.ts +1 -1
  137. package/lib/connectionTelemetry.d.ts.map +1 -1
  138. package/lib/connectionTelemetry.js +49 -0
  139. package/lib/connectionTelemetry.js.map +1 -1
  140. package/lib/containerRuntime.d.ts +17 -27
  141. package/lib/containerRuntime.d.ts.map +1 -1
  142. package/lib/containerRuntime.js +174 -143
  143. package/lib/containerRuntime.js.map +1 -1
  144. package/lib/dataStore.d.ts +1 -1
  145. package/lib/dataStore.d.ts.map +1 -1
  146. package/lib/dataStore.js +1 -1
  147. package/lib/dataStore.js.map +1 -1
  148. package/lib/dataStoreContext.d.ts.map +1 -1
  149. package/lib/dataStoreContext.js +1 -0
  150. package/lib/dataStoreContext.js.map +1 -1
  151. package/lib/dataStoreContexts.d.ts +2 -0
  152. package/lib/dataStoreContexts.d.ts.map +1 -1
  153. package/lib/dataStoreContexts.js +7 -0
  154. package/lib/dataStoreContexts.js.map +1 -1
  155. package/lib/deltaManagerSummarizerProxy.d.ts +18 -6
  156. package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -1
  157. package/lib/deltaManagerSummarizerProxy.js +36 -18
  158. package/lib/deltaManagerSummarizerProxy.js.map +1 -1
  159. package/lib/deltaScheduler.d.ts +1 -1
  160. package/lib/deltaScheduler.d.ts.map +1 -1
  161. package/lib/deltaScheduler.js.map +1 -1
  162. package/lib/gc/garbageCollection.d.ts +5 -12
  163. package/lib/gc/garbageCollection.d.ts.map +1 -1
  164. package/lib/gc/garbageCollection.js +47 -31
  165. package/lib/gc/garbageCollection.js.map +1 -1
  166. package/lib/gc/gcDefinitions.d.ts +27 -6
  167. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  168. package/lib/gc/gcDefinitions.js.map +1 -1
  169. package/lib/gc/gcHelpers.d.ts +5 -4
  170. package/lib/gc/gcHelpers.d.ts.map +1 -1
  171. package/lib/gc/gcHelpers.js +12 -1
  172. package/lib/gc/gcHelpers.js.map +1 -1
  173. package/lib/gc/gcTelemetry.d.ts +14 -4
  174. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  175. package/lib/gc/gcTelemetry.js +24 -21
  176. package/lib/gc/gcTelemetry.js.map +1 -1
  177. package/lib/gc/index.d.ts +2 -2
  178. package/lib/gc/index.d.ts.map +1 -1
  179. package/lib/gc/index.js +1 -1
  180. package/lib/gc/index.js.map +1 -1
  181. package/lib/index.d.ts +2 -2
  182. package/lib/index.d.ts.map +1 -1
  183. package/lib/index.js +1 -1
  184. package/lib/index.js.map +1 -1
  185. package/lib/{alpha.d.ts → legacy.d.ts} +3 -1
  186. package/lib/metadata.d.ts +2 -2
  187. package/lib/metadata.d.ts.map +1 -1
  188. package/lib/metadata.js.map +1 -1
  189. package/lib/opLifecycle/batchManager.d.ts +0 -1
  190. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  191. package/lib/opLifecycle/batchManager.js +0 -10
  192. package/lib/opLifecycle/batchManager.js.map +1 -1
  193. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  194. package/lib/opLifecycle/opDecompressor.js +6 -6
  195. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  196. package/lib/opLifecycle/opGroupingManager.js +2 -2
  197. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  198. package/lib/opLifecycle/opSplitter.js +1 -1
  199. package/lib/opLifecycle/opSplitter.js.map +1 -1
  200. package/lib/opLifecycle/outbox.d.ts +0 -4
  201. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  202. package/lib/opLifecycle/outbox.js +2 -34
  203. package/lib/opLifecycle/outbox.js.map +1 -1
  204. package/lib/packageVersion.d.ts +1 -1
  205. package/lib/packageVersion.js +1 -1
  206. package/lib/packageVersion.js.map +1 -1
  207. package/lib/pendingStateManager.d.ts +3 -2
  208. package/lib/pendingStateManager.d.ts.map +1 -1
  209. package/lib/pendingStateManager.js +18 -11
  210. package/lib/pendingStateManager.js.map +1 -1
  211. package/lib/public.d.ts +3 -0
  212. package/lib/scheduleManager.d.ts +1 -1
  213. package/lib/scheduleManager.d.ts.map +1 -1
  214. package/lib/scheduleManager.js.map +1 -1
  215. package/lib/summary/documentSchema.d.ts +3 -1
  216. package/lib/summary/documentSchema.d.ts.map +1 -1
  217. package/lib/summary/documentSchema.js +34 -16
  218. package/lib/summary/documentSchema.js.map +1 -1
  219. package/lib/summary/orderedClientElection.d.ts +1 -1
  220. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  221. package/lib/summary/orderedClientElection.js +1 -1
  222. package/lib/summary/orderedClientElection.js.map +1 -1
  223. package/lib/summary/summarizer.d.ts +1 -2
  224. package/lib/summary/summarizer.d.ts.map +1 -1
  225. package/lib/summary/summarizer.js.map +1 -1
  226. package/lib/summary/summarizerClientElection.d.ts +1 -1
  227. package/lib/summary/summarizerClientElection.d.ts.map +1 -1
  228. package/lib/summary/summarizerClientElection.js.map +1 -1
  229. package/lib/summary/summarizerHeuristics.d.ts +1 -1
  230. package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
  231. package/lib/summary/summarizerHeuristics.js.map +1 -1
  232. package/lib/summary/summarizerNode/summarizerNode.d.ts +4 -3
  233. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  234. package/lib/summary/summarizerNode/summarizerNode.js +4 -10
  235. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  236. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
  237. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  238. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  239. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -2
  240. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  241. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
  242. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  243. package/lib/summary/summarizerTypes.d.ts +2 -3
  244. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  245. package/lib/summary/summarizerTypes.js.map +1 -1
  246. package/lib/summary/summaryCollection.d.ts +1 -1
  247. package/lib/summary/summaryCollection.d.ts.map +1 -1
  248. package/lib/summary/summaryCollection.js.map +1 -1
  249. package/lib/summary/summaryGenerator.d.ts +1 -2
  250. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  251. package/lib/summary/summaryGenerator.js +4 -3
  252. package/lib/summary/summaryGenerator.js.map +1 -1
  253. package/lib/summary/summaryManager.d.ts.map +1 -1
  254. package/lib/summary/summaryManager.js +1 -1
  255. package/lib/summary/summaryManager.js.map +1 -1
  256. package/package.json +33 -57
  257. package/src/batchTracker.ts +1 -2
  258. package/src/channelCollection.ts +87 -35
  259. package/src/connectionTelemetry.ts +58 -3
  260. package/src/containerRuntime.ts +214 -222
  261. package/src/dataStore.ts +5 -2
  262. package/src/dataStoreContext.ts +1 -0
  263. package/src/dataStoreContexts.ts +13 -2
  264. package/src/deltaManagerSummarizerProxy.ts +43 -21
  265. package/src/deltaScheduler.ts +1 -2
  266. package/src/gc/garbageCollection.ts +64 -42
  267. package/src/gc/gcDefinitions.ts +22 -10
  268. package/src/gc/gcHelpers.ts +14 -1
  269. package/src/gc/gcTelemetry.ts +57 -50
  270. package/src/gc/index.ts +2 -1
  271. package/src/index.ts +2 -0
  272. package/src/metadata.ts +2 -2
  273. package/src/opLifecycle/README.md +4 -4
  274. package/src/opLifecycle/batchManager.ts +0 -14
  275. package/src/opLifecycle/opDecompressor.ts +12 -6
  276. package/src/opLifecycle/opGroupingManager.ts +2 -2
  277. package/src/opLifecycle/opSplitter.ts +1 -1
  278. package/src/opLifecycle/outbox.ts +2 -49
  279. package/src/packageVersion.ts +1 -1
  280. package/src/pendingStateManager.ts +28 -15
  281. package/src/scheduleManager.ts +1 -1
  282. package/src/summary/documentSchema.ts +52 -18
  283. package/src/summary/orderedClientElection.ts +5 -2
  284. package/src/summary/summarizer.ts +1 -1
  285. package/src/summary/summarizerClientElection.ts +1 -1
  286. package/src/summary/summarizerHeuristics.ts +1 -1
  287. package/src/summary/summarizerNode/summarizerNode.ts +3 -12
  288. package/src/summary/summarizerNode/summarizerNodeUtils.ts +2 -3
  289. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +1 -10
  290. package/src/summary/summarizerTypes.ts +5 -3
  291. package/src/summary/summaryCollection.ts +1 -1
  292. package/src/summary/summaryGenerator.ts +19 -8
  293. package/src/summary/summaryManager.ts +5 -2
@@ -25,7 +25,7 @@ import { FluidDataStoreRegistry } from "./dataStoreRegistry.js";
25
25
  import { DeltaManagerSummarizerProxy } from "./deltaManagerSummarizerProxy.js";
26
26
  import { GCNodeType, GarbageCollector, gcGenerationOptionName, } from "./gc/index.js";
27
27
  import { ContainerMessageType, } from "./messageTypes.js";
28
- import { OpCompressor, OpDecompressor, OpGroupingManager, OpSplitter, Outbox, RemoteMessageProcessor, getLongStack, } from "./opLifecycle/index.js";
28
+ import { OpCompressor, OpDecompressor, OpGroupingManager, OpSplitter, Outbox, RemoteMessageProcessor, } from "./opLifecycle/index.js";
29
29
  import { pkgVersion } from "./packageVersion.js";
30
30
  import { PendingStateManager, } from "./pendingStateManager.js";
31
31
  import { ScheduleManager } from "./scheduleManager.js";
@@ -60,6 +60,11 @@ export const DefaultSummaryConfiguration = {
60
60
  runtimeOpWeight: 1.0,
61
61
  nonRuntimeHeuristicThreshold: 20,
62
62
  };
63
+ /**
64
+ * Error responses when requesting a deleted object will have this header set to true
65
+ * @alpha
66
+ */
67
+ export const DeletedResponseHeaderKey = "wasDeleted";
63
68
  /**
64
69
  * Tombstone error responses will have this header set to true
65
70
  * @alpha
@@ -241,7 +246,7 @@ export class ContainerRuntime extends TypedEventEmitter {
241
246
  },
242
247
  });
243
248
  const mc = loggerToMonitoringContext(logger);
244
- const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor, chunkSizeInBytes = defaultChunkSizeInBytes, enableOpReentryCheck = false, enableGroupedBatching = false, explicitSchemaControl = false, } = runtimeOptions;
249
+ const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor, chunkSizeInBytes = defaultChunkSizeInBytes, enableGroupedBatching = false, explicitSchemaControl = false, } = runtimeOptions;
245
250
  const registry = new FluidDataStoreRegistry(registryEntries);
246
251
  const tryFetchBlob = async (blobName) => {
247
252
  const blobId = context.baseSnapshot?.blobs[blobName];
@@ -269,9 +274,9 @@ export class ContainerRuntime extends TypedEventEmitter {
269
274
  const messageAtLastSummary = lastMessageFromMetadata(metadata);
270
275
  // Verify summary runtime sequence number matches protocol sequence number.
271
276
  const runtimeSequenceNumber = messageAtLastSummary?.sequenceNumber;
277
+ const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
272
278
  // When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
273
279
  if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
274
- const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
275
280
  // Unless bypass is explicitly set, then take action when sequence numbers mismatch.
276
281
  if (loadSequenceNumberVerification !== "bypass" &&
277
282
  runtimeSequenceNumber !== protocolSequenceNumber) {
@@ -323,7 +328,14 @@ export class ContainerRuntime extends TypedEventEmitter {
323
328
  ?.idCompressorMode;
324
329
  // This is the only exception to the rule above - we have proper plumbing to load ID compressor on schema change
325
330
  // event. It is loaded async (relative to op processing), so this conversion is only safe for off -> delayed conversion!
326
- if (idCompressorMode === undefined && desiredIdCompressorMode === "delayed") {
331
+ // Clients do not expect ID compressor ops unless ID compressor is On for them, and that could be achieved only through
332
+ // explicit schema change, i.e. only if explicitSchemaControl is on.
333
+ // Note: it would be better if we throw on combination of options (explicitSchemaControl = off, desiredIdCompressorMode === "delayed")
334
+ // that is not supported. But our service tests are oblivious to these problems and throwing here will cause a ton of failures
335
+ // We ignored incompatible ID compressor changes from the start (they were sticky), so that's not a new problem being introduced...
336
+ if (idCompressorMode === undefined &&
337
+ desiredIdCompressorMode === "delayed" &&
338
+ explicitSchemaControl) {
327
339
  idCompressorMode = desiredIdCompressorMode;
328
340
  }
329
341
  }
@@ -362,7 +374,7 @@ export class ContainerRuntime extends TypedEventEmitter {
362
374
  compressionOptions.minimumBatchSizeInBytes !== Infinity &&
363
375
  compressionOptions.compressionAlgorithm === "lz4";
364
376
  const opGroupingEnabled = disableGroupedBatching !== true && enableGroupedBatching;
365
- const documentSchemaController = new DocumentsSchemaController(existing, metadata?.documentSchema, {
377
+ const documentSchemaController = new DocumentsSchemaController(existing, protocolSequenceNumber, metadata?.documentSchema, {
366
378
  explicitSchemaControl,
367
379
  compressionLz4,
368
380
  idCompressorMode,
@@ -385,7 +397,6 @@ export class ContainerRuntime extends TypedEventEmitter {
385
397
  chunkSizeInBytes,
386
398
  // Requires<> drops undefined from IdCompressorType
387
399
  enableRuntimeIdCompressor: enableRuntimeIdCompressor,
388
- enableOpReentryCheck,
389
400
  enableGroupedBatching,
390
401
  explicitSchemaControl,
391
402
  }, containerScope, logger, existing, blobManagerSnapshot, context.storage, createIdCompressorFn, documentSchemaController, featureGatesForTelemetry, provideEntryPoint, requestHandler, undefined);
@@ -424,11 +435,21 @@ export class ContainerRuntime extends TypedEventEmitter {
424
435
  get attachState() {
425
436
  return this._getAttachState();
426
437
  }
427
- get documentSchema() {
438
+ /**
439
+ * Current session schema - defines what options are on & off.
440
+ * It's overlap of document schema (controlled by summary & ops) and options controlling this session.
441
+ * For example, document schema might have compression ON, but feature gates / runtime options turn it Off.
442
+ * In such case it will be off in session schema (i.e. this session should not use compression), but this client
443
+ * has to deal with compressed ops as other clients might send them.
444
+ * And in reverse, session schema can have compression Off, but feature gates / runtime options want it On.
445
+ * In such case it will be off in session schema, however this client will propose change to schema, and once / if
446
+ * this op rountrips, compression will be On. Client can't send compressed ops until it's change in schema.
447
+ */
448
+ get sessionSchema() {
428
449
  return this.documentsSchemaController.sessionSchema.runtime;
429
450
  }
430
451
  get idCompressorMode() {
431
- return this.documentSchema.idCompressorMode;
452
+ return this.sessionSchema.idCompressorMode;
432
453
  }
433
454
  /**
434
455
  * See IContainerRuntimeBase.idCompressor() for details.
@@ -536,13 +557,6 @@ export class ContainerRuntime extends TypedEventEmitter {
536
557
  this.flushTaskExists = false;
537
558
  this.consecutiveReconnects = 0;
538
559
  this.ensureNoDataModelChangesCalls = 0;
539
- /**
540
- * Tracks the number of detected reentrant ops to report,
541
- * in order to self-throttle the telemetry events.
542
- *
543
- * This should be removed as part of ADO:2322
544
- */
545
- this.opReentryCallsToReport = 5;
546
560
  this._disposed = false;
547
561
  this.emitDirtyDocumentEvent = true;
548
562
  this.defaultTelemetrySignalSampleCount = 100;
@@ -569,7 +583,7 @@ export class ContainerRuntime extends TypedEventEmitter {
569
583
  // If it's not in the list, then we will need to either use no compression, or fallback to some other (supported by format)
570
584
  // compression.
571
585
  const compressionOptions = {
572
- minimumBatchSizeInBytes: this.documentSchema.compressionLz4
586
+ minimumBatchSizeInBytes: this.sessionSchema.compressionLz4
573
587
  ? runtimeOptions.compressionOptions.minimumBatchSizeInBytes
574
588
  : Number.POSITIVE_INFINITY,
575
589
  compressionAlgorithm: CompressionAlgorithms.lz4,
@@ -653,11 +667,6 @@ export class ContainerRuntime extends TypedEventEmitter {
653
667
  if (this.summaryConfiguration.state === "enabled") {
654
668
  this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
655
669
  }
656
- const disableOpReentryCheck = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck");
657
- this.enableOpReentryCheck =
658
- runtimeOptions.enableOpReentryCheck === true &&
659
- // Allow for a break-glass config to override the options
660
- disableOpReentryCheck !== true;
661
670
  this.summariesDisabled = this.isSummariesDisabled();
662
671
  this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
663
672
  this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
@@ -729,7 +738,7 @@ export class ContainerRuntime extends TypedEventEmitter {
729
738
  const envelope2 = this.createNewSignalEnvelope(envelope1.address, type, envelope1.contents);
730
739
  return this.submitSignalFn(envelope2, targetClientId);
731
740
  };
732
- this.channelCollection = new ChannelCollection(getSummaryForDatastores(baseSnapshot, metadata), parentContext, this.mc.logger, (path, reason, timestampMs, packagePath, request, headerData) => this.garbageCollector.nodeUpdated(path, reason, timestampMs, packagePath, request, headerData), (path) => this.garbageCollector.isNodeDeleted(path), new Map(dataStoreAliasMap), async (runtime) => provideEntryPoint);
741
+ this.channelCollection = new ChannelCollection(getSummaryForDatastores(baseSnapshot, metadata), parentContext, this.mc.logger, (props) => this.garbageCollector.nodeUpdated(props), (path) => this.garbageCollector.isNodeDeleted(path), new Map(dataStoreAliasMap), async (runtime) => provideEntryPoint);
733
742
  this.blobManager = new BlobManager({
734
743
  routeContext: this.handleContext,
735
744
  snapshot: blobManagerSnapshot,
@@ -742,7 +751,10 @@ export class ContainerRuntime extends TypedEventEmitter {
742
751
  });
743
752
  }
744
753
  },
745
- blobRequested: (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"),
754
+ blobRequested: (blobPath) => this.garbageCollector.nodeUpdated({
755
+ node: { type: "Blob", path: blobPath },
756
+ reason: "Loaded",
757
+ }),
746
758
  isBlobDeleted: (blobPath) => this.garbageCollector.isNodeDeleted(blobPath),
747
759
  runtime: this,
748
760
  stashedBlobs: pendingRuntimeState?.pendingAttachmentBlobs,
@@ -790,12 +802,29 @@ export class ContainerRuntime extends TypedEventEmitter {
790
802
  this._quorum.on("removeMember", (clientId) => {
791
803
  this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
792
804
  });
793
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
794
805
  this._audience = audience;
806
+ if (audience.getSelf === undefined) {
807
+ // back-compat, added in 2.0 RC3.
808
+ // Purpose: deal with cases when we run against old loader that does not have newly added capabilities
809
+ audience.getSelf = () => {
810
+ const clientId = this._getClientId();
811
+ return clientId === undefined
812
+ ? undefined
813
+ : ({
814
+ clientId,
815
+ client: audience.getMember(clientId),
816
+ });
817
+ };
818
+ let oldClientId = this.clientId;
819
+ this.on("connected", () => {
820
+ const clientId = this.clientId;
821
+ assert(clientId !== undefined, "can't be undefined");
822
+ audience.emit("selfChanged", { clientId: oldClientId }, { clientId, client: audience.getMember(clientId) });
823
+ oldClientId = clientId;
824
+ });
825
+ }
795
826
  const closeSummarizerDelayOverride = this.mc.config.getNumber("Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs");
796
827
  this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
797
- this.validateSummaryBeforeUpload =
798
- this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
799
828
  this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
800
829
  this.dirtyContainer =
801
830
  this.attachState !== AttachState.Attached || this.hasPendingMessages();
@@ -868,9 +897,9 @@ export class ContainerRuntime extends TypedEventEmitter {
868
897
  options: JSON.stringify(runtimeOptions),
869
898
  idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
870
899
  idCompressorMode: this.idCompressorMode,
900
+ sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
871
901
  featureGates: JSON.stringify({
872
902
  ...featureGatesForTelemetry,
873
- disableOpReentryCheck,
874
903
  disableChunking,
875
904
  disableAttachReorder: this.disableAttachReorder,
876
905
  disablePartialFlush,
@@ -878,6 +907,7 @@ export class ContainerRuntime extends TypedEventEmitter {
878
907
  }),
879
908
  telemetryDocumentId: this.telemetryDocumentId,
880
909
  groupedBatchingEnabled: this.groupedBatchingEnabled,
910
+ initialSequenceNumber: this.deltaManager.initialSequenceNumber,
881
911
  });
882
912
  ReportOpPerfTelemetry(this.clientId, this.deltaManager, this, this.logger);
883
913
  BindBatchTracker(this, this.logger);
@@ -893,6 +923,10 @@ export class ContainerRuntime extends TypedEventEmitter {
893
923
  this.skipSavedCompressorOps = pendingRuntimeState?.pendingIdCompressorState !== undefined;
894
924
  }
895
925
  onSchemaChange(schema) {
926
+ this.logger.sendTelemetryEvent({
927
+ eventName: "SchemaChangeAccept",
928
+ sessionRuntimeSchema: JSON.stringify(schema),
929
+ });
896
930
  // Most of the settings will be picked up only by new sessions (i.e. after reload).
897
931
  // We can make it better in the future (i.e. start to use op compression right away), but for simplicity
898
932
  // this is not done.
@@ -925,9 +959,9 @@ export class ContainerRuntime extends TypedEventEmitter {
925
959
  async initializeBaseState() {
926
960
  if (this.idCompressorMode === "on" ||
927
961
  (this.idCompressorMode === "delayed" && this.connected)) {
962
+ this._idCompressor = await this.createIdCompressor();
928
963
  // This is called from loadRuntime(), long before we process any ops, so there should be no ops accumulated yet.
929
964
  assert(this.pendingIdCompressorOps.length === 0, 0x8ec /* no pending ops */);
930
- this._idCompressor = await this.createIdCompressor();
931
965
  }
932
966
  await this.garbageCollector.initializeBaseState();
933
967
  }
@@ -1305,12 +1339,14 @@ export class ContainerRuntime extends TypedEventEmitter {
1305
1339
  this._loadIdCompressor === undefined) {
1306
1340
  this._loadIdCompressor = this.createIdCompressor()
1307
1341
  .then((compressor) => {
1308
- this._idCompressor = compressor;
1309
1342
  // Finalize any ranges we received while the compressor was turned off.
1310
- for (const range of this.pendingIdCompressorOps) {
1311
- this._idCompressor.finalizeCreationRange(range);
1312
- }
1343
+ const ops = this.pendingIdCompressorOps;
1313
1344
  this.pendingIdCompressorOps = [];
1345
+ for (const range of ops) {
1346
+ compressor.finalizeCreationRange(range);
1347
+ }
1348
+ assert(this.pendingIdCompressorOps.length === 0, "No new ops added");
1349
+ this._idCompressor = compressor;
1314
1350
  })
1315
1351
  .catch((error) => {
1316
1352
  this.logger.sendErrorEvent({ eventName: "IdCompressorDelayedLoad" }, error);
@@ -1320,6 +1356,10 @@ export class ContainerRuntime extends TypedEventEmitter {
1320
1356
  return this._loadIdCompressor;
1321
1357
  }
1322
1358
  setConnectionState(connected, clientId) {
1359
+ // Validate we have consistent state
1360
+ const currentClientId = this._audience.getSelf()?.clientId;
1361
+ assert(clientId === currentClientId, "input clientId does not match Audience");
1362
+ assert(this.clientId === currentClientId, "this.clientId does not match Audience");
1323
1363
  if (connected && this.idCompressorMode === "delayed") {
1324
1364
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
1325
1365
  this.loadIdCompressor();
@@ -1401,9 +1441,10 @@ export class ContainerRuntime extends TypedEventEmitter {
1401
1441
  // We do not need to make a deep copy. Each layer will just replace message.contents itself,
1402
1442
  // but will not modify the contents object (likely it will replace it on the message).
1403
1443
  const messageCopy = { ...messageArg };
1444
+ const savedOp = messageCopy.metadata?.savedOp;
1404
1445
  for (const message of this.remoteMessageProcessor.process(messageCopy)) {
1405
- if (modernRuntimeMessage) {
1406
- this.processCore({
1446
+ const msg = modernRuntimeMessage
1447
+ ? {
1407
1448
  // Cast it since we expect it to be this based on modernRuntimeMessage computation above.
1408
1449
  // There is nothing really ensuring that anytime original message.type is Operation that
1409
1450
  // the result messages will be so. In the end modern bool being true only directs to
@@ -1411,12 +1452,16 @@ export class ContainerRuntime extends TypedEventEmitter {
1411
1452
  message: message,
1412
1453
  local,
1413
1454
  modernRuntimeMessage,
1414
- });
1415
- }
1416
- else {
1417
- // Unrecognized message will be ignored.
1418
- this.processCore({ message, local, modernRuntimeMessage });
1419
- }
1455
+ }
1456
+ : // Unrecognized message will be ignored.
1457
+ {
1458
+ message,
1459
+ local,
1460
+ modernRuntimeMessage,
1461
+ };
1462
+ msg.savedOp = savedOp;
1463
+ // ensure that we observe any re-entrancy, and if needed, rebase ops
1464
+ this.ensureNoDataModelChanges(() => this.processCore(msg));
1420
1465
  }
1421
1466
  }
1422
1467
  /**
@@ -1434,7 +1479,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1434
1479
  // These calls should be made for all but chunked ops:
1435
1480
  // 1) this.pendingStateManager.processPendingLocalMessage() below
1436
1481
  // 2) this.resetReconnectCount() below
1437
- assert(message.type !== ContainerMessageType.ChunkedOp, "we should never get here with chunked ops");
1482
+ assert(message.type !== ContainerMessageType.ChunkedOp, 0x93b /* we should never get here with chunked ops */);
1438
1483
  let localOpMetadata;
1439
1484
  if (local && messageWithContext.modernRuntimeMessage) {
1440
1485
  localOpMetadata = this.pendingStateManager.processPendingLocalMessage(messageWithContext.message);
@@ -1481,17 +1526,16 @@ export class ContainerRuntime extends TypedEventEmitter {
1481
1526
  // stashed ops flow. The compressor is stashed with these ops already processed.
1482
1527
  // That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
1483
1528
  // thus we need to process all the ops.
1484
- if (!(this.skipSavedCompressorOps &&
1485
- messageWithContext.message.metadata?.savedOp ===
1486
- true)) {
1529
+ if (!(this.skipSavedCompressorOps && messageWithContext.savedOp === true)) {
1487
1530
  const range = messageWithContext.message.contents;
1488
1531
  // Some other client turned on the id compressor. If we have not turned it on,
1489
1532
  // put it in a pending queue and delay finalization.
1490
1533
  if (this._idCompressor === undefined) {
1491
- assert(this.idCompressorMode !== undefined, "id compressor should be enabled");
1534
+ assert(this.idCompressorMode !== undefined, 0x93c /* id compressor should be enabled */);
1492
1535
  this.pendingIdCompressorOps.push(range);
1493
1536
  }
1494
1537
  else {
1538
+ assert(this.pendingIdCompressorOps.length === 0, "there should be no pending ops!");
1495
1539
  this._idCompressor.finalizeCreationRange(range);
1496
1540
  }
1497
1541
  }
@@ -1502,7 +1546,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1502
1546
  case ContainerMessageType.ChunkedOp:
1503
1547
  // From observability POV, we should not exppse the rest of the system (including "op" events on object) to these messages.
1504
1548
  // Also resetReconnectCount() would be wrong - see comment that was there before this change was made.
1505
- assert(false, "should not even get here");
1549
+ assert(false, 0x93d /* should not even get here */);
1506
1550
  case ContainerMessageType.Rejoin:
1507
1551
  break;
1508
1552
  case ContainerMessageType.DocumentSchemaChange:
@@ -1612,9 +1656,9 @@ export class ContainerRuntime extends TypedEventEmitter {
1612
1656
  let checkpoint;
1613
1657
  let result;
1614
1658
  if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
1615
- // Note: we are not touching this.pendingAttachBatch here, for two reasons:
1616
- // 1. It would not help, as we flush attach ops as they become available.
1617
- // 2. There is no way to undo process of data store creation.
1659
+ // Note: we are not touching any batches other than mainBatch here, for two reasons:
1660
+ // 1. It would not help, as other batches are flushed independently from main batch.
1661
+ // 2. There is no way to undo process of data store creation, blob creation, ID compressor ops, or other things tracked by other batches.
1618
1662
  checkpoint = this.outbox.checkpoint().mainBatch;
1619
1663
  }
1620
1664
  try {
@@ -1677,7 +1721,11 @@ export class ContainerRuntime extends TypedEventEmitter {
1677
1721
  if (channel.entryPoint === undefined) {
1678
1722
  throw new UsageError("entryPoint must be defined on data store runtime for using getAliasedDataStoreEntryPoint");
1679
1723
  }
1680
- this.garbageCollector.nodeUpdated(`/${internalId}`, "Loaded", undefined /* timestampMs */, context.packagePath);
1724
+ this.garbageCollector.nodeUpdated({
1725
+ node: { type: "DataStore", path: `/${internalId}` },
1726
+ reason: "Loaded",
1727
+ packagePath: context.packagePath,
1728
+ });
1681
1729
  return channel.entryPoint;
1682
1730
  }
1683
1731
  createDetachedDataStore(pkg, loadingGroupId) {
@@ -1801,7 +1849,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1801
1849
  // We can finalize any allocated IDs since we're the only client
1802
1850
  const idRange = this._idCompressor?.takeNextCreationRange();
1803
1851
  if (idRange !== undefined) {
1804
- assert(idRange.ids === undefined || idRange.ids.firstGenCount === 1, "No other ranges should be taken while container is detached.");
1852
+ assert(idRange.ids === undefined || idRange.ids.firstGenCount === 1, 0x93e /* No other ranges should be taken while container is detached. */);
1805
1853
  this._idCompressor?.finalizeCreationRange(idRange);
1806
1854
  }
1807
1855
  const summarizeResult = this.channelCollection.getAttachSummary(telemetryContext);
@@ -2011,10 +2059,14 @@ export class ContainerRuntime extends TypedEventEmitter {
2011
2059
  // The summary number for this summary. This will be updated during the summary process, so get it now and
2012
2060
  // use it for all events logged during this summary.
2013
2061
  const summaryNumber = this.nextSummaryNumber;
2062
+ let summaryRefSeqNum;
2014
2063
  const summaryNumberLogger = createChildLogger({
2015
2064
  logger: summaryLogger,
2016
2065
  properties: {
2017
- all: { summaryNumber },
2066
+ all: {
2067
+ summaryNumber,
2068
+ referenceSequenceNumber: () => summaryRefSeqNum,
2069
+ },
2018
2070
  },
2019
2071
  });
2020
2072
  assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
@@ -2028,7 +2080,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2028
2080
  // If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
2029
2081
  // and it may even be incorrect. So, wait for the container to be saved with a timeout. If the container is not
2030
2082
  // saved within the timeout, check if it should be failed or can continue.
2031
- if (this.validateSummaryBeforeUpload && this.isDirty) {
2083
+ if (this.isDirty) {
2032
2084
  const countBefore = this.pendingMessagesCount;
2033
2085
  // The timeout for waiting for pending ops can be overridden via configurations.
2034
2086
  const pendingOpsTimeout = this.mc.config.getNumber("Fluid.Summarizer.waitForPendingOpsTimeoutMs") ??
@@ -2061,7 +2113,6 @@ export class ContainerRuntime extends TypedEventEmitter {
2061
2113
  }
2062
2114
  const shouldPauseInboundSignal = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.disableInboundSignalPause") !== true;
2063
2115
  const shouldValidatePreSummaryState = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.shouldValidatePreSummaryState") === true;
2064
- let summaryRefSeqNum;
2065
2116
  try {
2066
2117
  await this.deltaManager.inbound.pause();
2067
2118
  if (shouldPauseInboundSignal) {
@@ -2072,9 +2123,17 @@ export class ContainerRuntime extends TypedEventEmitter {
2072
2123
  const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
2073
2124
  const lastAck = this.summaryCollection.latestAck;
2074
2125
  const startSummaryResult = this.summarizerNode.startSummary(summaryRefSeqNum, summaryNumberLogger, latestSummaryRefSeqNum);
2126
+ /**
2127
+ * This was added to validate that the summarizer node tree has the same reference sequence number from the
2128
+ * top running summarizer down to the lowest summarizer node.
2129
+ *
2130
+ * The order of mismatch numbers goes (validate sequence number)-(node sequence number).
2131
+ * Generally the validate sequence number comes from the running summarizer and the node sequence number comes from the
2132
+ * summarizer nodes.
2133
+ */
2075
2134
  if (startSummaryResult.invalidNodes > 0 ||
2076
2135
  startSummaryResult.mismatchNumbers.size > 0) {
2077
- summaryLogger.sendErrorEvent({
2136
+ summaryLogger.sendTelemetryEvent({
2078
2137
  eventName: "LatestSummaryRefSeqNumMismatch",
2079
2138
  details: {
2080
2139
  ...startSummaryResult,
@@ -2086,7 +2145,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2086
2145
  stage: "base",
2087
2146
  referenceSequenceNumber: summaryRefSeqNum,
2088
2147
  minimumSequenceNumber,
2089
- error: `Summarizer node state inconsistent with summarizer state.`,
2148
+ error: new LoggingError(`Summarizer node state inconsistent with summarizer state.`),
2090
2149
  };
2091
2150
  }
2092
2151
  }
@@ -2129,7 +2188,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2129
2188
  stage: "base",
2130
2189
  referenceSequenceNumber: summaryRefSeqNum,
2131
2190
  minimumSequenceNumber,
2132
- error: continueResult.error,
2191
+ error: new LoggingError(continueResult.error),
2133
2192
  };
2134
2193
  }
2135
2194
  const trace = Trace.start();
@@ -2146,6 +2205,18 @@ export class ContainerRuntime extends TypedEventEmitter {
2146
2205
  });
2147
2206
  }
2148
2207
  catch (error) {
2208
+ return {
2209
+ stage: "base",
2210
+ referenceSequenceNumber: summaryRefSeqNum,
2211
+ minimumSequenceNumber,
2212
+ error: wrapError(error, (msg) => new LoggingError(msg)),
2213
+ };
2214
+ }
2215
+ // Validate that the summary generated by summarizer nodes is correct before uploading.
2216
+ const validateResult = this.summarizerNode.validateSummary();
2217
+ if (!validateResult.success) {
2218
+ const { success, ...loggingProps } = validateResult;
2219
+ const error = new RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
2149
2220
  return {
2150
2221
  stage: "base",
2151
2222
  referenceSequenceNumber: summaryRefSeqNum,
@@ -2153,24 +2224,11 @@ export class ContainerRuntime extends TypedEventEmitter {
2153
2224
  error,
2154
2225
  };
2155
2226
  }
2156
- // If validateSummaryBeforeUpload is true, validate that the summary generated is correct before uploading.
2157
- if (this.validateSummaryBeforeUpload) {
2158
- // Validate that the summaries generated by summarize nodes is correct.
2159
- const validateResult = this.summarizerNode.validateSummary();
2160
- if (!validateResult.success) {
2161
- const { success, ...loggingProps } = validateResult;
2162
- const error = new RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
2163
- return {
2164
- stage: "base",
2165
- referenceSequenceNumber: summaryRefSeqNum,
2166
- minimumSequenceNumber,
2167
- error,
2168
- };
2169
- }
2170
- const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, summaryRefSeqNum, minimumSequenceNumber, finalAttempt, false /* beforeSummaryGeneration */);
2171
- if (pendingMessagesFailResult !== undefined) {
2172
- return pendingMessagesFailResult;
2173
- }
2227
+ // If there are pending unacked ops, this summary attempt may fail as the uploaded
2228
+ // summary would be eventually inconsistent.
2229
+ const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, summaryRefSeqNum, minimumSequenceNumber, finalAttempt, false /* beforeSummaryGeneration */);
2230
+ if (pendingMessagesFailResult !== undefined) {
2231
+ return pendingMessagesFailResult;
2174
2232
  }
2175
2233
  const { summary: summaryTree, stats: partialStats } = summarizeResult;
2176
2234
  // Now that we have generated the summary, update the message at last summary to the last message processed.
@@ -2203,7 +2261,11 @@ export class ContainerRuntime extends TypedEventEmitter {
2203
2261
  };
2204
2262
  continueResult = checkContinue();
2205
2263
  if (!continueResult.continue) {
2206
- return { stage: "generate", ...generateSummaryData, error: continueResult.error };
2264
+ return {
2265
+ stage: "generate",
2266
+ ...generateSummaryData,
2267
+ error: new LoggingError(continueResult.error),
2268
+ };
2207
2269
  }
2208
2270
  const summaryContext = lastAck === undefined
2209
2271
  ? {
@@ -2221,7 +2283,11 @@ export class ContainerRuntime extends TypedEventEmitter {
2221
2283
  handle = await this.storage.uploadSummaryWithContext(summarizeResult.summary, summaryContext);
2222
2284
  }
2223
2285
  catch (error) {
2224
- return { stage: "generate", ...generateSummaryData, error };
2286
+ return {
2287
+ stage: "generate",
2288
+ ...generateSummaryData,
2289
+ error: wrapError(error, (msg) => new LoggingError(msg)),
2290
+ };
2225
2291
  }
2226
2292
  const parent = summaryContext.ackHandle;
2227
2293
  const summaryMessage = {
@@ -2238,14 +2304,22 @@ export class ContainerRuntime extends TypedEventEmitter {
2238
2304
  };
2239
2305
  continueResult = checkContinue();
2240
2306
  if (!continueResult.continue) {
2241
- return { stage: "upload", ...uploadData, error: continueResult.error };
2307
+ return {
2308
+ stage: "upload",
2309
+ ...uploadData,
2310
+ error: new LoggingError(continueResult.error),
2311
+ };
2242
2312
  }
2243
2313
  let clientSequenceNumber;
2244
2314
  try {
2245
2315
  clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
2246
2316
  }
2247
2317
  catch (error) {
2248
- return { stage: "upload", ...uploadData, error };
2318
+ return {
2319
+ stage: "upload",
2320
+ ...uploadData,
2321
+ error: wrapError(error, (msg) => new LoggingError(msg)),
2322
+ };
2249
2323
  }
2250
2324
  const submitData = {
2251
2325
  stage: "submit",
@@ -2254,11 +2328,14 @@ export class ContainerRuntime extends TypedEventEmitter {
2254
2328
  submitOpDuration: trace.trace().duration,
2255
2329
  };
2256
2330
  try {
2257
- // If validateSummaryBeforeUpload is false, the summary should be validated in this step.
2258
- this.summarizerNode.completeSummary(handle, !this.validateSummaryBeforeUpload /* validate */);
2331
+ this.summarizerNode.completeSummary(handle);
2259
2332
  }
2260
2333
  catch (error) {
2261
- return { stage: "upload", ...uploadData, error };
2334
+ return {
2335
+ stage: "upload",
2336
+ ...uploadData,
2337
+ error: wrapError(error, (msg) => new LoggingError(msg)),
2338
+ };
2262
2339
  }
2263
2340
  return submitData;
2264
2341
  }
@@ -2366,11 +2443,10 @@ export class ContainerRuntime extends TypedEventEmitter {
2366
2443
  }
2367
2444
  submit(containerRuntimeMessage, localOpMetadata = undefined, metadata) {
2368
2445
  this.verifyNotClosed();
2369
- this.verifyCanSubmitOps();
2370
2446
  // There should be no ops in detached container state!
2371
2447
  assert(this.attachState !== AttachState.Detached, 0x132 /* "sending ops in detached container" */);
2372
2448
  assert(metadata === undefined ||
2373
- containerRuntimeMessage.type === ContainerMessageType.BlobAttach, "metadata");
2449
+ containerRuntimeMessage.type === ContainerMessageType.BlobAttach, 0x93f /* metadata */);
2374
2450
  const serializedContent = JSON.stringify(containerRuntimeMessage);
2375
2451
  // Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
2376
2452
  // container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
@@ -2402,6 +2478,14 @@ export class ContainerRuntime extends TypedEventEmitter {
2402
2478
  // on this callback to do actual sending.
2403
2479
  const contents = this.documentsSchemaController.maybeSendSchemaMessage();
2404
2480
  if (contents) {
2481
+ this.logger.sendTelemetryEvent({
2482
+ eventName: "SchemaChangeProposal",
2483
+ refSeq: contents.refSeq,
2484
+ version: contents.version,
2485
+ newRuntimeSchema: JSON.stringify(contents.runtime),
2486
+ sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
2487
+ oldRuntimeSchema: JSON.stringify(this.metadata?.documentSchema?.runtime),
2488
+ });
2405
2489
  const msg = {
2406
2490
  type: ContainerMessageType.DocumentSchemaChange,
2407
2491
  contents,
@@ -2411,32 +2495,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2411
2495
  referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
2412
2496
  });
2413
2497
  }
2414
- // If this is attach message for new data store, and we are in a batch, send this op out of order
2415
- // Is it safe:
2416
- // Yes, this should be safe reordering. Newly created data stores are not visible through API surface.
2417
- // They become visible only when aliased, or handle to some sub-element of newly created datastore
2418
- // is stored in some DDS, i.e. only after some other op.
2419
- // Why:
2420
- // Attach ops are large, and expensive to process. Plus there are scenarios where a lot of new data
2421
- // stores are created, causing issues like relay service throttling (too many ops) and catastrophic
2422
- // failure (batch is too large). Pushing them earlier and outside of main batch should alleviate
2423
- // these issues.
2424
- // Cons:
2425
- // 1. With large batches, relay service may throttle clients. Clients may disconnect while throttled.
2426
- // This change creates new possibility of a lot of newly created data stores never being referenced
2427
- // because client died before it had a change to submit the rest of the ops. This will create more
2428
- // garbage that needs to be collected leveraging GC (Garbage Collection) feature.
2429
- // 2. Sending ops out of order means they are excluded from rollback functionality. This is not an issue
2430
- // today as rollback can't undo creation of data store. To some extent not sending them is a bigger
2431
- // issue than sending.
2432
- // Please note that this does not change file format, so it can be disabled in the future if this
2433
- // optimization no longer makes sense (for example, batch compression may make it less appealing).
2434
- if (this.currentlyBatching() &&
2435
- type === ContainerMessageType.Attach &&
2436
- this.disableAttachReorder !== true) {
2437
- this.outbox.submitAttach(message);
2438
- }
2439
- else if (type === ContainerMessageType.BlobAttach) {
2498
+ if (type === ContainerMessageType.BlobAttach) {
2440
2499
  // BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
2441
2500
  this.outbox.submitBlobAttach(message);
2442
2501
  }
@@ -2511,32 +2570,6 @@ export class ContainerRuntime extends TypedEventEmitter {
2511
2570
  throw new Error("Runtime is closed");
2512
2571
  }
2513
2572
  }
2514
- verifyCanSubmitOps() {
2515
- if (this.ensureNoDataModelChangesCalls > 0) {
2516
- const errorMessage = "Op was submitted from within a `ensureNoDataModelChanges` callback";
2517
- if (this.opReentryCallsToReport > 0) {
2518
- this.mc.logger.sendTelemetryEvent({ eventName: "OpReentry" },
2519
- // We need to capture the call stack in order to inspect the source of this usage pattern
2520
- getLongStack(() => new UsageError(errorMessage)));
2521
- this.opReentryCallsToReport--;
2522
- }
2523
- // Creating ops while processing ops can lead
2524
- // to undefined behavior and events observed in the wrong order.
2525
- // For example, we have two callbacks registered for a DDS, A and B.
2526
- // Then if on change #1 callback A creates change #2, the invocation flow will be:
2527
- //
2528
- // A because of #1
2529
- // A because of #2
2530
- // B because of #2
2531
- // B because of #1
2532
- //
2533
- // The runtime must enforce op coherence by not allowing ops to be submitted
2534
- // while ops are being processed.
2535
- if (this.enableOpReentryCheck) {
2536
- throw new UsageError(errorMessage);
2537
- }
2538
- }
2539
- }
2540
2573
  reSubmitBatch(batch) {
2541
2574
  this.orderSequentially(() => {
2542
2575
  for (const message of batch) {
@@ -2666,7 +2699,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2666
2699
  await this.closeStaleSummarizer();
2667
2700
  return {
2668
2701
  stage: "base",
2669
- error: "summary state stale - Unsupported option 'refreshLatestAck'",
2702
+ error: new LoggingError("summary state stale - Unsupported option 'refreshLatestAck'"),
2670
2703
  referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
2671
2704
  minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
2672
2705
  };
@@ -2710,16 +2743,14 @@ export class ContainerRuntime extends TypedEventEmitter {
2710
2743
  }
2711
2744
  this.imminentClosure || (this.imminentClosure = props?.notifyImminentClosure ?? false);
2712
2745
  const getSyncState = (pendingAttachmentBlobs) => {
2713
- const pending = this.pendingStateManager.getLocalState();
2714
- if (pendingAttachmentBlobs === undefined && !this.hasPendingMessages()) {
2715
- return; // no pending state to save
2716
- }
2746
+ const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
2747
+ const sessionExpiryTimerStarted = props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
2717
2748
  const pendingIdCompressorState = this._idCompressor?.serialize(true);
2718
2749
  return {
2719
2750
  pending,
2720
2751
  pendingIdCompressorState,
2721
2752
  pendingAttachmentBlobs,
2722
- sessionExpiryTimerStarted: this.garbageCollector.sessionExpiryTimerStarted,
2753
+ sessionExpiryTimerStarted,
2723
2754
  };
2724
2755
  };
2725
2756
  const perfEvent = {
@@ -2789,7 +2820,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2789
2820
  }
2790
2821
  }
2791
2822
  get groupedBatchingEnabled() {
2792
- return this.documentSchema.opGroupingEnabled === true;
2823
+ return this.sessionSchema.opGroupingEnabled === true;
2793
2824
  }
2794
2825
  }
2795
2826
  //# sourceMappingURL=containerRuntime.js.map