@fluidframework/container-runtime 2.0.0-rc.3.0.2 → 2.0.0-rc.4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/api-report/container-runtime.api.md +72 -34
  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/blobManager.d.ts +7 -7
  7. package/dist/blobManager.d.ts.map +1 -1
  8. package/dist/blobManager.js +2 -4
  9. package/dist/blobManager.js.map +1 -1
  10. package/dist/channelCollection.d.ts +6 -4
  11. package/dist/channelCollection.d.ts.map +1 -1
  12. package/dist/channelCollection.js +20 -7
  13. package/dist/channelCollection.js.map +1 -1
  14. package/dist/connectionTelemetry.d.ts +2 -2
  15. package/dist/connectionTelemetry.d.ts.map +1 -1
  16. package/dist/connectionTelemetry.js +54 -5
  17. package/dist/connectionTelemetry.js.map +1 -1
  18. package/dist/containerRuntime.d.ts +17 -35
  19. package/dist/containerRuntime.d.ts.map +1 -1
  20. package/dist/containerRuntime.js +182 -160
  21. package/dist/containerRuntime.js.map +1 -1
  22. package/dist/dataStore.d.ts +1 -1
  23. package/dist/dataStore.d.ts.map +1 -1
  24. package/dist/dataStore.js.map +1 -1
  25. package/dist/dataStoreContext.d.ts +9 -6
  26. package/dist/dataStoreContext.d.ts.map +1 -1
  27. package/dist/dataStoreContext.js +19 -5
  28. package/dist/dataStoreContext.js.map +1 -1
  29. package/dist/dataStoreContexts.d.ts.map +1 -1
  30. package/dist/dataStoreContexts.js.map +1 -1
  31. package/dist/deltaManagerProxies.d.ts +81 -0
  32. package/dist/deltaManagerProxies.d.ts.map +1 -0
  33. package/dist/{deltaManagerSummarizerProxy.js → deltaManagerProxies.js} +75 -20
  34. package/dist/deltaManagerProxies.js.map +1 -0
  35. package/dist/deltaScheduler.d.ts +2 -2
  36. package/dist/deltaScheduler.d.ts.map +1 -1
  37. package/dist/deltaScheduler.js.map +1 -1
  38. package/dist/gc/garbageCollection.d.ts +1 -1
  39. package/dist/gc/garbageCollection.d.ts.map +1 -1
  40. package/dist/gc/garbageCollection.js.map +1 -1
  41. package/dist/gc/gcDefinitions.d.ts +1 -1
  42. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  43. package/dist/gc/gcDefinitions.js.map +1 -1
  44. package/dist/gc/gcTelemetry.d.ts +1 -2
  45. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  46. package/dist/gc/gcTelemetry.js.map +1 -1
  47. package/dist/index.d.ts +1 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js.map +1 -1
  50. package/dist/legacy.d.ts +6 -1
  51. package/dist/messageTypes.d.ts +5 -2
  52. package/dist/messageTypes.d.ts.map +1 -1
  53. package/dist/messageTypes.js.map +1 -1
  54. package/dist/metadata.d.ts +2 -2
  55. package/dist/metadata.d.ts.map +1 -1
  56. package/dist/metadata.js.map +1 -1
  57. package/dist/opLifecycle/batchManager.d.ts +0 -1
  58. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  59. package/dist/opLifecycle/batchManager.js +0 -10
  60. package/dist/opLifecycle/batchManager.js.map +1 -1
  61. package/dist/opLifecycle/outbox.d.ts +0 -4
  62. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  63. package/dist/opLifecycle/outbox.js +2 -34
  64. package/dist/opLifecycle/outbox.js.map +1 -1
  65. package/dist/packageVersion.d.ts +1 -1
  66. package/dist/packageVersion.js +1 -1
  67. package/dist/packageVersion.js.map +1 -1
  68. package/dist/pendingStateManager.d.ts +9 -2
  69. package/dist/pendingStateManager.d.ts.map +1 -1
  70. package/dist/pendingStateManager.js +26 -10
  71. package/dist/pendingStateManager.js.map +1 -1
  72. package/dist/scheduleManager.d.ts +2 -2
  73. package/dist/scheduleManager.d.ts.map +1 -1
  74. package/dist/scheduleManager.js.map +1 -1
  75. package/dist/summary/documentSchema.d.ts +3 -1
  76. package/dist/summary/documentSchema.d.ts.map +1 -1
  77. package/dist/summary/documentSchema.js +25 -7
  78. package/dist/summary/documentSchema.js.map +1 -1
  79. package/dist/summary/index.d.ts +1 -1
  80. package/dist/summary/index.d.ts.map +1 -1
  81. package/dist/summary/index.js.map +1 -1
  82. package/dist/summary/orderedClientElection.d.ts +2 -2
  83. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  84. package/dist/summary/orderedClientElection.js.map +1 -1
  85. package/dist/summary/runningSummarizer.js +10 -10
  86. package/dist/summary/runningSummarizer.js.map +1 -1
  87. package/dist/summary/summarizer.d.ts +1 -2
  88. package/dist/summary/summarizer.d.ts.map +1 -1
  89. package/dist/summary/summarizer.js.map +1 -1
  90. package/dist/summary/summarizerClientElection.d.ts +1 -1
  91. package/dist/summary/summarizerClientElection.d.ts.map +1 -1
  92. package/dist/summary/summarizerClientElection.js.map +1 -1
  93. package/dist/summary/summarizerHeuristics.d.ts +1 -1
  94. package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
  95. package/dist/summary/summarizerHeuristics.js.map +1 -1
  96. package/dist/summary/summarizerNode/summarizerNode.d.ts +4 -3
  97. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  98. package/dist/summary/summarizerNode/summarizerNode.js +4 -10
  99. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  100. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
  101. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  102. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  103. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -2
  104. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  105. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
  106. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  107. package/dist/summary/summarizerTypes.d.ts +3 -5
  108. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  109. package/dist/summary/summarizerTypes.js.map +1 -1
  110. package/dist/summary/summaryCollection.d.ts +2 -2
  111. package/dist/summary/summaryCollection.d.ts.map +1 -1
  112. package/dist/summary/summaryCollection.js.map +1 -1
  113. package/dist/summary/summaryFormat.d.ts +25 -5
  114. package/dist/summary/summaryFormat.d.ts.map +1 -1
  115. package/dist/summary/summaryFormat.js.map +1 -1
  116. package/dist/summary/summaryGenerator.d.ts +1 -2
  117. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  118. package/dist/summary/summaryGenerator.js +12 -11
  119. package/dist/summary/summaryGenerator.js.map +1 -1
  120. package/dist/summary/summaryManager.d.ts.map +1 -1
  121. package/dist/summary/summaryManager.js +5 -5
  122. package/dist/summary/summaryManager.js.map +1 -1
  123. package/lib/batchTracker.d.ts +1 -1
  124. package/lib/batchTracker.d.ts.map +1 -1
  125. package/lib/batchTracker.js.map +1 -1
  126. package/lib/blobManager.d.ts +7 -7
  127. package/lib/blobManager.d.ts.map +1 -1
  128. package/lib/blobManager.js +3 -5
  129. package/lib/blobManager.js.map +1 -1
  130. package/lib/channelCollection.d.ts +6 -4
  131. package/lib/channelCollection.d.ts.map +1 -1
  132. package/lib/channelCollection.js +21 -8
  133. package/lib/channelCollection.js.map +1 -1
  134. package/lib/connectionTelemetry.d.ts +2 -2
  135. package/lib/connectionTelemetry.d.ts.map +1 -1
  136. package/lib/connectionTelemetry.js +49 -0
  137. package/lib/connectionTelemetry.js.map +1 -1
  138. package/lib/containerRuntime.d.ts +17 -35
  139. package/lib/containerRuntime.d.ts.map +1 -1
  140. package/lib/containerRuntime.js +183 -161
  141. package/lib/containerRuntime.js.map +1 -1
  142. package/lib/dataStore.d.ts +1 -1
  143. package/lib/dataStore.d.ts.map +1 -1
  144. package/lib/dataStore.js +1 -1
  145. package/lib/dataStore.js.map +1 -1
  146. package/lib/dataStoreContext.d.ts +9 -6
  147. package/lib/dataStoreContext.d.ts.map +1 -1
  148. package/lib/dataStoreContext.js +21 -7
  149. package/lib/dataStoreContext.js.map +1 -1
  150. package/lib/dataStoreContexts.d.ts.map +1 -1
  151. package/lib/dataStoreContexts.js.map +1 -1
  152. package/lib/deltaManagerProxies.d.ts +81 -0
  153. package/lib/deltaManagerProxies.d.ts.map +1 -0
  154. package/lib/{deltaManagerSummarizerProxy.js → deltaManagerProxies.js} +72 -19
  155. package/lib/deltaManagerProxies.js.map +1 -0
  156. package/lib/deltaScheduler.d.ts +2 -2
  157. package/lib/deltaScheduler.d.ts.map +1 -1
  158. package/lib/deltaScheduler.js.map +1 -1
  159. package/lib/gc/garbageCollection.d.ts +1 -1
  160. package/lib/gc/garbageCollection.d.ts.map +1 -1
  161. package/lib/gc/garbageCollection.js.map +1 -1
  162. package/lib/gc/gcDefinitions.d.ts +1 -1
  163. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  164. package/lib/gc/gcDefinitions.js.map +1 -1
  165. package/lib/gc/gcTelemetry.d.ts +1 -2
  166. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  167. package/lib/gc/gcTelemetry.js.map +1 -1
  168. package/lib/index.d.ts +1 -1
  169. package/lib/index.d.ts.map +1 -1
  170. package/lib/index.js.map +1 -1
  171. package/lib/legacy.d.ts +6 -1
  172. package/lib/messageTypes.d.ts +5 -2
  173. package/lib/messageTypes.d.ts.map +1 -1
  174. package/lib/messageTypes.js.map +1 -1
  175. package/lib/metadata.d.ts +2 -2
  176. package/lib/metadata.d.ts.map +1 -1
  177. package/lib/metadata.js.map +1 -1
  178. package/lib/opLifecycle/batchManager.d.ts +0 -1
  179. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  180. package/lib/opLifecycle/batchManager.js +0 -10
  181. package/lib/opLifecycle/batchManager.js.map +1 -1
  182. package/lib/opLifecycle/outbox.d.ts +0 -4
  183. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  184. package/lib/opLifecycle/outbox.js +2 -34
  185. package/lib/opLifecycle/outbox.js.map +1 -1
  186. package/lib/packageVersion.d.ts +1 -1
  187. package/lib/packageVersion.js +1 -1
  188. package/lib/packageVersion.js.map +1 -1
  189. package/lib/pendingStateManager.d.ts +9 -2
  190. package/lib/pendingStateManager.d.ts.map +1 -1
  191. package/lib/pendingStateManager.js +27 -11
  192. package/lib/pendingStateManager.js.map +1 -1
  193. package/lib/scheduleManager.d.ts +2 -2
  194. package/lib/scheduleManager.d.ts.map +1 -1
  195. package/lib/scheduleManager.js.map +1 -1
  196. package/lib/summary/documentSchema.d.ts +3 -1
  197. package/lib/summary/documentSchema.d.ts.map +1 -1
  198. package/lib/summary/documentSchema.js +25 -7
  199. package/lib/summary/documentSchema.js.map +1 -1
  200. package/lib/summary/index.d.ts +1 -1
  201. package/lib/summary/index.d.ts.map +1 -1
  202. package/lib/summary/index.js.map +1 -1
  203. package/lib/summary/orderedClientElection.d.ts +2 -2
  204. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  205. package/lib/summary/orderedClientElection.js +1 -1
  206. package/lib/summary/orderedClientElection.js.map +1 -1
  207. package/lib/summary/runningSummarizer.js +1 -1
  208. package/lib/summary/runningSummarizer.js.map +1 -1
  209. package/lib/summary/summarizer.d.ts +1 -2
  210. package/lib/summary/summarizer.d.ts.map +1 -1
  211. package/lib/summary/summarizer.js.map +1 -1
  212. package/lib/summary/summarizerClientElection.d.ts +1 -1
  213. package/lib/summary/summarizerClientElection.d.ts.map +1 -1
  214. package/lib/summary/summarizerClientElection.js.map +1 -1
  215. package/lib/summary/summarizerHeuristics.d.ts +1 -1
  216. package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
  217. package/lib/summary/summarizerHeuristics.js.map +1 -1
  218. package/lib/summary/summarizerNode/summarizerNode.d.ts +4 -3
  219. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  220. package/lib/summary/summarizerNode/summarizerNode.js +4 -10
  221. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  222. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
  223. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  224. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  225. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -2
  226. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  227. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
  228. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  229. package/lib/summary/summarizerTypes.d.ts +3 -5
  230. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  231. package/lib/summary/summarizerTypes.js.map +1 -1
  232. package/lib/summary/summaryCollection.d.ts +2 -2
  233. package/lib/summary/summaryCollection.d.ts.map +1 -1
  234. package/lib/summary/summaryCollection.js.map +1 -1
  235. package/lib/summary/summaryFormat.d.ts +25 -5
  236. package/lib/summary/summaryFormat.d.ts.map +1 -1
  237. package/lib/summary/summaryFormat.js.map +1 -1
  238. package/lib/summary/summaryGenerator.d.ts +1 -2
  239. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  240. package/lib/summary/summaryGenerator.js +5 -4
  241. package/lib/summary/summaryGenerator.js.map +1 -1
  242. package/lib/summary/summaryManager.d.ts.map +1 -1
  243. package/lib/summary/summaryManager.js +2 -2
  244. package/lib/summary/summaryManager.js.map +1 -1
  245. package/lib/tsdoc-metadata.json +1 -1
  246. package/package.json +28 -50
  247. package/src/batchTracker.ts +1 -2
  248. package/src/blobManager.ts +11 -10
  249. package/src/channelCollection.ts +30 -12
  250. package/src/connectionTelemetry.ts +59 -4
  251. package/src/containerRuntime.ts +250 -236
  252. package/src/dataStore.ts +7 -4
  253. package/src/dataStoreContext.ts +57 -16
  254. package/src/dataStoreContexts.ts +1 -2
  255. package/src/{deltaManagerSummarizerProxy.ts → deltaManagerProxies.ts} +98 -24
  256. package/src/deltaScheduler.ts +2 -3
  257. package/src/gc/garbageCollection.ts +1 -1
  258. package/src/gc/gcDefinitions.ts +1 -1
  259. package/src/gc/gcTelemetry.ts +1 -3
  260. package/src/index.ts +5 -0
  261. package/src/messageTypes.ts +4 -2
  262. package/src/metadata.ts +2 -2
  263. package/src/opLifecycle/README.md +4 -4
  264. package/src/opLifecycle/batchManager.ts +0 -14
  265. package/src/opLifecycle/outbox.ts +2 -49
  266. package/src/packageVersion.ts +1 -1
  267. package/src/pendingStateManager.ts +38 -15
  268. package/src/scheduleManager.ts +2 -2
  269. package/src/summary/documentSchema.ts +37 -12
  270. package/src/summary/index.ts +4 -0
  271. package/src/summary/orderedClientElection.ts +6 -3
  272. package/src/summary/runningSummarizer.ts +1 -1
  273. package/src/summary/summarizer.ts +1 -1
  274. package/src/summary/summarizerClientElection.ts +1 -1
  275. package/src/summary/summarizerHeuristics.ts +1 -1
  276. package/src/summary/summarizerNode/summarizerNode.ts +3 -12
  277. package/src/summary/summarizerNode/summarizerNodeUtils.ts +2 -3
  278. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +1 -10
  279. package/src/summary/summarizerTypes.ts +6 -5
  280. package/src/summary/summaryCollection.ts +2 -2
  281. package/src/summary/summaryFormat.ts +30 -4
  282. package/src/summary/summaryGenerator.ts +20 -9
  283. package/src/summary/summaryManager.ts +6 -3
  284. package/dist/deltaManagerSummarizerProxy.d.ts +0 -44
  285. package/dist/deltaManagerSummarizerProxy.d.ts.map +0 -1
  286. package/dist/deltaManagerSummarizerProxy.js.map +0 -1
  287. package/lib/deltaManagerSummarizerProxy.d.ts +0 -44
  288. package/lib/deltaManagerSummarizerProxy.d.ts.map +0 -1
  289. package/lib/deltaManagerSummarizerProxy.js.map +0 -1
@@ -22,10 +22,10 @@ import { ReportOpPerfTelemetry } from "./connectionTelemetry.js";
22
22
  import { ContainerFluidHandleContext } from "./containerHandleContext.js";
23
23
  import { channelToDataStore } from "./dataStore.js";
24
24
  import { FluidDataStoreRegistry } from "./dataStoreRegistry.js";
25
- import { DeltaManagerSummarizerProxy } from "./deltaManagerSummarizerProxy.js";
25
+ import { DeltaManagerPendingOpsProxy, DeltaManagerSummarizerProxy } from "./deltaManagerProxies.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";
@@ -246,7 +246,7 @@ export class ContainerRuntime extends TypedEventEmitter {
246
246
  },
247
247
  });
248
248
  const mc = loggerToMonitoringContext(logger);
249
- 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 = true, explicitSchemaControl = false, } = runtimeOptions;
250
250
  const registry = new FluidDataStoreRegistry(registryEntries);
251
251
  const tryFetchBlob = async (blobName) => {
252
252
  const blobId = context.baseSnapshot?.blobs[blobName];
@@ -274,9 +274,9 @@ export class ContainerRuntime extends TypedEventEmitter {
274
274
  const messageAtLastSummary = lastMessageFromMetadata(metadata);
275
275
  // Verify summary runtime sequence number matches protocol sequence number.
276
276
  const runtimeSequenceNumber = messageAtLastSummary?.sequenceNumber;
277
+ const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
277
278
  // When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
278
279
  if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
279
- const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
280
280
  // Unless bypass is explicitly set, then take action when sequence numbers mismatch.
281
281
  if (loadSequenceNumberVerification !== "bypass" &&
282
282
  runtimeSequenceNumber !== protocolSequenceNumber) {
@@ -368,23 +368,20 @@ export class ContainerRuntime extends TypedEventEmitter {
368
368
  return createIdCompressor(compressorLogger);
369
369
  }
370
370
  };
371
- const disableGroupedBatching = mc.config.getBoolean("Fluid.ContainerRuntime.DisableGroupedBatching");
372
371
  const disableCompression = mc.config.getBoolean("Fluid.ContainerRuntime.CompressionDisabled");
373
372
  const compressionLz4 = disableCompression !== true &&
374
373
  compressionOptions.minimumBatchSizeInBytes !== Infinity &&
375
374
  compressionOptions.compressionAlgorithm === "lz4";
376
- const opGroupingEnabled = disableGroupedBatching !== true && enableGroupedBatching;
377
- const documentSchemaController = new DocumentsSchemaController(existing, metadata?.documentSchema, {
375
+ const documentSchemaController = new DocumentsSchemaController(existing, protocolSequenceNumber, metadata?.documentSchema, {
378
376
  explicitSchemaControl,
379
377
  compressionLz4,
380
378
  idCompressorMode,
381
- opGroupingEnabled,
379
+ opGroupingEnabled: enableGroupedBatching,
382
380
  disallowedVersions: [],
383
381
  }, (schema) => {
384
382
  runtime.onSchemaChange(schema);
385
383
  });
386
384
  const featureGatesForTelemetry = {
387
- disableGroupedBatching,
388
385
  disableCompression,
389
386
  };
390
387
  const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], {
@@ -397,7 +394,6 @@ export class ContainerRuntime extends TypedEventEmitter {
397
394
  chunkSizeInBytes,
398
395
  // Requires<> drops undefined from IdCompressorType
399
396
  enableRuntimeIdCompressor: enableRuntimeIdCompressor,
400
- enableOpReentryCheck,
401
397
  enableGroupedBatching,
402
398
  explicitSchemaControl,
403
399
  }, containerScope, logger, existing, blobManagerSnapshot, context.storage, createIdCompressorFn, documentSchemaController, featureGatesForTelemetry, provideEntryPoint, requestHandler, undefined);
@@ -436,11 +432,21 @@ export class ContainerRuntime extends TypedEventEmitter {
436
432
  get attachState() {
437
433
  return this._getAttachState();
438
434
  }
439
- get documentSchema() {
435
+ /**
436
+ * Current session schema - defines what options are on & off.
437
+ * It's overlap of document schema (controlled by summary & ops) and options controlling this session.
438
+ * For example, document schema might have compression ON, but feature gates / runtime options turn it Off.
439
+ * In such case it will be off in session schema (i.e. this session should not use compression), but this client
440
+ * has to deal with compressed ops as other clients might send them.
441
+ * And in reverse, session schema can have compression Off, but feature gates / runtime options want it On.
442
+ * In such case it will be off in session schema, however this client will propose change to schema, and once / if
443
+ * this op rountrips, compression will be On. Client can't send compressed ops until it's change in schema.
444
+ */
445
+ get sessionSchema() {
440
446
  return this.documentsSchemaController.sessionSchema.runtime;
441
447
  }
442
448
  get idCompressorMode() {
443
- return this.documentSchema.idCompressorMode;
449
+ return this.sessionSchema.idCompressorMode;
444
450
  }
445
451
  /**
446
452
  * See IContainerRuntimeBase.idCompressor() for details.
@@ -548,13 +554,6 @@ export class ContainerRuntime extends TypedEventEmitter {
548
554
  this.flushTaskExists = false;
549
555
  this.consecutiveReconnects = 0;
550
556
  this.ensureNoDataModelChangesCalls = 0;
551
- /**
552
- * Tracks the number of detected reentrant ops to report,
553
- * in order to self-throttle the telemetry events.
554
- *
555
- * This should be removed as part of ADO:2322
556
- */
557
- this.opReentryCallsToReport = 5;
558
557
  this._disposed = false;
559
558
  this.emitDirtyDocumentEvent = true;
560
559
  this.defaultTelemetrySignalSampleCount = 100;
@@ -571,7 +570,7 @@ export class ContainerRuntime extends TypedEventEmitter {
571
570
  this.snapshotCacheForLoadingGroupIds = new PromiseCache({
572
571
  expiry: { policy: "absolute", durationMs: 60000 },
573
572
  });
574
- const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, loader, pendingLocalState, supportedFeatures, } = context;
573
+ const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, loader, pendingLocalState, supportedFeatures, snapshotWithContents, } = context;
575
574
  this.mc = createChildMonitoringContext({
576
575
  logger: this.logger,
577
576
  namespace: "ContainerRuntime",
@@ -581,13 +580,12 @@ export class ContainerRuntime extends TypedEventEmitter {
581
580
  // If it's not in the list, then we will need to either use no compression, or fallback to some other (supported by format)
582
581
  // compression.
583
582
  const compressionOptions = {
584
- minimumBatchSizeInBytes: this.documentSchema.compressionLz4
583
+ minimumBatchSizeInBytes: this.sessionSchema.compressionLz4
585
584
  ? runtimeOptions.compressionOptions.minimumBatchSizeInBytes
586
585
  : Number.POSITIVE_INFINITY,
587
586
  compressionAlgorithm: CompressionAlgorithms.lz4,
588
587
  };
589
588
  this.innerDeltaManager = deltaManager;
590
- this.deltaManager = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
591
589
  // Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
592
590
  // This makes ContainerRuntime the final gatekeeper for outgoing messages.
593
591
  this.submitFn = submitFn;
@@ -661,15 +659,36 @@ export class ContainerRuntime extends TypedEventEmitter {
661
659
  }, this.mc.logger);
662
660
  const opSplitter = new OpSplitter(chunks, this.submitBatchFn, disableChunking === true ? Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger);
663
661
  this.remoteMessageProcessor = new RemoteMessageProcessor(opSplitter, new OpDecompressor(this.mc.logger), opGroupingManager);
662
+ const pendingRuntimeState = pendingLocalState;
663
+ this.pendingStateManager = new PendingStateManager({
664
+ applyStashedOp: this.applyStashedOp.bind(this),
665
+ clientId: () => this.clientId,
666
+ close: this.closeFn,
667
+ connected: () => this.connected,
668
+ reSubmit: (message) => {
669
+ this.reSubmit(message);
670
+ this.flush();
671
+ },
672
+ reSubmitBatch: this.reSubmitBatch.bind(this),
673
+ isActiveConnection: () => this.innerDeltaManager.active,
674
+ isAttached: () => this.attachState !== AttachState.Detached,
675
+ }, pendingRuntimeState?.pending, this.logger);
676
+ let outerDeltaManager;
677
+ const useDeltaManagerOpsProxy = this.mc.config.getBoolean("Fluid.ContainerRuntime.DeltaManagerOpsProxy") !== false;
678
+ // The summarizerDeltaManager Proxy is used to lie to the summarizer to convince it is in the right state as a summarizer client.
679
+ const summarizerDeltaManagerProxy = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
680
+ outerDeltaManager = summarizerDeltaManagerProxy;
681
+ // The DeltaManagerPendingOpsProxy is used to control the minimum sequence number
682
+ // It allows us to lie to the layers below so that they can maintain enough local state for rebasing ops.
683
+ if (useDeltaManagerOpsProxy) {
684
+ const pendingOpsDeltaManagerProxy = new DeltaManagerPendingOpsProxy(summarizerDeltaManagerProxy, this.pendingStateManager);
685
+ outerDeltaManager = pendingOpsDeltaManagerProxy;
686
+ }
687
+ this.deltaManager = outerDeltaManager;
664
688
  this.handleContext = new ContainerFluidHandleContext("", this);
665
689
  if (this.summaryConfiguration.state === "enabled") {
666
690
  this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
667
691
  }
668
- const disableOpReentryCheck = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck");
669
- this.enableOpReentryCheck =
670
- runtimeOptions.enableOpReentryCheck === true &&
671
- // Allow for a break-glass config to override the options
672
- disableOpReentryCheck !== true;
673
692
  this.summariesDisabled = this.isSummariesDisabled();
674
693
  this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
675
694
  this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
@@ -685,7 +704,6 @@ export class ContainerRuntime extends TypedEventEmitter {
685
704
  else {
686
705
  this._flushMode = runtimeOptions.flushMode;
687
706
  }
688
- const pendingRuntimeState = pendingLocalState;
689
707
  if (context.attachState === AttachState.Attached) {
690
708
  const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
691
709
  if (maxSnapshotCacheDurationMs !== undefined &&
@@ -741,7 +759,14 @@ export class ContainerRuntime extends TypedEventEmitter {
741
759
  const envelope2 = this.createNewSignalEnvelope(envelope1.address, type, envelope1.contents);
742
760
  return this.submitSignalFn(envelope2, targetClientId);
743
761
  };
744
- 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);
762
+ let snapshot = getSummaryForDatastores(baseSnapshot, metadata);
763
+ if (snapshot !== undefined && snapshotWithContents !== undefined) {
764
+ snapshot = {
765
+ ...snapshotWithContents,
766
+ snapshotTree: snapshot,
767
+ };
768
+ }
769
+ this.channelCollection = new ChannelCollection(snapshot, parentContext, this.mc.logger, (props) => this.garbageCollector.nodeUpdated(props), (path) => this.garbageCollector.isNodeDeleted(path), new Map(dataStoreAliasMap), async (runtime) => provideEntryPoint);
745
770
  this.blobManager = new BlobManager({
746
771
  routeContext: this.handleContext,
747
772
  snapshot: blobManagerSnapshot,
@@ -764,19 +789,6 @@ export class ContainerRuntime extends TypedEventEmitter {
764
789
  closeContainer: (error) => this.closeFn(error),
765
790
  });
766
791
  this.scheduleManager = new ScheduleManager(this.innerDeltaManager, this, () => this.clientId, createChildLogger({ logger: this.logger, namespace: "ScheduleManager" }));
767
- this.pendingStateManager = new PendingStateManager({
768
- applyStashedOp: this.applyStashedOp.bind(this),
769
- clientId: () => this.clientId,
770
- close: this.closeFn,
771
- connected: () => this.connected,
772
- reSubmit: (message) => {
773
- this.reSubmit(message);
774
- this.flush();
775
- },
776
- reSubmitBatch: this.reSubmitBatch.bind(this),
777
- isActiveConnection: () => this.innerDeltaManager.active,
778
- isAttached: () => this.attachState !== AttachState.Detached,
779
- }, pendingRuntimeState?.pending, this.logger);
780
792
  const disablePartialFlush = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisablePartialFlush");
781
793
  const legacySendBatchFn = makeLegacySendBatchFn(this.submitFn, this.innerDeltaManager);
782
794
  this.outbox = new Outbox({
@@ -805,12 +817,29 @@ export class ContainerRuntime extends TypedEventEmitter {
805
817
  this._quorum.on("removeMember", (clientId) => {
806
818
  this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
807
819
  });
808
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
809
820
  this._audience = audience;
821
+ if (audience.getSelf === undefined) {
822
+ // back-compat, added in 2.0 RC3.
823
+ // Purpose: deal with cases when we run against old loader that does not have newly added capabilities
824
+ audience.getSelf = () => {
825
+ const clientId = this._getClientId();
826
+ return clientId === undefined
827
+ ? undefined
828
+ : ({
829
+ clientId,
830
+ client: audience.getMember(clientId),
831
+ });
832
+ };
833
+ let oldClientId = this.clientId;
834
+ this.on("connected", () => {
835
+ const clientId = this.clientId;
836
+ assert(clientId !== undefined, 0x975 /* can't be undefined */);
837
+ audience.emit("selfChanged", { clientId: oldClientId }, { clientId, client: audience.getMember(clientId) });
838
+ oldClientId = clientId;
839
+ });
840
+ }
810
841
  const closeSummarizerDelayOverride = this.mc.config.getNumber("Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs");
811
842
  this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
812
- this.validateSummaryBeforeUpload =
813
- this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
814
843
  this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
815
844
  this.dirtyContainer =
816
845
  this.attachState !== AttachState.Attached || this.hasPendingMessages();
@@ -883,9 +912,9 @@ export class ContainerRuntime extends TypedEventEmitter {
883
912
  options: JSON.stringify(runtimeOptions),
884
913
  idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
885
914
  idCompressorMode: this.idCompressorMode,
915
+ sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
886
916
  featureGates: JSON.stringify({
887
917
  ...featureGatesForTelemetry,
888
- disableOpReentryCheck,
889
918
  disableChunking,
890
919
  disableAttachReorder: this.disableAttachReorder,
891
920
  disablePartialFlush,
@@ -893,6 +922,7 @@ export class ContainerRuntime extends TypedEventEmitter {
893
922
  }),
894
923
  telemetryDocumentId: this.telemetryDocumentId,
895
924
  groupedBatchingEnabled: this.groupedBatchingEnabled,
925
+ initialSequenceNumber: this.deltaManager.initialSequenceNumber,
896
926
  });
897
927
  ReportOpPerfTelemetry(this.clientId, this.deltaManager, this, this.logger);
898
928
  BindBatchTracker(this, this.logger);
@@ -908,6 +938,10 @@ export class ContainerRuntime extends TypedEventEmitter {
908
938
  this.skipSavedCompressorOps = pendingRuntimeState?.pendingIdCompressorState !== undefined;
909
939
  }
910
940
  onSchemaChange(schema) {
941
+ this.logger.sendTelemetryEvent({
942
+ eventName: "SchemaChangeAccept",
943
+ sessionRuntimeSchema: JSON.stringify(schema),
944
+ });
911
945
  // Most of the settings will be picked up only by new sessions (i.e. after reload).
912
946
  // We can make it better in the future (i.e. start to use op compression right away), but for simplicity
913
947
  // this is not done.
@@ -940,9 +974,9 @@ export class ContainerRuntime extends TypedEventEmitter {
940
974
  async initializeBaseState() {
941
975
  if (this.idCompressorMode === "on" ||
942
976
  (this.idCompressorMode === "delayed" && this.connected)) {
977
+ this._idCompressor = await this.createIdCompressor();
943
978
  // This is called from loadRuntime(), long before we process any ops, so there should be no ops accumulated yet.
944
979
  assert(this.pendingIdCompressorOps.length === 0, 0x8ec /* no pending ops */);
945
- this._idCompressor = await this.createIdCompressor();
946
980
  }
947
981
  await this.garbageCollector.initializeBaseState();
948
982
  }
@@ -1287,8 +1321,6 @@ export class ContainerRuntime extends TypedEventEmitter {
1287
1321
  return;
1288
1322
  case ContainerMessageType.BlobAttach:
1289
1323
  return;
1290
- case ContainerMessageType.ChunkedOp:
1291
- throw new Error("chunkedOp not expected here");
1292
1324
  case ContainerMessageType.Rejoin:
1293
1325
  throw new Error("rejoin not expected here");
1294
1326
  case ContainerMessageType.GC:
@@ -1300,7 +1332,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1300
1332
  // e.g. if an app rolled back its container version
1301
1333
  const compatBehavior = opContents.compatDetails?.behavior;
1302
1334
  if (!compatBehaviorAllowsMessageType(opContents.type, compatBehavior)) {
1303
- const error = DataProcessingError.create("Stashed runtime message of unknown type", "applyStashedOp", undefined /* sequencedMessage */, {
1335
+ const error = DataProcessingError.create("Stashed runtime message of unexpected type", "applyStashedOp", undefined /* sequencedMessage */, {
1304
1336
  messageDetails: JSON.stringify({
1305
1337
  type: opContents.type,
1306
1338
  compatBehavior,
@@ -1320,12 +1352,14 @@ export class ContainerRuntime extends TypedEventEmitter {
1320
1352
  this._loadIdCompressor === undefined) {
1321
1353
  this._loadIdCompressor = this.createIdCompressor()
1322
1354
  .then((compressor) => {
1323
- this._idCompressor = compressor;
1324
1355
  // Finalize any ranges we received while the compressor was turned off.
1325
- for (const range of this.pendingIdCompressorOps) {
1326
- this._idCompressor.finalizeCreationRange(range);
1327
- }
1356
+ const ops = this.pendingIdCompressorOps;
1328
1357
  this.pendingIdCompressorOps = [];
1358
+ for (const range of ops) {
1359
+ compressor.finalizeCreationRange(range);
1360
+ }
1361
+ assert(this.pendingIdCompressorOps.length === 0, 0x976 /* No new ops added */);
1362
+ this._idCompressor = compressor;
1329
1363
  })
1330
1364
  .catch((error) => {
1331
1365
  this.logger.sendErrorEvent({ eventName: "IdCompressorDelayedLoad" }, error);
@@ -1335,6 +1369,10 @@ export class ContainerRuntime extends TypedEventEmitter {
1335
1369
  return this._loadIdCompressor;
1336
1370
  }
1337
1371
  setConnectionState(connected, clientId) {
1372
+ // Validate we have consistent state
1373
+ const currentClientId = this._audience.getSelf()?.clientId;
1374
+ assert(clientId === currentClientId, 0x977 /* input clientId does not match Audience */);
1375
+ assert(this.clientId === currentClientId, 0x978 /* this.clientId does not match Audience */);
1338
1376
  if (connected && this.idCompressorMode === "delayed") {
1339
1377
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
1340
1378
  this.loadIdCompressor();
@@ -1416,9 +1454,10 @@ export class ContainerRuntime extends TypedEventEmitter {
1416
1454
  // We do not need to make a deep copy. Each layer will just replace message.contents itself,
1417
1455
  // but will not modify the contents object (likely it will replace it on the message).
1418
1456
  const messageCopy = { ...messageArg };
1457
+ const savedOp = messageCopy.metadata?.savedOp;
1419
1458
  for (const message of this.remoteMessageProcessor.process(messageCopy)) {
1420
- if (modernRuntimeMessage) {
1421
- this.processCore({
1459
+ const msg = modernRuntimeMessage
1460
+ ? {
1422
1461
  // Cast it since we expect it to be this based on modernRuntimeMessage computation above.
1423
1462
  // There is nothing really ensuring that anytime original message.type is Operation that
1424
1463
  // the result messages will be so. In the end modern bool being true only directs to
@@ -1426,12 +1465,16 @@ export class ContainerRuntime extends TypedEventEmitter {
1426
1465
  message: message,
1427
1466
  local,
1428
1467
  modernRuntimeMessage,
1429
- });
1430
- }
1431
- else {
1432
- // Unrecognized message will be ignored.
1433
- this.processCore({ message, local, modernRuntimeMessage });
1434
- }
1468
+ }
1469
+ : // Unrecognized message will be ignored.
1470
+ {
1471
+ message,
1472
+ local,
1473
+ modernRuntimeMessage,
1474
+ };
1475
+ msg.savedOp = savedOp;
1476
+ // ensure that we observe any re-entrancy, and if needed, rebase ops
1477
+ this.ensureNoDataModelChanges(() => this.processCore(msg));
1435
1478
  }
1436
1479
  }
1437
1480
  /**
@@ -1439,6 +1482,13 @@ export class ContainerRuntime extends TypedEventEmitter {
1439
1482
  */
1440
1483
  processCore(messageWithContext) {
1441
1484
  const { message, local } = messageWithContext;
1485
+ // Intercept to reduce minimum sequence number to the delta manager's minimum sequence number.
1486
+ // Sequence numbers are not guaranteed to follow any sort of order. Re-entrancy is one of those situations
1487
+ if (this.deltaManager.minimumSequenceNumber <
1488
+ messageWithContext.message.minimumSequenceNumber) {
1489
+ messageWithContext.message.minimumSequenceNumber =
1490
+ this.deltaManager.minimumSequenceNumber;
1491
+ }
1442
1492
  // Surround the actual processing of the operation with messages to the schedule manager indicating
1443
1493
  // the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
1444
1494
  // messages once a batch has been fully processed.
@@ -1496,9 +1546,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1496
1546
  // stashed ops flow. The compressor is stashed with these ops already processed.
1497
1547
  // That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
1498
1548
  // thus we need to process all the ops.
1499
- if (!(this.skipSavedCompressorOps &&
1500
- messageWithContext.message.metadata?.savedOp ===
1501
- true)) {
1549
+ if (!(this.skipSavedCompressorOps && messageWithContext.savedOp === true)) {
1502
1550
  const range = messageWithContext.message.contents;
1503
1551
  // Some other client turned on the id compressor. If we have not turned it on,
1504
1552
  // put it in a pending queue and delay finalization.
@@ -1507,6 +1555,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1507
1555
  this.pendingIdCompressorOps.push(range);
1508
1556
  }
1509
1557
  else {
1558
+ assert(this.pendingIdCompressorOps.length === 0, 0x979 /* there should be no pending ops! */);
1510
1559
  this._idCompressor.finalizeCreationRange(range);
1511
1560
  }
1512
1561
  }
@@ -1627,9 +1676,9 @@ export class ContainerRuntime extends TypedEventEmitter {
1627
1676
  let checkpoint;
1628
1677
  let result;
1629
1678
  if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
1630
- // Note: we are not touching this.pendingAttachBatch here, for two reasons:
1631
- // 1. It would not help, as we flush attach ops as they become available.
1632
- // 2. There is no way to undo process of data store creation.
1679
+ // Note: we are not touching any batches other than mainBatch here, for two reasons:
1680
+ // 1. It would not help, as other batches are flushed independently from main batch.
1681
+ // 2. There is no way to undo process of data store creation, blob creation, ID compressor ops, or other things tracked by other batches.
1633
1682
  checkpoint = this.outbox.checkpoint().mainBatch;
1634
1683
  }
1635
1684
  try {
@@ -2030,10 +2079,14 @@ export class ContainerRuntime extends TypedEventEmitter {
2030
2079
  // The summary number for this summary. This will be updated during the summary process, so get it now and
2031
2080
  // use it for all events logged during this summary.
2032
2081
  const summaryNumber = this.nextSummaryNumber;
2082
+ let summaryRefSeqNum;
2033
2083
  const summaryNumberLogger = createChildLogger({
2034
2084
  logger: summaryLogger,
2035
2085
  properties: {
2036
- all: { summaryNumber },
2086
+ all: {
2087
+ summaryNumber,
2088
+ referenceSequenceNumber: () => summaryRefSeqNum,
2089
+ },
2037
2090
  },
2038
2091
  });
2039
2092
  assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
@@ -2047,7 +2100,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2047
2100
  // If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
2048
2101
  // and it may even be incorrect. So, wait for the container to be saved with a timeout. If the container is not
2049
2102
  // saved within the timeout, check if it should be failed or can continue.
2050
- if (this.validateSummaryBeforeUpload && this.isDirty) {
2103
+ if (this.isDirty) {
2051
2104
  const countBefore = this.pendingMessagesCount;
2052
2105
  // The timeout for waiting for pending ops can be overridden via configurations.
2053
2106
  const pendingOpsTimeout = this.mc.config.getNumber("Fluid.Summarizer.waitForPendingOpsTimeoutMs") ??
@@ -2080,7 +2133,6 @@ export class ContainerRuntime extends TypedEventEmitter {
2080
2133
  }
2081
2134
  const shouldPauseInboundSignal = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.disableInboundSignalPause") !== true;
2082
2135
  const shouldValidatePreSummaryState = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.shouldValidatePreSummaryState") === true;
2083
- let summaryRefSeqNum;
2084
2136
  try {
2085
2137
  await this.deltaManager.inbound.pause();
2086
2138
  if (shouldPauseInboundSignal) {
@@ -2113,7 +2165,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2113
2165
  stage: "base",
2114
2166
  referenceSequenceNumber: summaryRefSeqNum,
2115
2167
  minimumSequenceNumber,
2116
- error: `Summarizer node state inconsistent with summarizer state.`,
2168
+ error: new LoggingError(`Summarizer node state inconsistent with summarizer state.`),
2117
2169
  };
2118
2170
  }
2119
2171
  }
@@ -2156,7 +2208,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2156
2208
  stage: "base",
2157
2209
  referenceSequenceNumber: summaryRefSeqNum,
2158
2210
  minimumSequenceNumber,
2159
- error: continueResult.error,
2211
+ error: new LoggingError(continueResult.error),
2160
2212
  };
2161
2213
  }
2162
2214
  const trace = Trace.start();
@@ -2173,6 +2225,18 @@ export class ContainerRuntime extends TypedEventEmitter {
2173
2225
  });
2174
2226
  }
2175
2227
  catch (error) {
2228
+ return {
2229
+ stage: "base",
2230
+ referenceSequenceNumber: summaryRefSeqNum,
2231
+ minimumSequenceNumber,
2232
+ error: wrapError(error, (msg) => new LoggingError(msg)),
2233
+ };
2234
+ }
2235
+ // Validate that the summary generated by summarizer nodes is correct before uploading.
2236
+ const validateResult = this.summarizerNode.validateSummary();
2237
+ if (!validateResult.success) {
2238
+ const { success, ...loggingProps } = validateResult;
2239
+ const error = new RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
2176
2240
  return {
2177
2241
  stage: "base",
2178
2242
  referenceSequenceNumber: summaryRefSeqNum,
@@ -2180,24 +2244,11 @@ export class ContainerRuntime extends TypedEventEmitter {
2180
2244
  error,
2181
2245
  };
2182
2246
  }
2183
- // If validateSummaryBeforeUpload is true, validate that the summary generated is correct before uploading.
2184
- if (this.validateSummaryBeforeUpload) {
2185
- // Validate that the summaries generated by summarize nodes is correct.
2186
- const validateResult = this.summarizerNode.validateSummary();
2187
- if (!validateResult.success) {
2188
- const { success, ...loggingProps } = validateResult;
2189
- const error = new RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
2190
- return {
2191
- stage: "base",
2192
- referenceSequenceNumber: summaryRefSeqNum,
2193
- minimumSequenceNumber,
2194
- error,
2195
- };
2196
- }
2197
- const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, summaryRefSeqNum, minimumSequenceNumber, finalAttempt, false /* beforeSummaryGeneration */);
2198
- if (pendingMessagesFailResult !== undefined) {
2199
- return pendingMessagesFailResult;
2200
- }
2247
+ // If there are pending unacked ops, this summary attempt may fail as the uploaded
2248
+ // summary would be eventually inconsistent.
2249
+ const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, summaryRefSeqNum, minimumSequenceNumber, finalAttempt, false /* beforeSummaryGeneration */);
2250
+ if (pendingMessagesFailResult !== undefined) {
2251
+ return pendingMessagesFailResult;
2201
2252
  }
2202
2253
  const { summary: summaryTree, stats: partialStats } = summarizeResult;
2203
2254
  // Now that we have generated the summary, update the message at last summary to the last message processed.
@@ -2230,7 +2281,11 @@ export class ContainerRuntime extends TypedEventEmitter {
2230
2281
  };
2231
2282
  continueResult = checkContinue();
2232
2283
  if (!continueResult.continue) {
2233
- return { stage: "generate", ...generateSummaryData, error: continueResult.error };
2284
+ return {
2285
+ stage: "generate",
2286
+ ...generateSummaryData,
2287
+ error: new LoggingError(continueResult.error),
2288
+ };
2234
2289
  }
2235
2290
  const summaryContext = lastAck === undefined
2236
2291
  ? {
@@ -2248,7 +2303,11 @@ export class ContainerRuntime extends TypedEventEmitter {
2248
2303
  handle = await this.storage.uploadSummaryWithContext(summarizeResult.summary, summaryContext);
2249
2304
  }
2250
2305
  catch (error) {
2251
- return { stage: "generate", ...generateSummaryData, error };
2306
+ return {
2307
+ stage: "generate",
2308
+ ...generateSummaryData,
2309
+ error: wrapError(error, (msg) => new LoggingError(msg)),
2310
+ };
2252
2311
  }
2253
2312
  const parent = summaryContext.ackHandle;
2254
2313
  const summaryMessage = {
@@ -2265,14 +2324,22 @@ export class ContainerRuntime extends TypedEventEmitter {
2265
2324
  };
2266
2325
  continueResult = checkContinue();
2267
2326
  if (!continueResult.continue) {
2268
- return { stage: "upload", ...uploadData, error: continueResult.error };
2327
+ return {
2328
+ stage: "upload",
2329
+ ...uploadData,
2330
+ error: new LoggingError(continueResult.error),
2331
+ };
2269
2332
  }
2270
2333
  let clientSequenceNumber;
2271
2334
  try {
2272
2335
  clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
2273
2336
  }
2274
2337
  catch (error) {
2275
- return { stage: "upload", ...uploadData, error };
2338
+ return {
2339
+ stage: "upload",
2340
+ ...uploadData,
2341
+ error: wrapError(error, (msg) => new LoggingError(msg)),
2342
+ };
2276
2343
  }
2277
2344
  const submitData = {
2278
2345
  stage: "submit",
@@ -2281,11 +2348,14 @@ export class ContainerRuntime extends TypedEventEmitter {
2281
2348
  submitOpDuration: trace.trace().duration,
2282
2349
  };
2283
2350
  try {
2284
- // If validateSummaryBeforeUpload is false, the summary should be validated in this step.
2285
- this.summarizerNode.completeSummary(handle, !this.validateSummaryBeforeUpload /* validate */);
2351
+ this.summarizerNode.completeSummary(handle);
2286
2352
  }
2287
2353
  catch (error) {
2288
- return { stage: "upload", ...uploadData, error };
2354
+ return {
2355
+ stage: "upload",
2356
+ ...uploadData,
2357
+ error: wrapError(error, (msg) => new LoggingError(msg)),
2358
+ };
2289
2359
  }
2290
2360
  return submitData;
2291
2361
  }
@@ -2393,7 +2463,6 @@ export class ContainerRuntime extends TypedEventEmitter {
2393
2463
  }
2394
2464
  submit(containerRuntimeMessage, localOpMetadata = undefined, metadata) {
2395
2465
  this.verifyNotClosed();
2396
- this.verifyCanSubmitOps();
2397
2466
  // There should be no ops in detached container state!
2398
2467
  assert(this.attachState !== AttachState.Detached, 0x132 /* "sending ops in detached container" */);
2399
2468
  assert(metadata === undefined ||
@@ -2429,6 +2498,14 @@ export class ContainerRuntime extends TypedEventEmitter {
2429
2498
  // on this callback to do actual sending.
2430
2499
  const contents = this.documentsSchemaController.maybeSendSchemaMessage();
2431
2500
  if (contents) {
2501
+ this.logger.sendTelemetryEvent({
2502
+ eventName: "SchemaChangeProposal",
2503
+ refSeq: contents.refSeq,
2504
+ version: contents.version,
2505
+ newRuntimeSchema: JSON.stringify(contents.runtime),
2506
+ sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
2507
+ oldRuntimeSchema: JSON.stringify(this.metadata?.documentSchema?.runtime),
2508
+ });
2432
2509
  const msg = {
2433
2510
  type: ContainerMessageType.DocumentSchemaChange,
2434
2511
  contents,
@@ -2438,32 +2515,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2438
2515
  referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
2439
2516
  });
2440
2517
  }
2441
- // If this is attach message for new data store, and we are in a batch, send this op out of order
2442
- // Is it safe:
2443
- // Yes, this should be safe reordering. Newly created data stores are not visible through API surface.
2444
- // They become visible only when aliased, or handle to some sub-element of newly created datastore
2445
- // is stored in some DDS, i.e. only after some other op.
2446
- // Why:
2447
- // Attach ops are large, and expensive to process. Plus there are scenarios where a lot of new data
2448
- // stores are created, causing issues like relay service throttling (too many ops) and catastrophic
2449
- // failure (batch is too large). Pushing them earlier and outside of main batch should alleviate
2450
- // these issues.
2451
- // Cons:
2452
- // 1. With large batches, relay service may throttle clients. Clients may disconnect while throttled.
2453
- // This change creates new possibility of a lot of newly created data stores never being referenced
2454
- // because client died before it had a change to submit the rest of the ops. This will create more
2455
- // garbage that needs to be collected leveraging GC (Garbage Collection) feature.
2456
- // 2. Sending ops out of order means they are excluded from rollback functionality. This is not an issue
2457
- // today as rollback can't undo creation of data store. To some extent not sending them is a bigger
2458
- // issue than sending.
2459
- // Please note that this does not change file format, so it can be disabled in the future if this
2460
- // optimization no longer makes sense (for example, batch compression may make it less appealing).
2461
- if (this.currentlyBatching() &&
2462
- type === ContainerMessageType.Attach &&
2463
- this.disableAttachReorder !== true) {
2464
- this.outbox.submitAttach(message);
2465
- }
2466
- else if (type === ContainerMessageType.BlobAttach) {
2518
+ if (type === ContainerMessageType.BlobAttach) {
2467
2519
  // BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
2468
2520
  this.outbox.submitBlobAttach(message);
2469
2521
  }
@@ -2538,32 +2590,6 @@ export class ContainerRuntime extends TypedEventEmitter {
2538
2590
  throw new Error("Runtime is closed");
2539
2591
  }
2540
2592
  }
2541
- verifyCanSubmitOps() {
2542
- if (this.ensureNoDataModelChangesCalls > 0) {
2543
- const errorMessage = "Op was submitted from within a `ensureNoDataModelChanges` callback";
2544
- if (this.opReentryCallsToReport > 0) {
2545
- this.mc.logger.sendTelemetryEvent({ eventName: "OpReentry" },
2546
- // We need to capture the call stack in order to inspect the source of this usage pattern
2547
- getLongStack(() => new UsageError(errorMessage)));
2548
- this.opReentryCallsToReport--;
2549
- }
2550
- // Creating ops while processing ops can lead
2551
- // to undefined behavior and events observed in the wrong order.
2552
- // For example, we have two callbacks registered for a DDS, A and B.
2553
- // Then if on change #1 callback A creates change #2, the invocation flow will be:
2554
- //
2555
- // A because of #1
2556
- // A because of #2
2557
- // B because of #2
2558
- // B because of #1
2559
- //
2560
- // The runtime must enforce op coherence by not allowing ops to be submitted
2561
- // while ops are being processed.
2562
- if (this.enableOpReentryCheck) {
2563
- throw new UsageError(errorMessage);
2564
- }
2565
- }
2566
- }
2567
2593
  reSubmitBatch(batch) {
2568
2594
  this.orderSequentially(() => {
2569
2595
  for (const message of batch) {
@@ -2598,8 +2624,6 @@ export class ContainerRuntime extends TypedEventEmitter {
2598
2624
  this.submit(message, localOpMetadata);
2599
2625
  break;
2600
2626
  }
2601
- case ContainerMessageType.ChunkedOp:
2602
- throw new Error(`chunkedOp not expected here`);
2603
2627
  case ContainerMessageType.BlobAttach:
2604
2628
  this.blobManager.reSubmit(opMetadata);
2605
2629
  break;
@@ -2626,7 +2650,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2626
2650
  });
2627
2651
  }
2628
2652
  else {
2629
- const error = DataProcessingError.create("Resubmitting runtime message of unknown type", "reSubmitCore", undefined /* sequencedMessage */, {
2653
+ const error = DataProcessingError.create("Resubmitting runtime message of unexpected type", "reSubmitCore", undefined /* sequencedMessage */, {
2630
2654
  messageDetails: JSON.stringify({
2631
2655
  type: message.type,
2632
2656
  compatBehavior,
@@ -2693,7 +2717,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2693
2717
  await this.closeStaleSummarizer();
2694
2718
  return {
2695
2719
  stage: "base",
2696
- error: "summary state stale - Unsupported option 'refreshLatestAck'",
2720
+ error: new LoggingError("summary state stale - Unsupported option 'refreshLatestAck'"),
2697
2721
  referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
2698
2722
  minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
2699
2723
  };
@@ -2737,16 +2761,14 @@ export class ContainerRuntime extends TypedEventEmitter {
2737
2761
  }
2738
2762
  this.imminentClosure || (this.imminentClosure = props?.notifyImminentClosure ?? false);
2739
2763
  const getSyncState = (pendingAttachmentBlobs) => {
2740
- const pending = this.pendingStateManager.getLocalState();
2741
- if (pendingAttachmentBlobs === undefined && !this.hasPendingMessages()) {
2742
- return; // no pending state to save
2743
- }
2764
+ const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
2765
+ const sessionExpiryTimerStarted = props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
2744
2766
  const pendingIdCompressorState = this._idCompressor?.serialize(true);
2745
2767
  return {
2746
2768
  pending,
2747
2769
  pendingIdCompressorState,
2748
2770
  pendingAttachmentBlobs,
2749
- sessionExpiryTimerStarted: this.garbageCollector.sessionExpiryTimerStarted,
2771
+ sessionExpiryTimerStarted,
2750
2772
  };
2751
2773
  };
2752
2774
  const perfEvent = {
@@ -2816,7 +2838,7 @@ export class ContainerRuntime extends TypedEventEmitter {
2816
2838
  }
2817
2839
  }
2818
2840
  get groupedBatchingEnabled() {
2819
- return this.documentSchema.opGroupingEnabled === true;
2841
+ return this.sessionSchema.opGroupingEnabled === true;
2820
2842
  }
2821
2843
  }
2822
2844
  //# sourceMappingURL=containerRuntime.js.map