@fluidframework/container-runtime 2.0.0-dev.2.3.0.115467 → 2.0.0-dev.4.1.0.148229

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 (563) hide show
  1. package/.eslintrc.js +21 -10
  2. package/.mocharc.js +2 -2
  3. package/api-extractor.json +2 -2
  4. package/dist/batchTracker.d.ts +1 -2
  5. package/dist/batchTracker.d.ts.map +1 -1
  6. package/dist/batchTracker.js +2 -1
  7. package/dist/batchTracker.js.map +1 -1
  8. package/dist/blobManager.d.ts +74 -42
  9. package/dist/blobManager.d.ts.map +1 -1
  10. package/dist/blobManager.js +321 -152
  11. package/dist/blobManager.js.map +1 -1
  12. package/dist/connectionTelemetry.d.ts.map +1 -1
  13. package/dist/connectionTelemetry.js +11 -9
  14. package/dist/connectionTelemetry.js.map +1 -1
  15. package/dist/containerHandleContext.d.ts.map +1 -1
  16. package/dist/containerHandleContext.js +3 -1
  17. package/dist/containerHandleContext.js.map +1 -1
  18. package/dist/containerRuntime.d.ts +148 -114
  19. package/dist/containerRuntime.d.ts.map +1 -1
  20. package/dist/containerRuntime.js +534 -342
  21. package/dist/containerRuntime.js.map +1 -1
  22. package/dist/dataStore.d.ts.map +1 -1
  23. package/dist/dataStore.js +11 -9
  24. package/dist/dataStore.js.map +1 -1
  25. package/dist/dataStoreContext.d.ts +40 -13
  26. package/dist/dataStoreContext.d.ts.map +1 -1
  27. package/dist/dataStoreContext.js +146 -66
  28. package/dist/dataStoreContext.js.map +1 -1
  29. package/dist/dataStoreContexts.d.ts.map +1 -1
  30. package/dist/dataStoreContexts.js +7 -3
  31. package/dist/dataStoreContexts.js.map +1 -1
  32. package/dist/dataStoreRegistry.d.ts.map +1 -1
  33. package/dist/dataStoreRegistry.js +3 -1
  34. package/dist/dataStoreRegistry.js.map +1 -1
  35. package/dist/dataStores.d.ts +39 -12
  36. package/dist/dataStores.d.ts.map +1 -1
  37. package/dist/dataStores.js +164 -76
  38. package/dist/dataStores.js.map +1 -1
  39. package/dist/deltaManagerSummarizerProxy.d.ts +19 -0
  40. package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -0
  41. package/dist/deltaManagerSummarizerProxy.js +40 -0
  42. package/dist/deltaManagerSummarizerProxy.js.map +1 -0
  43. package/dist/deltaScheduler.d.ts.map +1 -1
  44. package/dist/deltaScheduler.js +8 -3
  45. package/dist/deltaScheduler.js.map +1 -1
  46. package/dist/gc/garbageCollection.d.ts +204 -0
  47. package/dist/gc/garbageCollection.d.ts.map +1 -0
  48. package/dist/gc/garbageCollection.js +926 -0
  49. package/dist/gc/garbageCollection.js.map +1 -0
  50. package/dist/gc/gcConfigs.d.ts +22 -0
  51. package/dist/gc/gcConfigs.d.ts.map +1 -0
  52. package/dist/gc/gcConfigs.js +143 -0
  53. package/dist/gc/gcConfigs.js.map +1 -0
  54. package/dist/gc/gcDefinitions.d.ts +320 -0
  55. package/dist/gc/gcDefinitions.d.ts.map +1 -0
  56. package/dist/gc/gcDefinitions.js +81 -0
  57. package/dist/gc/gcDefinitions.js.map +1 -0
  58. package/dist/gc/gcHelpers.d.ts +86 -0
  59. package/dist/gc/gcHelpers.d.ts.map +1 -0
  60. package/dist/gc/gcHelpers.js +268 -0
  61. package/dist/gc/gcHelpers.js.map +1 -0
  62. package/dist/gc/gcReferenceGraphAlgorithm.d.ts +16 -0
  63. package/dist/gc/gcReferenceGraphAlgorithm.d.ts.map +1 -0
  64. package/dist/gc/gcReferenceGraphAlgorithm.js +49 -0
  65. package/dist/gc/gcReferenceGraphAlgorithm.js.map +1 -0
  66. package/dist/gc/gcSummaryDefinitions.d.ts +52 -0
  67. package/dist/gc/gcSummaryDefinitions.d.ts.map +1 -0
  68. package/dist/gc/gcSummaryDefinitions.js +7 -0
  69. package/dist/gc/gcSummaryDefinitions.js.map +1 -0
  70. package/dist/gc/gcSummaryStateTracker.d.ts +93 -0
  71. package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -0
  72. package/dist/gc/gcSummaryStateTracker.js +239 -0
  73. package/dist/gc/gcSummaryStateTracker.js.map +1 -0
  74. package/{lib → dist/gc}/gcSweepReadyUsageDetection.d.ts +5 -5
  75. package/dist/gc/gcSweepReadyUsageDetection.d.ts.map +1 -0
  76. package/dist/{gcSweepReadyUsageDetection.js → gc/gcSweepReadyUsageDetection.js} +15 -11
  77. package/dist/gc/gcSweepReadyUsageDetection.js.map +1 -0
  78. package/dist/gc/gcUnreferencedStateTracker.d.ts +34 -0
  79. package/dist/gc/gcUnreferencedStateTracker.d.ts.map +1 -0
  80. package/dist/gc/gcUnreferencedStateTracker.js +94 -0
  81. package/dist/gc/gcUnreferencedStateTracker.js.map +1 -0
  82. package/dist/gc/index.d.ts +13 -0
  83. package/dist/gc/index.d.ts.map +1 -0
  84. package/dist/gc/index.js +50 -0
  85. package/dist/gc/index.js.map +1 -0
  86. package/dist/index.d.ts +3 -8
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +7 -13
  89. package/dist/index.js.map +1 -1
  90. package/dist/opLifecycle/batchManager.d.ts +10 -0
  91. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  92. package/dist/opLifecycle/batchManager.js +37 -8
  93. package/dist/opLifecycle/batchManager.js.map +1 -1
  94. package/dist/opLifecycle/definitions.d.ts +29 -1
  95. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  96. package/dist/opLifecycle/definitions.js.map +1 -1
  97. package/dist/opLifecycle/index.d.ts +3 -3
  98. package/dist/opLifecycle/index.d.ts.map +1 -1
  99. package/dist/opLifecycle/index.js +3 -1
  100. package/dist/opLifecycle/index.js.map +1 -1
  101. package/dist/opLifecycle/opCompressor.d.ts +1 -1
  102. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  103. package/dist/opLifecycle/opCompressor.js +46 -17
  104. package/dist/opLifecycle/opCompressor.js.map +1 -1
  105. package/dist/opLifecycle/opDecompressor.d.ts +6 -1
  106. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  107. package/dist/opLifecycle/opDecompressor.js +72 -18
  108. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  109. package/dist/opLifecycle/opSplitter.d.ts +46 -2
  110. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  111. package/dist/opLifecycle/opSplitter.js +142 -5
  112. package/dist/opLifecycle/opSplitter.js.map +1 -1
  113. package/dist/opLifecycle/outbox.d.ts +23 -2
  114. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  115. package/dist/opLifecycle/outbox.js +101 -51
  116. package/dist/opLifecycle/outbox.js.map +1 -1
  117. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  118. package/dist/opLifecycle/remoteMessageProcessor.js +17 -2
  119. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  120. package/dist/opProperties.d.ts.map +1 -1
  121. package/dist/opProperties.js +1 -3
  122. package/dist/opProperties.js.map +1 -1
  123. package/dist/packageVersion.d.ts +1 -1
  124. package/dist/packageVersion.js +1 -1
  125. package/dist/packageVersion.js.map +1 -1
  126. package/dist/pendingStateManager.d.ts +6 -15
  127. package/dist/pendingStateManager.d.ts.map +1 -1
  128. package/dist/pendingStateManager.js +137 -165
  129. package/dist/pendingStateManager.js.map +1 -1
  130. package/dist/scheduleManager.d.ts +0 -1
  131. package/dist/scheduleManager.d.ts.map +1 -1
  132. package/dist/scheduleManager.js +11 -21
  133. package/dist/scheduleManager.js.map +1 -1
  134. package/dist/storageServiceWithAttachBlobs.d.ts +17 -0
  135. package/dist/storageServiceWithAttachBlobs.d.ts.map +1 -0
  136. package/dist/storageServiceWithAttachBlobs.js +32 -0
  137. package/dist/storageServiceWithAttachBlobs.js.map +1 -0
  138. package/dist/summary/index.d.ts +17 -0
  139. package/dist/summary/index.d.ts.map +1 -0
  140. package/dist/summary/index.js +46 -0
  141. package/dist/summary/index.js.map +1 -0
  142. package/dist/summary/orderedClientElection.d.ts.map +1 -0
  143. package/dist/{orderedClientElection.js → summary/orderedClientElection.js} +10 -4
  144. package/dist/summary/orderedClientElection.js.map +1 -0
  145. package/{lib → dist/summary}/runWhileConnectedCoordinator.d.ts +3 -2
  146. package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -0
  147. package/dist/{runWhileConnectedCoordinator.js → summary/runWhileConnectedCoordinator.js} +5 -4
  148. package/dist/summary/runWhileConnectedCoordinator.js.map +1 -0
  149. package/{lib → dist/summary}/runningSummarizer.d.ts +19 -18
  150. package/dist/summary/runningSummarizer.d.ts.map +1 -0
  151. package/dist/{runningSummarizer.js → summary/runningSummarizer.js} +191 -77
  152. package/dist/summary/runningSummarizer.js.map +1 -0
  153. package/dist/{summarizer.d.ts → summary/summarizer.d.ts} +6 -12
  154. package/dist/summary/summarizer.d.ts.map +1 -0
  155. package/dist/{summarizer.js → summary/summarizer.js} +32 -76
  156. package/dist/summary/summarizer.js.map +1 -0
  157. package/dist/{summarizerClientElection.d.ts → summary/summarizerClientElection.d.ts} +1 -2
  158. package/dist/summary/summarizerClientElection.d.ts.map +1 -0
  159. package/dist/{summarizerClientElection.js → summary/summarizerClientElection.js} +3 -30
  160. package/dist/summary/summarizerClientElection.js.map +1 -0
  161. package/dist/{summarizerHeuristics.d.ts → summary/summarizerHeuristics.d.ts} +1 -1
  162. package/dist/summary/summarizerHeuristics.d.ts.map +1 -0
  163. package/dist/{summarizerHeuristics.js → summary/summarizerHeuristics.js} +9 -12
  164. package/dist/summary/summarizerHeuristics.js.map +1 -0
  165. package/dist/summary/summarizerNode/index.d.ts +8 -0
  166. package/dist/summary/summarizerNode/index.d.ts.map +1 -0
  167. package/dist/summary/summarizerNode/index.js +12 -0
  168. package/dist/summary/summarizerNode/index.js.map +1 -0
  169. package/dist/summary/summarizerNode/summarizerNode.d.ts +149 -0
  170. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -0
  171. package/dist/summary/summarizerNode/summarizerNode.js +531 -0
  172. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -0
  173. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +125 -0
  174. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -0
  175. package/dist/summary/summarizerNode/summarizerNodeUtils.js +132 -0
  176. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -0
  177. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +22 -0
  178. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -0
  179. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +423 -0
  180. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -0
  181. package/{lib → dist/summary}/summarizerTypes.d.ts +29 -42
  182. package/dist/summary/summarizerTypes.d.ts.map +1 -0
  183. package/dist/{summarizerTypes.js → summary/summarizerTypes.js} +0 -5
  184. package/dist/summary/summarizerTypes.js.map +1 -0
  185. package/dist/summary/summaryCollection.d.ts.map +1 -0
  186. package/dist/{summaryCollection.js → summary/summaryCollection.js} +18 -8
  187. package/dist/summary/summaryCollection.js.map +1 -0
  188. package/{lib → dist/summary}/summaryFormat.d.ts +3 -21
  189. package/dist/summary/summaryFormat.d.ts.map +1 -0
  190. package/dist/{summaryFormat.js → summary/summaryFormat.js} +19 -21
  191. package/dist/summary/summaryFormat.js.map +1 -0
  192. package/dist/summary/summaryGenerator.d.ts.map +1 -0
  193. package/dist/{summaryGenerator.js → summary/summaryGenerator.js} +34 -16
  194. package/dist/summary/summaryGenerator.js.map +1 -0
  195. package/dist/{summaryManager.d.ts → summary/summaryManager.d.ts} +1 -1
  196. package/dist/summary/summaryManager.d.ts.map +1 -0
  197. package/dist/{summaryManager.js → summary/summaryManager.js} +21 -9
  198. package/dist/summary/summaryManager.js.map +1 -0
  199. package/dist/throttler.d.ts +2 -2
  200. package/dist/throttler.d.ts.map +1 -1
  201. package/dist/throttler.js +4 -4
  202. package/dist/throttler.js.map +1 -1
  203. package/lib/batchTracker.d.ts +1 -2
  204. package/lib/batchTracker.d.ts.map +1 -1
  205. package/lib/batchTracker.js +2 -1
  206. package/lib/batchTracker.js.map +1 -1
  207. package/lib/blobManager.d.ts +74 -42
  208. package/lib/blobManager.d.ts.map +1 -1
  209. package/lib/blobManager.js +322 -153
  210. package/lib/blobManager.js.map +1 -1
  211. package/lib/connectionTelemetry.d.ts.map +1 -1
  212. package/lib/connectionTelemetry.js +11 -9
  213. package/lib/connectionTelemetry.js.map +1 -1
  214. package/lib/containerHandleContext.d.ts.map +1 -1
  215. package/lib/containerHandleContext.js +3 -1
  216. package/lib/containerHandleContext.js.map +1 -1
  217. package/lib/containerRuntime.d.ts +148 -114
  218. package/lib/containerRuntime.d.ts.map +1 -1
  219. package/lib/containerRuntime.js +506 -314
  220. package/lib/containerRuntime.js.map +1 -1
  221. package/lib/dataStore.d.ts.map +1 -1
  222. package/lib/dataStore.js +11 -9
  223. package/lib/dataStore.js.map +1 -1
  224. package/lib/dataStoreContext.d.ts +40 -13
  225. package/lib/dataStoreContext.d.ts.map +1 -1
  226. package/lib/dataStoreContext.js +136 -56
  227. package/lib/dataStoreContext.js.map +1 -1
  228. package/lib/dataStoreContexts.d.ts.map +1 -1
  229. package/lib/dataStoreContexts.js +7 -3
  230. package/lib/dataStoreContexts.js.map +1 -1
  231. package/lib/dataStoreRegistry.d.ts.map +1 -1
  232. package/lib/dataStoreRegistry.js +3 -1
  233. package/lib/dataStoreRegistry.js.map +1 -1
  234. package/lib/dataStores.d.ts +39 -12
  235. package/lib/dataStores.d.ts.map +1 -1
  236. package/lib/dataStores.js +162 -74
  237. package/lib/dataStores.js.map +1 -1
  238. package/lib/deltaManagerSummarizerProxy.d.ts +19 -0
  239. package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -0
  240. package/lib/deltaManagerSummarizerProxy.js +36 -0
  241. package/lib/deltaManagerSummarizerProxy.js.map +1 -0
  242. package/lib/deltaScheduler.d.ts.map +1 -1
  243. package/lib/deltaScheduler.js +9 -4
  244. package/lib/deltaScheduler.js.map +1 -1
  245. package/lib/gc/garbageCollection.d.ts +204 -0
  246. package/lib/gc/garbageCollection.d.ts.map +1 -0
  247. package/lib/gc/garbageCollection.js +922 -0
  248. package/lib/gc/garbageCollection.js.map +1 -0
  249. package/lib/gc/gcConfigs.d.ts +22 -0
  250. package/lib/gc/gcConfigs.d.ts.map +1 -0
  251. package/lib/gc/gcConfigs.js +139 -0
  252. package/lib/gc/gcConfigs.js.map +1 -0
  253. package/lib/gc/gcDefinitions.d.ts +320 -0
  254. package/lib/gc/gcDefinitions.d.ts.map +1 -0
  255. package/lib/gc/gcDefinitions.js +78 -0
  256. package/lib/gc/gcDefinitions.js.map +1 -0
  257. package/lib/gc/gcHelpers.d.ts +86 -0
  258. package/lib/gc/gcHelpers.d.ts.map +1 -0
  259. package/lib/gc/gcHelpers.js +254 -0
  260. package/lib/gc/gcHelpers.js.map +1 -0
  261. package/lib/gc/gcReferenceGraphAlgorithm.d.ts +16 -0
  262. package/lib/gc/gcReferenceGraphAlgorithm.d.ts.map +1 -0
  263. package/lib/gc/gcReferenceGraphAlgorithm.js +45 -0
  264. package/lib/gc/gcReferenceGraphAlgorithm.js.map +1 -0
  265. package/lib/gc/gcSummaryDefinitions.d.ts +52 -0
  266. package/lib/gc/gcSummaryDefinitions.d.ts.map +1 -0
  267. package/lib/gc/gcSummaryDefinitions.js +6 -0
  268. package/lib/gc/gcSummaryDefinitions.js.map +1 -0
  269. package/lib/gc/gcSummaryStateTracker.d.ts +93 -0
  270. package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -0
  271. package/lib/gc/gcSummaryStateTracker.js +235 -0
  272. package/lib/gc/gcSummaryStateTracker.js.map +1 -0
  273. package/{dist → lib/gc}/gcSweepReadyUsageDetection.d.ts +5 -5
  274. package/lib/gc/gcSweepReadyUsageDetection.d.ts.map +1 -0
  275. package/lib/{gcSweepReadyUsageDetection.js → gc/gcSweepReadyUsageDetection.js} +15 -11
  276. package/lib/gc/gcSweepReadyUsageDetection.js.map +1 -0
  277. package/lib/gc/gcUnreferencedStateTracker.d.ts +34 -0
  278. package/lib/gc/gcUnreferencedStateTracker.d.ts.map +1 -0
  279. package/lib/gc/gcUnreferencedStateTracker.js +90 -0
  280. package/lib/gc/gcUnreferencedStateTracker.js.map +1 -0
  281. package/lib/gc/index.d.ts +13 -0
  282. package/lib/gc/index.d.ts.map +1 -0
  283. package/lib/gc/index.js +12 -0
  284. package/lib/gc/index.js.map +1 -0
  285. package/lib/index.d.ts +3 -8
  286. package/lib/index.d.ts.map +1 -1
  287. package/lib/index.js +2 -6
  288. package/lib/index.js.map +1 -1
  289. package/lib/opLifecycle/batchManager.d.ts +10 -0
  290. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  291. package/lib/opLifecycle/batchManager.js +35 -7
  292. package/lib/opLifecycle/batchManager.js.map +1 -1
  293. package/lib/opLifecycle/definitions.d.ts +29 -1
  294. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  295. package/lib/opLifecycle/definitions.js.map +1 -1
  296. package/lib/opLifecycle/index.d.ts +3 -3
  297. package/lib/opLifecycle/index.d.ts.map +1 -1
  298. package/lib/opLifecycle/index.js +2 -2
  299. package/lib/opLifecycle/index.js.map +1 -1
  300. package/lib/opLifecycle/opCompressor.d.ts +1 -1
  301. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  302. package/lib/opLifecycle/opCompressor.js +47 -18
  303. package/lib/opLifecycle/opCompressor.js.map +1 -1
  304. package/lib/opLifecycle/opDecompressor.d.ts +6 -1
  305. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  306. package/lib/opLifecycle/opDecompressor.js +72 -18
  307. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  308. package/lib/opLifecycle/opSplitter.d.ts +46 -2
  309. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  310. package/lib/opLifecycle/opSplitter.js +141 -5
  311. package/lib/opLifecycle/opSplitter.js.map +1 -1
  312. package/lib/opLifecycle/outbox.d.ts +23 -2
  313. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  314. package/lib/opLifecycle/outbox.js +103 -53
  315. package/lib/opLifecycle/outbox.js.map +1 -1
  316. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  317. package/lib/opLifecycle/remoteMessageProcessor.js +17 -2
  318. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  319. package/lib/opProperties.d.ts.map +1 -1
  320. package/lib/opProperties.js +1 -3
  321. package/lib/opProperties.js.map +1 -1
  322. package/lib/packageVersion.d.ts +1 -1
  323. package/lib/packageVersion.js +1 -1
  324. package/lib/packageVersion.js.map +1 -1
  325. package/lib/pendingStateManager.d.ts +6 -15
  326. package/lib/pendingStateManager.d.ts.map +1 -1
  327. package/lib/pendingStateManager.js +137 -165
  328. package/lib/pendingStateManager.js.map +1 -1
  329. package/lib/scheduleManager.d.ts +0 -1
  330. package/lib/scheduleManager.d.ts.map +1 -1
  331. package/lib/scheduleManager.js +11 -21
  332. package/lib/scheduleManager.js.map +1 -1
  333. package/lib/storageServiceWithAttachBlobs.d.ts +17 -0
  334. package/lib/storageServiceWithAttachBlobs.d.ts.map +1 -0
  335. package/lib/storageServiceWithAttachBlobs.js +28 -0
  336. package/lib/storageServiceWithAttachBlobs.js.map +1 -0
  337. package/lib/summary/index.d.ts +17 -0
  338. package/lib/summary/index.d.ts.map +1 -0
  339. package/lib/summary/index.js +15 -0
  340. package/lib/summary/index.js.map +1 -0
  341. package/lib/summary/orderedClientElection.d.ts.map +1 -0
  342. package/lib/{orderedClientElection.js → summary/orderedClientElection.js} +10 -4
  343. package/lib/summary/orderedClientElection.js.map +1 -0
  344. package/{dist → lib/summary}/runWhileConnectedCoordinator.d.ts +3 -2
  345. package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -0
  346. package/lib/{runWhileConnectedCoordinator.js → summary/runWhileConnectedCoordinator.js} +5 -4
  347. package/lib/summary/runWhileConnectedCoordinator.js.map +1 -0
  348. package/{dist → lib/summary}/runningSummarizer.d.ts +19 -18
  349. package/lib/summary/runningSummarizer.d.ts.map +1 -0
  350. package/lib/{runningSummarizer.js → summary/runningSummarizer.js} +193 -79
  351. package/lib/summary/runningSummarizer.js.map +1 -0
  352. package/lib/{summarizer.d.ts → summary/summarizer.d.ts} +6 -12
  353. package/lib/summary/summarizer.d.ts.map +1 -0
  354. package/lib/{summarizer.js → summary/summarizer.js} +34 -78
  355. package/lib/summary/summarizer.js.map +1 -0
  356. package/lib/{summarizerClientElection.d.ts → summary/summarizerClientElection.d.ts} +1 -2
  357. package/lib/summary/summarizerClientElection.d.ts.map +1 -0
  358. package/lib/{summarizerClientElection.js → summary/summarizerClientElection.js} +3 -30
  359. package/lib/summary/summarizerClientElection.js.map +1 -0
  360. package/lib/{summarizerHeuristics.d.ts → summary/summarizerHeuristics.d.ts} +1 -1
  361. package/lib/summary/summarizerHeuristics.d.ts.map +1 -0
  362. package/lib/{summarizerHeuristics.js → summary/summarizerHeuristics.js} +9 -12
  363. package/lib/summary/summarizerHeuristics.js.map +1 -0
  364. package/lib/summary/summarizerNode/index.d.ts +8 -0
  365. package/lib/summary/summarizerNode/index.d.ts.map +1 -0
  366. package/lib/summary/summarizerNode/index.js +7 -0
  367. package/lib/summary/summarizerNode/index.js.map +1 -0
  368. package/lib/summary/summarizerNode/summarizerNode.d.ts +149 -0
  369. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -0
  370. package/lib/summary/summarizerNode/summarizerNode.js +526 -0
  371. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -0
  372. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +125 -0
  373. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -0
  374. package/lib/summary/summarizerNode/summarizerNodeUtils.js +125 -0
  375. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -0
  376. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +22 -0
  377. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -0
  378. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +419 -0
  379. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -0
  380. package/{dist → lib/summary}/summarizerTypes.d.ts +29 -42
  381. package/lib/summary/summarizerTypes.d.ts.map +1 -0
  382. package/lib/summary/summarizerTypes.js +6 -0
  383. package/lib/summary/summarizerTypes.js.map +1 -0
  384. package/lib/summary/summaryCollection.d.ts.map +1 -0
  385. package/lib/{summaryCollection.js → summary/summaryCollection.js} +18 -8
  386. package/lib/summary/summaryCollection.js.map +1 -0
  387. package/{dist → lib/summary}/summaryFormat.d.ts +3 -21
  388. package/lib/summary/summaryFormat.d.ts.map +1 -0
  389. package/lib/{summaryFormat.js → summary/summaryFormat.js} +20 -21
  390. package/lib/summary/summaryFormat.js.map +1 -0
  391. package/lib/summary/summaryGenerator.d.ts.map +1 -0
  392. package/lib/{summaryGenerator.js → summary/summaryGenerator.js} +34 -16
  393. package/lib/summary/summaryGenerator.js.map +1 -0
  394. package/lib/{summaryManager.d.ts → summary/summaryManager.d.ts} +1 -1
  395. package/lib/summary/summaryManager.d.ts.map +1 -0
  396. package/lib/{summaryManager.js → summary/summaryManager.js} +21 -9
  397. package/lib/summary/summaryManager.js.map +1 -0
  398. package/lib/throttler.d.ts +2 -2
  399. package/lib/throttler.d.ts.map +1 -1
  400. package/lib/throttler.js +4 -4
  401. package/lib/throttler.js.map +1 -1
  402. package/package.json +67 -61
  403. package/prettier.config.cjs +1 -1
  404. package/src/batchTracker.ts +55 -50
  405. package/src/blobManager.ts +863 -594
  406. package/src/connectionTelemetry.ts +280 -249
  407. package/src/containerHandleContext.ts +27 -29
  408. package/src/containerRuntime.ts +3174 -2805
  409. package/src/dataStore.ts +172 -159
  410. package/src/dataStoreContext.ts +1141 -993
  411. package/src/dataStoreContexts.ts +178 -161
  412. package/src/dataStoreRegistry.ts +25 -20
  413. package/src/dataStores.ts +887 -716
  414. package/src/deltaManagerSummarizerProxy.ts +46 -0
  415. package/src/deltaScheduler.ts +158 -150
  416. package/{garbageCollection.md → src/gc/garbageCollection.md} +16 -3
  417. package/src/gc/garbageCollection.ts +1250 -0
  418. package/src/gc/gcConfigs.ts +193 -0
  419. package/src/gc/gcDefinitions.ts +387 -0
  420. package/src/gc/gcHelpers.ts +332 -0
  421. package/src/gc/gcReferenceGraphAlgorithm.ts +52 -0
  422. package/src/gc/gcSummaryDefinitions.ts +54 -0
  423. package/src/gc/gcSummaryStateTracker.ts +329 -0
  424. package/src/gc/gcSweepReadyUsageDetection.ts +145 -0
  425. package/src/gc/gcUnreferencedStateTracker.ts +114 -0
  426. package/src/gc/index.ts +65 -0
  427. package/src/index.ts +61 -75
  428. package/src/opLifecycle/README.md +157 -0
  429. package/src/opLifecycle/batchManager.ts +119 -86
  430. package/src/opLifecycle/definitions.ts +49 -19
  431. package/src/opLifecycle/index.ts +7 -6
  432. package/src/opLifecycle/opCompressor.ts +78 -40
  433. package/src/opLifecycle/opDecompressor.ts +148 -64
  434. package/src/opLifecycle/opSplitter.ts +269 -66
  435. package/src/opLifecycle/outbox.ts +268 -184
  436. package/src/opLifecycle/remoteMessageProcessor.ts +63 -47
  437. package/src/opProperties.ts +11 -9
  438. package/src/packageVersion.ts +1 -1
  439. package/src/pendingStateManager.ts +386 -381
  440. package/src/scheduleManager.ts +299 -280
  441. package/src/storageServiceWithAttachBlobs.ts +38 -0
  442. package/src/summary/index.ts +105 -0
  443. package/src/summary/orderedClientElection.ts +564 -0
  444. package/src/summary/runWhileConnectedCoordinator.ts +113 -0
  445. package/src/summary/runningSummarizer.ts +788 -0
  446. package/src/summary/summarizer.ts +372 -0
  447. package/src/summary/summarizerClientElection.ts +139 -0
  448. package/src/summary/summarizerHeuristics.ts +224 -0
  449. package/src/summary/summarizerNode/index.ts +12 -0
  450. package/src/summary/summarizerNode/summarizerNode.ts +766 -0
  451. package/src/summary/summarizerNode/summarizerNodeUtils.ts +214 -0
  452. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +644 -0
  453. package/src/summary/summarizerTypes.ts +507 -0
  454. package/src/summary/summaryCollection.ts +450 -0
  455. package/src/summary/summaryFormat.ts +228 -0
  456. package/src/summary/summaryGenerator.ts +505 -0
  457. package/src/summary/summaryManager.ts +423 -0
  458. package/src/throttler.ts +131 -122
  459. package/tsconfig.esnext.json +6 -6
  460. package/tsconfig.json +9 -13
  461. package/dist/garbageCollection.d.ts +0 -387
  462. package/dist/garbageCollection.d.ts.map +0 -1
  463. package/dist/garbageCollection.js +0 -1138
  464. package/dist/garbageCollection.js.map +0 -1
  465. package/dist/garbageCollectionConstants.d.ts +0 -19
  466. package/dist/garbageCollectionConstants.d.ts.map +0 -1
  467. package/dist/garbageCollectionConstants.js +0 -34
  468. package/dist/garbageCollectionConstants.js.map +0 -1
  469. package/dist/gcSweepReadyUsageDetection.d.ts.map +0 -1
  470. package/dist/gcSweepReadyUsageDetection.js.map +0 -1
  471. package/dist/orderedClientElection.d.ts.map +0 -1
  472. package/dist/orderedClientElection.js.map +0 -1
  473. package/dist/runWhileConnectedCoordinator.d.ts.map +0 -1
  474. package/dist/runWhileConnectedCoordinator.js.map +0 -1
  475. package/dist/runningSummarizer.d.ts.map +0 -1
  476. package/dist/runningSummarizer.js.map +0 -1
  477. package/dist/serializedSnapshotStorage.d.ts +0 -58
  478. package/dist/serializedSnapshotStorage.d.ts.map +0 -1
  479. package/dist/serializedSnapshotStorage.js +0 -108
  480. package/dist/serializedSnapshotStorage.js.map +0 -1
  481. package/dist/summarizer.d.ts.map +0 -1
  482. package/dist/summarizer.js.map +0 -1
  483. package/dist/summarizerClientElection.d.ts.map +0 -1
  484. package/dist/summarizerClientElection.js.map +0 -1
  485. package/dist/summarizerHandle.d.ts +0 -12
  486. package/dist/summarizerHandle.d.ts.map +0 -1
  487. package/dist/summarizerHandle.js +0 -22
  488. package/dist/summarizerHandle.js.map +0 -1
  489. package/dist/summarizerHeuristics.d.ts.map +0 -1
  490. package/dist/summarizerHeuristics.js.map +0 -1
  491. package/dist/summarizerTypes.d.ts.map +0 -1
  492. package/dist/summarizerTypes.js.map +0 -1
  493. package/dist/summaryCollection.d.ts.map +0 -1
  494. package/dist/summaryCollection.js.map +0 -1
  495. package/dist/summaryFormat.d.ts.map +0 -1
  496. package/dist/summaryFormat.js.map +0 -1
  497. package/dist/summaryGenerator.d.ts.map +0 -1
  498. package/dist/summaryGenerator.js.map +0 -1
  499. package/dist/summaryManager.d.ts.map +0 -1
  500. package/dist/summaryManager.js.map +0 -1
  501. package/lib/garbageCollection.d.ts +0 -387
  502. package/lib/garbageCollection.d.ts.map +0 -1
  503. package/lib/garbageCollection.js +0 -1133
  504. package/lib/garbageCollection.js.map +0 -1
  505. package/lib/garbageCollectionConstants.d.ts +0 -19
  506. package/lib/garbageCollectionConstants.d.ts.map +0 -1
  507. package/lib/garbageCollectionConstants.js +0 -31
  508. package/lib/garbageCollectionConstants.js.map +0 -1
  509. package/lib/gcSweepReadyUsageDetection.d.ts.map +0 -1
  510. package/lib/gcSweepReadyUsageDetection.js.map +0 -1
  511. package/lib/orderedClientElection.d.ts.map +0 -1
  512. package/lib/orderedClientElection.js.map +0 -1
  513. package/lib/runWhileConnectedCoordinator.d.ts.map +0 -1
  514. package/lib/runWhileConnectedCoordinator.js.map +0 -1
  515. package/lib/runningSummarizer.d.ts.map +0 -1
  516. package/lib/runningSummarizer.js.map +0 -1
  517. package/lib/serializedSnapshotStorage.d.ts +0 -58
  518. package/lib/serializedSnapshotStorage.d.ts.map +0 -1
  519. package/lib/serializedSnapshotStorage.js +0 -104
  520. package/lib/serializedSnapshotStorage.js.map +0 -1
  521. package/lib/summarizer.d.ts.map +0 -1
  522. package/lib/summarizer.js.map +0 -1
  523. package/lib/summarizerClientElection.d.ts.map +0 -1
  524. package/lib/summarizerClientElection.js.map +0 -1
  525. package/lib/summarizerHandle.d.ts +0 -12
  526. package/lib/summarizerHandle.d.ts.map +0 -1
  527. package/lib/summarizerHandle.js +0 -18
  528. package/lib/summarizerHandle.js.map +0 -1
  529. package/lib/summarizerHeuristics.d.ts.map +0 -1
  530. package/lib/summarizerHeuristics.js.map +0 -1
  531. package/lib/summarizerTypes.d.ts.map +0 -1
  532. package/lib/summarizerTypes.js +0 -9
  533. package/lib/summarizerTypes.js.map +0 -1
  534. package/lib/summaryCollection.d.ts.map +0 -1
  535. package/lib/summaryCollection.js.map +0 -1
  536. package/lib/summaryFormat.d.ts.map +0 -1
  537. package/lib/summaryFormat.js.map +0 -1
  538. package/lib/summaryGenerator.d.ts.map +0 -1
  539. package/lib/summaryGenerator.js.map +0 -1
  540. package/lib/summaryManager.d.ts.map +0 -1
  541. package/lib/summaryManager.js.map +0 -1
  542. package/src/garbageCollection.ts +0 -1646
  543. package/src/garbageCollectionConstants.ts +0 -35
  544. package/src/gcSweepReadyUsageDetection.ts +0 -139
  545. package/src/orderedClientElection.ts +0 -532
  546. package/src/runWhileConnectedCoordinator.ts +0 -106
  547. package/src/runningSummarizer.ts +0 -611
  548. package/src/serializedSnapshotStorage.ts +0 -146
  549. package/src/summarizer.ts +0 -421
  550. package/src/summarizerClientElection.ts +0 -161
  551. package/src/summarizerHandle.ts +0 -21
  552. package/src/summarizerHeuristics.ts +0 -222
  553. package/src/summarizerTypes.ts +0 -510
  554. package/src/summaryCollection.ts +0 -421
  555. package/src/summaryFormat.ts +0 -235
  556. package/src/summaryGenerator.ts +0 -446
  557. package/src/summaryManager.ts +0 -394
  558. /package/dist/{orderedClientElection.d.ts → summary/orderedClientElection.d.ts} +0 -0
  559. /package/dist/{summaryCollection.d.ts → summary/summaryCollection.d.ts} +0 -0
  560. /package/dist/{summaryGenerator.d.ts → summary/summaryGenerator.d.ts} +0 -0
  561. /package/lib/{orderedClientElection.d.ts → summary/orderedClientElection.d.ts} +0 -0
  562. /package/lib/{summaryCollection.d.ts → summary/summaryCollection.d.ts} +0 -0
  563. /package/lib/{summaryGenerator.d.ts → summary/summaryGenerator.d.ts} +0 -0
@@ -23,74 +23,92 @@ const packageVersion_1 = require("./packageVersion");
23
23
  */
24
24
  class PendingStateManager {
25
25
  constructor(stateHandler, initialLocalState) {
26
- var _a;
26
+ var _a, _b;
27
27
  this.stateHandler = stateHandler;
28
- this.pendingStates = new double_ended_queue_1.default();
28
+ this.pendingMessages = new double_ended_queue_1.default();
29
+ this.initialMessages = new double_ended_queue_1.default();
29
30
  this.disposeOnce = new common_utils_1.Lazy(() => {
30
- this.initialStates.clear();
31
- this.pendingStates.clear();
31
+ this.initialMessages.clear();
32
+ this.pendingMessages.clear();
32
33
  });
33
- // Maintains the count of messages that are currently unacked.
34
- this._pendingMessagesCount = 0;
35
34
  // Indicates whether we are processing a batch.
36
35
  this.isProcessingBatch = false;
37
36
  this.dispose = () => this.disposeOnce.value;
38
- this.initialStates = new double_ended_queue_1.default((_a = initialLocalState === null || initialLocalState === void 0 ? void 0 : initialLocalState.pendingStates) !== null && _a !== void 0 ? _a : []);
37
+ /**
38
+ * Convert old local state format to the new format
39
+ * The old format contained "flush" messages as the indicator of batch ends
40
+ * The new format instead uses batch metadata on the last message to indicate batch ends
41
+ * ! TODO: Remove this conversion in "2.0.0-internal.5.0.0" as version from "2.0.0-internal.4.0.0" will be new format
42
+ * AB#2496 tracks removal
43
+ */
44
+ if (initialLocalState === null || initialLocalState === void 0 ? void 0 : initialLocalState.pendingStates) {
45
+ const pendingStates = initialLocalState === null || initialLocalState === void 0 ? void 0 : initialLocalState.pendingStates;
46
+ let currentlyBatching = false;
47
+ for (let i = 0; i < pendingStates.length; i++) {
48
+ const initialState = pendingStates[i];
49
+ // Skip over "flush" messages
50
+ if (initialState.type === "message") {
51
+ if ((_a = initialState.opMetadata) === null || _a === void 0 ? void 0 : _a.batch) {
52
+ currentlyBatching = true;
53
+ }
54
+ else if (((_b = initialState.opMetadata) === null || _b === void 0 ? void 0 : _b.batch) === false) {
55
+ currentlyBatching = false;
56
+ }
57
+ else if (
58
+ // End of batch if we are currently batching and this is last message or next message is flush
59
+ currentlyBatching &&
60
+ (i === pendingStates.length - 1 || pendingStates[i + 1].type === "flush")) {
61
+ currentlyBatching = false;
62
+ initialState.opMetadata = Object.assign(Object.assign({}, initialState.opMetadata), { batch: false });
63
+ }
64
+ this.initialMessages.push(initialState);
65
+ }
66
+ }
67
+ }
39
68
  }
40
69
  get pendingMessagesCount() {
41
- return this._pendingMessagesCount;
70
+ return this.pendingMessages.length;
42
71
  }
43
72
  /**
44
- * Called to check if there are any pending messages in the pending state queue.
73
+ * Called to check if there are any pending messages in the pending message queue.
45
74
  * @returns A boolean indicating whether there are messages or not.
46
75
  */
47
76
  hasPendingMessages() {
48
- return this._pendingMessagesCount !== 0 || !this.initialStates.isEmpty();
77
+ return !this.pendingMessages.isEmpty() || !this.initialMessages.isEmpty();
49
78
  }
50
79
  getLocalState() {
51
- (0, common_utils_1.assert)(this.initialStates.isEmpty(), 0x2e9 /* "Must call getLocalState() after applying initial states" */);
52
- if (this.hasPendingMessages()) {
80
+ (0, common_utils_1.assert)(this.initialMessages.isEmpty(), 0x2e9 /* "Must call getLocalState() after applying initial states" */);
81
+ if (!this.pendingMessages.isEmpty()) {
53
82
  return {
54
- pendingStates: this.pendingStates.toArray().map(
55
83
  // delete localOpMetadata since it may not be serializable
56
84
  // and will be regenerated by applyStashedOp()
57
- (state) => state.type === "message" ? Object.assign(Object.assign({}, state), { localOpMetadata: undefined }) : state),
85
+ pendingStates: this.pendingMessages
86
+ .toArray()
87
+ .map((message) => (Object.assign(Object.assign({}, message), { localOpMetadata: undefined }))),
58
88
  };
59
89
  }
60
90
  }
61
- get disposed() { return this.disposeOnce.evaluated; }
91
+ get disposed() {
92
+ return this.disposeOnce.evaluated;
93
+ }
62
94
  /**
63
95
  * Called when a message is submitted locally. Adds the message and the associated details to the pending state
64
96
  * queue.
65
97
  * @param type - The container message type.
66
- * @param clientSequenceNumber - The clientSequenceNumber associated with the message.
67
98
  * @param content - The message content.
68
99
  * @param localOpMetadata - The local metadata associated with the message.
69
100
  */
70
- onSubmitMessage(type, clientSequenceNumber, referenceSequenceNumber, content, localOpMetadata, opMetadata) {
101
+ onSubmitMessage(type, referenceSequenceNumber, content, localOpMetadata, opMetadata) {
71
102
  const pendingMessage = {
72
103
  type: "message",
73
104
  messageType: type,
74
- clientSequenceNumber,
105
+ clientSequenceNumber: -1,
75
106
  referenceSequenceNumber,
76
107
  content,
77
108
  localOpMetadata,
78
109
  opMetadata,
79
110
  };
80
- this.pendingStates.push(pendingMessage);
81
- this._pendingMessagesCount++;
82
- }
83
- /**
84
- * Called when flush() is called on the ContainerRuntime to manually flush messages.
85
- */
86
- onFlush() {
87
- // If the previous state is not a message, flush is a no-op.
88
- const previousState = this.pendingStates.peekBack();
89
- if ((previousState === null || previousState === void 0 ? void 0 : previousState.type) !== "message") {
90
- return;
91
- }
92
- // An explicit flush is interesting and is tracked only if there are messages sent in TurnBased mode.
93
- this.pendingStates.push({ type: "flush" });
111
+ this.pendingMessages.push(pendingMessage);
94
112
  }
95
113
  /**
96
114
  * Applies stashed ops at their reference sequence number so they are ready to be ACKed or resubmitted
@@ -98,29 +116,23 @@ class PendingStateManager {
98
116
  */
99
117
  async applyStashedOpsAt(seqNum) {
100
118
  // apply stashed ops at sequence number
101
- while (!this.initialStates.isEmpty()) {
119
+ while (!this.initialMessages.isEmpty()) {
102
120
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
103
- const nextState = this.initialStates.peekFront();
104
- if (nextState.type === "message") {
105
- if (seqNum !== undefined) {
106
- if (nextState.referenceSequenceNumber > seqNum) {
107
- break; // nothing left to do at this sequence number
108
- }
109
- else if (nextState.referenceSequenceNumber < seqNum) {
110
- throw new Error("loaded from snapshot too recent to apply stashed ops");
111
- }
121
+ const nextMessage = this.initialMessages.peekFront();
122
+ if (seqNum !== undefined) {
123
+ if (nextMessage.referenceSequenceNumber > seqNum) {
124
+ break; // nothing left to do at this sequence number
125
+ }
126
+ if (nextMessage.referenceSequenceNumber < seqNum) {
127
+ throw new Error("loaded from snapshot too recent to apply stashed ops");
112
128
  }
113
- // applyStashedOp will cause the DDS to behave as if it has sent the op but not actually send it
114
- const localOpMetadata = await this.stateHandler.applyStashedOp(nextState.messageType, nextState.content);
115
- nextState.localOpMetadata = localOpMetadata;
116
129
  }
117
- // then we push onto pendingStates which will cause PendingStateManager to resubmit when we connect
130
+ // applyStashedOp will cause the DDS to behave as if it has sent the op but not actually send it
131
+ const localOpMetadata = await this.stateHandler.applyStashedOp(nextMessage.messageType, nextMessage.content);
132
+ nextMessage.localOpMetadata = localOpMetadata;
133
+ // then we push onto pendingMessages which will cause PendingStateManager to resubmit when we connect
118
134
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
119
- const firstPendingState = this.initialStates.shift();
120
- this.pendingStates.push(firstPendingState);
121
- if (firstPendingState.type === "message") {
122
- this._pendingMessagesCount++;
123
- }
135
+ this.pendingMessages.push(this.initialMessages.shift());
124
136
  }
125
137
  }
126
138
  /**
@@ -131,23 +143,28 @@ class PendingStateManager {
131
143
  processPendingLocalMessage(message) {
132
144
  // Pre-processing part - This may be the start of a batch.
133
145
  this.maybeProcessBatchBegin(message);
134
- // Get the next state from the pending queue and verify that it is of type "message".
135
- const pendingState = this.peekNextPendingState();
136
- (0, common_utils_1.assert)(pendingState.type === "message", 0x169 /* "No pending message found for this remote message" */);
137
- this.pendingStates.shift();
138
- // Processing part - Verify that there has been no data corruption.
139
- // The clientSequenceNumber of the incoming message must match that of the pending message.
140
- if (pendingState.clientSequenceNumber !== message.clientSequenceNumber) {
146
+ // Get the next message from the pending queue. Verify a message exists.
147
+ const pendingMessage = this.pendingMessages.peekFront();
148
+ (0, common_utils_1.assert)(pendingMessage !== undefined, 0x169 /* "No pending message found for this remote message" */);
149
+ this.pendingMessages.shift();
150
+ if (pendingMessage.messageType !== message.type) {
141
151
  // Close the container because this could indicate data corruption.
142
- const error = container_utils_1.DataProcessingError.create("pending local message clientSequenceNumber mismatch", "unexpectedAckReceived", message, { expectedClientSequenceNumber: pendingState.clientSequenceNumber });
143
- this.stateHandler.close(error);
152
+ this.stateHandler.close(container_utils_1.DataProcessingError.create("pending local message type mismatch", "unexpectedAckReceived", message, {
153
+ expectedMessageType: pendingMessage.messageType,
154
+ }));
155
+ return;
156
+ }
157
+ const pendingMessageContent = JSON.stringify(pendingMessage.content);
158
+ const messageContent = JSON.stringify(message.contents);
159
+ // Stringified content does not match
160
+ if (pendingMessageContent !== messageContent) {
161
+ // Close the container because this could indicate data corruption.
162
+ this.stateHandler.close(container_utils_1.DataProcessingError.create("pending local message content mismatch", "unexpectedAckReceived", message));
144
163
  return;
145
164
  }
146
- this._pendingMessagesCount--;
147
- (0, common_utils_1.assert)(this._pendingMessagesCount >= 0, 0x3d6 /* positive */);
148
165
  // Post-processing part - If we are processing a batch then this could be the last message in the batch.
149
166
  this.maybeProcessBatchEnd(message);
150
- return pendingState.localOpMetadata;
167
+ return pendingMessage.localOpMetadata;
151
168
  }
152
169
  /**
153
170
  * This message could be the first message in batch. If so, set batch state marking the beginning of a batch.
@@ -155,20 +172,6 @@ class PendingStateManager {
155
172
  */
156
173
  maybeProcessBatchBegin(message) {
157
174
  var _a;
158
- /**
159
- * We are checking if the next message is the start of a batch. It can happen in the following scenarios:
160
- *
161
- * 1. The FlushMode was set to TurnBased before this message was sent.
162
- *
163
- * 2. The FlushMode was already TurnBased and a flush was called before this message was sent. This essentially
164
- * means that the flush marked the end of a previous batch and beginning of a new batch.
165
- *
166
- * Keep reading pending states from the queue until we encounter a message. It's possible that the FlushMode was
167
- * updated a bunch of times without sending any messages.
168
- */
169
- while (this.peekNextPendingState().type !== "message") {
170
- this.pendingStates.shift();
171
- }
172
175
  // This message is the first in a batch if the "batch" property on the metadata is set to true
173
176
  if ((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.batch) {
174
177
  // We should not already be processing a batch and there should be no pending batch begin message.
@@ -187,48 +190,35 @@ class PendingStateManager {
187
190
  if (!this.isProcessingBatch) {
188
191
  return;
189
192
  }
190
- const nextPendingState = this.peekNextPendingState();
191
- if (nextPendingState.type === "message") {
192
- return;
193
- }
194
193
  // There should be a pending batch begin message.
195
194
  (0, common_utils_1.assert)(this.pendingBatchBeginMessage !== undefined, 0x16d /* "There is no pending batch begin message" */);
196
- // Get the batch begin metadata from the first message in the batch.
197
- const batchBeginMetadata = (_a = this.pendingBatchBeginMessage.metadata) === null || _a === void 0 ? void 0 : _a.batch;
198
- // There could be just a single message in the batch. If so, it should not have any batch metadata. If there
199
- // are multiple messages in the batch, verify that we got the correct batch begin and end metadata.
200
- if (this.pendingBatchBeginMessage === message) {
201
- (0, common_utils_1.assert)(batchBeginMetadata === undefined, 0x16e /* "Batch with single message should not have batch metadata" */);
202
- }
203
- else {
204
- // Get the batch metadata from the last message in the batch.
205
- const batchEndMetadata = (_b = message.metadata) === null || _b === void 0 ? void 0 : _b.batch;
206
- if (batchBeginMetadata !== true || batchEndMetadata !== false) {
207
- this.stateHandler.close(container_utils_1.DataProcessingError.create("Pending batch inconsistency", // Formerly known as asserts 0x16f and 0x170
208
- "processPendingLocalMessage", message, {
209
- runtimeVersion: packageVersion_1.pkgVersion,
210
- batchClientId: this.pendingBatchBeginMessage.clientId,
211
- clientId: this.stateHandler.clientId(),
212
- hasBatchStart: batchBeginMetadata === true,
213
- hasBatchEnd: batchEndMetadata === false,
214
- messageType: message.type,
215
- batchStartSequenceNumber: this.pendingBatchBeginMessage.clientSequenceNumber,
216
- pendingMessagesCount: this.pendingMessagesCount,
217
- nextPendingState: nextPendingState.type,
218
- }));
195
+ const batchEndMetadata = (_a = message.metadata) === null || _a === void 0 ? void 0 : _a.batch;
196
+ if (this.pendingMessages.isEmpty() || batchEndMetadata === false) {
197
+ // Get the batch begin metadata from the first message in the batch.
198
+ const batchBeginMetadata = (_b = this.pendingBatchBeginMessage.metadata) === null || _b === void 0 ? void 0 : _b.batch;
199
+ // There could be just a single message in the batch. If so, it should not have any batch metadata. If there
200
+ // are multiple messages in the batch, verify that we got the correct batch begin and end metadata.
201
+ if (this.pendingBatchBeginMessage === message) {
202
+ (0, common_utils_1.assert)(batchBeginMetadata === undefined, 0x16e /* "Batch with single message should not have batch metadata" */);
203
+ }
204
+ else {
205
+ if (batchBeginMetadata !== true || batchEndMetadata !== false) {
206
+ this.stateHandler.close(container_utils_1.DataProcessingError.create("Pending batch inconsistency", // Formerly known as asserts 0x16f and 0x170
207
+ "processPendingLocalMessage", message, {
208
+ runtimeVersion: packageVersion_1.pkgVersion,
209
+ batchClientId: this.pendingBatchBeginMessage.clientId,
210
+ clientId: this.stateHandler.clientId(),
211
+ hasBatchStart: batchBeginMetadata === true,
212
+ hasBatchEnd: batchEndMetadata === false,
213
+ messageType: message.type,
214
+ pendingMessagesCount: this.pendingMessagesCount,
215
+ }));
216
+ }
219
217
  }
218
+ // Clear the pending batch state now that we have processed the entire batch.
219
+ this.pendingBatchBeginMessage = undefined;
220
+ this.isProcessingBatch = false;
220
221
  }
221
- // Clear the pending batch state now that we have processed the entire batch.
222
- this.pendingBatchBeginMessage = undefined;
223
- this.isProcessingBatch = false;
224
- }
225
- /**
226
- * Returns the next pending state from the pending state queue.
227
- */
228
- peekNextPendingState() {
229
- const nextPendingState = this.pendingStates.peekFront();
230
- (0, common_utils_1.assert)(!!nextPendingState, 0x171 /* "No pending state found for the remote message" */);
231
- return nextPendingState;
232
222
  }
233
223
  /**
234
224
  * Called when the Container's connection state changes. If the Container gets connected, it replays all the pending
@@ -240,63 +230,45 @@ class PendingStateManager {
240
230
  // This assert suggests we are about to send same ops twice, which will result in data loss.
241
231
  (0, common_utils_1.assert)(this.clientId !== this.stateHandler.clientId(), 0x173 /* "replayPendingStates called twice for same clientId!" */);
242
232
  this.clientId = this.stateHandler.clientId();
243
- (0, common_utils_1.assert)(this.initialStates.isEmpty(), 0x174 /* "initial states should be empty before replaying pending" */);
244
- let pendingStatesCount = this.pendingStates.length;
245
- if (pendingStatesCount === 0) {
233
+ (0, common_utils_1.assert)(this.initialMessages.isEmpty(), 0x174 /* "initial states should be empty before replaying pending" */);
234
+ let pendingMessagesCount = this.pendingMessages.length;
235
+ if (pendingMessagesCount === 0) {
246
236
  return;
247
237
  }
248
- // Reset the pending message count because all these messages will be removed from the queue.
249
- this._pendingMessagesCount = 0;
250
- const messageBatchQueue = new double_ended_queue_1.default();
251
- // Process exactly `pendingStatesCount` items in the queue as it represents the number of states that were
238
+ // Process exactly `pendingMessagesCount` items in the queue as it represents the number of messages that were
252
239
  // pending when we connected. This is important because the `reSubmitFn` might add more items in the queue
253
240
  // which must not be replayed.
254
- while (pendingStatesCount > 0) {
241
+ while (pendingMessagesCount > 0) {
255
242
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
256
- const pendingState = this.pendingStates.shift();
257
- switch (pendingState.type) {
258
- case "message":
259
- (0, common_utils_1.assert)(((_a = pendingState.opMetadata) === null || _a === void 0 ? void 0 : _a.batch) !== false || messageBatchQueue.length > 0, 0x41b /* We cannot process batches in chunks */);
260
- /**
261
- * We want to ensure grouped messages get processed in a batch.
262
- * Note: It is not possible for the PendingStateManager to receive a partially acked batch. It will
263
- * either receive the whole batch ack or nothing at all.
264
- */
265
- if (messageBatchQueue.length > 0 || ((_b = pendingState.opMetadata) === null || _b === void 0 ? void 0 : _b.batch)) {
266
- messageBatchQueue.enqueue(pendingState);
243
+ let pendingMessage = this.pendingMessages.shift();
244
+ pendingMessagesCount--;
245
+ (0, common_utils_1.assert)(((_a = pendingMessage.opMetadata) === null || _a === void 0 ? void 0 : _a.batch) !== false, 0x41b /* We cannot process batches in chunks */);
246
+ /**
247
+ * We want to ensure grouped messages get processed in a batch.
248
+ * Note: It is not possible for the PendingStateManager to receive a partially acked batch. It will
249
+ * either receive the whole batch ack or nothing at all.
250
+ */
251
+ if ((_b = pendingMessage.opMetadata) === null || _b === void 0 ? void 0 : _b.batch) {
252
+ (0, common_utils_1.assert)(pendingMessagesCount > 0, 0x554 /* Last pending message cannot be a batch begin */);
253
+ this.stateHandler.orderSequentially(() => {
254
+ var _a, _b;
255
+ while (pendingMessagesCount >= 0) {
256
+ // check is >= because batch end may be last pending message
257
+ this.stateHandler.reSubmit(pendingMessage.messageType, pendingMessage.content, pendingMessage.localOpMetadata, pendingMessage.opMetadata);
258
+ if (((_a = pendingMessage.opMetadata) === null || _a === void 0 ? void 0 : _a.batch) === false) {
259
+ break;
260
+ }
261
+ (0, common_utils_1.assert)(pendingMessagesCount > 0, 0x555 /* No batch end found */);
262
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
263
+ pendingMessage = this.pendingMessages.shift();
264
+ pendingMessagesCount--;
265
+ (0, common_utils_1.assert)(((_b = pendingMessage.opMetadata) === null || _b === void 0 ? void 0 : _b.batch) !== true, 0x556 /* Batch start needs a corresponding batch end */);
267
266
  }
268
- else {
269
- this.stateHandler.reSubmit(pendingState.messageType, pendingState.content, pendingState.localOpMetadata, pendingState.opMetadata);
270
- }
271
- break;
272
- case "flush":
273
- /**
274
- * A "flush" call can indicate the end of a batch.
275
- * We can't rely on the "batch" property in the message metadata as it gets
276
- * updated elsewhere and it is not the same object instance that gets updated.
277
- */
278
- if (messageBatchQueue.length > 0) {
279
- this.stateHandler.orderSequentially(() => {
280
- while (messageBatchQueue.length > 0) {
281
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
282
- const message = messageBatchQueue.dequeue();
283
- this.stateHandler.reSubmit(message.messageType, message.content, message.localOpMetadata, message.opMetadata);
284
- }
285
- });
286
- }
287
- (0, common_utils_1.assert)(messageBatchQueue.length === 0, 0x41c /* cannot flush in the middle of a batch */);
288
- this.stateHandler.flush();
289
- break;
290
- default:
291
- break;
267
+ });
268
+ }
269
+ else {
270
+ this.stateHandler.reSubmit(pendingMessage.messageType, pendingMessage.content, pendingMessage.localOpMetadata, pendingMessage.opMetadata);
292
271
  }
293
- pendingStatesCount--;
294
- }
295
- // There are some cases where ops are stashed but not flushed. We need to ensure they are resubmitted
296
- while (messageBatchQueue.length > 0) {
297
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
298
- const message = messageBatchQueue.dequeue();
299
- this.stateHandler.reSubmit(message.messageType, message.content, message.localOpMetadata, message.opMetadata);
300
272
  }
301
273
  }
302
274
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pendingStateManager.js","sourceRoot":"","sources":["../src/pendingStateManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IDisposable } from \"@fluidframework/common-definitions\";\nimport { assert, Lazy } from \"@fluidframework/common-utils\";\nimport { ICriticalContainerError } from \"@fluidframework/container-definitions\";\nimport { DataProcessingError } from \"@fluidframework/container-utils\";\nimport {\n ISequencedDocumentMessage,\n} from \"@fluidframework/protocol-definitions\";\nimport Deque from \"double-ended-queue\";\nimport { ContainerMessageType } from \"./containerRuntime\";\nimport { pkgVersion } from \"./packageVersion\";\n\n/**\n * This represents a message that has been submitted and is added to the pending queue when `submit` is called on the\n * ContainerRuntime. This message has either not been ack'd by the server or has not been submitted to the server yet.\n */\nexport interface IPendingMessage {\n type: \"message\";\n messageType: ContainerMessageType;\n clientSequenceNumber: number;\n referenceSequenceNumber: number;\n content: any;\n localOpMetadata: unknown;\n opMetadata: Record<string, unknown> | undefined;\n}\n\n/**\n * This represents an explicit flush call and is added to the pending queue when flush is called on the ContainerRuntime\n * to flush pending messages.\n */\nexport interface IPendingFlush {\n type: \"flush\";\n}\n\nexport type IPendingState = IPendingMessage | IPendingFlush;\n\nexport interface IPendingLocalState {\n /**\n * list of pending states, including ops and batch information\n */\n pendingStates: IPendingState[];\n}\n\nexport interface IRuntimeStateHandler{\n connected(): boolean;\n clientId(): string | undefined;\n close(error?: ICriticalContainerError): void;\n applyStashedOp: (type: ContainerMessageType, content: ISequencedDocumentMessage) => Promise<unknown>;\n flush(): void;\n reSubmit(\n type: ContainerMessageType,\n content: any,\n localOpMetadata: unknown,\n opMetadata: Record<string, unknown> | undefined): void;\n rollback(\n type: ContainerMessageType,\n content: any,\n localOpMetadata: unknown): void;\n orderSequentially(callback: () => void): void;\n}\n\n/**\n * PendingStateManager is responsible for maintaining the messages that have not been sent or have not yet been\n * acknowledged by the server. It also maintains the batch information for both automatically and manually flushed\n * batches along with the messages.\n * When the Container reconnects, it replays the pending states, which includes manual flushing\n * of messages and triggering resubmission of unacked ops.\n *\n * It verifies that all the ops are acked, are received in the right order and batch information is correct.\n */\nexport class PendingStateManager implements IDisposable {\n private readonly pendingStates = new Deque<IPendingState>();\n private readonly initialStates: Deque<IPendingState>;\n private readonly disposeOnce = new Lazy<void>(() => {\n this.initialStates.clear();\n this.pendingStates.clear();\n });\n\n // Maintains the count of messages that are currently unacked.\n private _pendingMessagesCount: number = 0;\n public get pendingMessagesCount(): number {\n return this._pendingMessagesCount;\n }\n\n // Indicates whether we are processing a batch.\n private isProcessingBatch: boolean = false;\n\n // This stores the first message in the batch that we are processing. This is used to verify that we get\n // the correct batch metadata.\n private pendingBatchBeginMessage: ISequencedDocumentMessage | undefined;\n\n private clientId: string | undefined;\n\n /**\n * Called to check if there are any pending messages in the pending state queue.\n * @returns A boolean indicating whether there are messages or not.\n */\n public hasPendingMessages(): boolean {\n return this._pendingMessagesCount !== 0 || !this.initialStates.isEmpty();\n }\n\n public getLocalState(): IPendingLocalState | undefined {\n assert(this.initialStates.isEmpty(), 0x2e9 /* \"Must call getLocalState() after applying initial states\" */);\n if (this.hasPendingMessages()) {\n return {\n pendingStates: this.pendingStates.toArray().map(\n // delete localOpMetadata since it may not be serializable\n // and will be regenerated by applyStashedOp()\n (state) => state.type === \"message\" ? { ...state, localOpMetadata: undefined } : state),\n };\n }\n }\n\n constructor(\n private readonly stateHandler: IRuntimeStateHandler,\n initialLocalState: IPendingLocalState | undefined,\n ) {\n this.initialStates = new Deque<IPendingState>(initialLocalState?.pendingStates ?? []);\n }\n\n public get disposed() { return this.disposeOnce.evaluated; }\n public readonly dispose = () => this.disposeOnce.value;\n\n /**\n * Called when a message is submitted locally. Adds the message and the associated details to the pending state\n * queue.\n * @param type - The container message type.\n * @param clientSequenceNumber - The clientSequenceNumber associated with the message.\n * @param content - The message content.\n * @param localOpMetadata - The local metadata associated with the message.\n */\n public onSubmitMessage(\n type: ContainerMessageType,\n clientSequenceNumber: number,\n referenceSequenceNumber: number,\n content: any,\n localOpMetadata: unknown,\n opMetadata: Record<string, unknown> | undefined,\n ) {\n const pendingMessage: IPendingMessage = {\n type: \"message\",\n messageType: type,\n clientSequenceNumber,\n referenceSequenceNumber,\n content,\n localOpMetadata,\n opMetadata,\n };\n\n this.pendingStates.push(pendingMessage);\n\n this._pendingMessagesCount++;\n }\n\n /**\n * Called when flush() is called on the ContainerRuntime to manually flush messages.\n */\n public onFlush() {\n // If the previous state is not a message, flush is a no-op.\n const previousState = this.pendingStates.peekBack();\n if (previousState?.type !== \"message\") {\n return;\n }\n\n // An explicit flush is interesting and is tracked only if there are messages sent in TurnBased mode.\n this.pendingStates.push({ type: \"flush\" });\n }\n\n /**\n * Applies stashed ops at their reference sequence number so they are ready to be ACKed or resubmitted\n * @param seqNum - Sequence number at which to apply ops. Will apply all ops if seqNum is undefined.\n */\n public async applyStashedOpsAt(seqNum?: number) {\n // apply stashed ops at sequence number\n while (!this.initialStates.isEmpty()) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const nextState = this.initialStates.peekFront()!;\n if (nextState.type === \"message\") {\n if (seqNum !== undefined) {\n if (nextState.referenceSequenceNumber > seqNum) {\n break; // nothing left to do at this sequence number\n } else if (nextState.referenceSequenceNumber < seqNum) {\n throw new Error(\"loaded from snapshot too recent to apply stashed ops\");\n }\n }\n\n // applyStashedOp will cause the DDS to behave as if it has sent the op but not actually send it\n const localOpMetadata =\n await this.stateHandler.applyStashedOp(nextState.messageType, nextState.content);\n nextState.localOpMetadata = localOpMetadata;\n }\n\n // then we push onto pendingStates which will cause PendingStateManager to resubmit when we connect\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const firstPendingState = this.initialStates.shift()!;\n this.pendingStates.push(firstPendingState);\n if (firstPendingState.type === \"message\") {\n this._pendingMessagesCount++;\n }\n }\n }\n\n /**\n * Processes a local message once its ack'd by the server. It verifies that there was no data corruption and that\n * the batch information was preserved for batch messages.\n * @param message - The message that got ack'd and needs to be processed.\n */\n public processPendingLocalMessage(message: ISequencedDocumentMessage): unknown {\n // Pre-processing part - This may be the start of a batch.\n this.maybeProcessBatchBegin(message);\n\n // Get the next state from the pending queue and verify that it is of type \"message\".\n const pendingState = this.peekNextPendingState();\n assert(pendingState.type === \"message\", 0x169 /* \"No pending message found for this remote message\" */);\n this.pendingStates.shift();\n\n // Processing part - Verify that there has been no data corruption.\n // The clientSequenceNumber of the incoming message must match that of the pending message.\n if (pendingState.clientSequenceNumber !== message.clientSequenceNumber) {\n // Close the container because this could indicate data corruption.\n const error = DataProcessingError.create(\n \"pending local message clientSequenceNumber mismatch\",\n \"unexpectedAckReceived\",\n message,\n { expectedClientSequenceNumber: pendingState.clientSequenceNumber },\n );\n\n this.stateHandler.close(error);\n return;\n }\n\n this._pendingMessagesCount--;\n assert(this._pendingMessagesCount >= 0, 0x3d6 /* positive */);\n\n // Post-processing part - If we are processing a batch then this could be the last message in the batch.\n this.maybeProcessBatchEnd(message);\n\n return pendingState.localOpMetadata;\n }\n\n /**\n * This message could be the first message in batch. If so, set batch state marking the beginning of a batch.\n * @param message - The message that is being processed.\n */\n private maybeProcessBatchBegin(message: ISequencedDocumentMessage) {\n /**\n * We are checking if the next message is the start of a batch. It can happen in the following scenarios:\n *\n * 1. The FlushMode was set to TurnBased before this message was sent.\n *\n * 2. The FlushMode was already TurnBased and a flush was called before this message was sent. This essentially\n * means that the flush marked the end of a previous batch and beginning of a new batch.\n *\n * Keep reading pending states from the queue until we encounter a message. It's possible that the FlushMode was\n * updated a bunch of times without sending any messages.\n */\n while (this.peekNextPendingState().type !== \"message\") {\n this.pendingStates.shift();\n }\n\n // This message is the first in a batch if the \"batch\" property on the metadata is set to true\n if (message.metadata?.batch) {\n // We should not already be processing a batch and there should be no pending batch begin message.\n assert(!this.isProcessingBatch && this.pendingBatchBeginMessage === undefined,\n 0x16b /* \"The pending batch state indicates we are already processing a batch\" */);\n\n // Set the pending batch state indicating we have started processing a batch.\n this.pendingBatchBeginMessage = message;\n this.isProcessingBatch = true;\n }\n }\n\n /**\n * This message could be the last message in batch. If so, clear batch state since the batch is complete.\n * @param message - The message that is being processed.\n */\n private maybeProcessBatchEnd(message: ISequencedDocumentMessage) {\n if (!this.isProcessingBatch) {\n return;\n }\n\n const nextPendingState = this.peekNextPendingState();\n if (nextPendingState.type === \"message\") {\n return;\n }\n\n // There should be a pending batch begin message.\n assert(this.pendingBatchBeginMessage !== undefined, 0x16d /* \"There is no pending batch begin message\" */);\n\n // Get the batch begin metadata from the first message in the batch.\n const batchBeginMetadata = this.pendingBatchBeginMessage.metadata?.batch;\n\n // There could be just a single message in the batch. If so, it should not have any batch metadata. If there\n // are multiple messages in the batch, verify that we got the correct batch begin and end metadata.\n if (this.pendingBatchBeginMessage === message) {\n assert(batchBeginMetadata === undefined,\n 0x16e /* \"Batch with single message should not have batch metadata\" */);\n } else {\n // Get the batch metadata from the last message in the batch.\n const batchEndMetadata = message.metadata?.batch;\n if (batchBeginMetadata !== true || batchEndMetadata !== false) {\n this.stateHandler.close(DataProcessingError.create(\n \"Pending batch inconsistency\", // Formerly known as asserts 0x16f and 0x170\n \"processPendingLocalMessage\",\n message,\n {\n runtimeVersion: pkgVersion,\n batchClientId: this.pendingBatchBeginMessage.clientId,\n clientId: this.stateHandler.clientId(),\n hasBatchStart: batchBeginMetadata === true,\n hasBatchEnd: batchEndMetadata === false,\n messageType: message.type,\n batchStartSequenceNumber: this.pendingBatchBeginMessage.clientSequenceNumber,\n pendingMessagesCount: this.pendingMessagesCount,\n nextPendingState: nextPendingState.type,\n }));\n }\n }\n\n // Clear the pending batch state now that we have processed the entire batch.\n this.pendingBatchBeginMessage = undefined;\n this.isProcessingBatch = false;\n }\n\n /**\n * Returns the next pending state from the pending state queue.\n */\n private peekNextPendingState(): IPendingState {\n const nextPendingState = this.pendingStates.peekFront();\n assert(!!nextPendingState, 0x171 /* \"No pending state found for the remote message\" */);\n return nextPendingState;\n }\n\n /**\n * Called when the Container's connection state changes. If the Container gets connected, it replays all the pending\n * states in its queue. This includes triggering resubmission of unacked ops.\n */\n public replayPendingStates() {\n assert(this.stateHandler.connected(), 0x172 /* \"The connection state is not consistent with the runtime\" */);\n\n // This assert suggests we are about to send same ops twice, which will result in data loss.\n assert(this.clientId !== this.stateHandler.clientId(),\n 0x173 /* \"replayPendingStates called twice for same clientId!\" */);\n this.clientId = this.stateHandler.clientId();\n\n assert(this.initialStates.isEmpty(), 0x174 /* \"initial states should be empty before replaying pending\" */);\n\n let pendingStatesCount = this.pendingStates.length;\n if (pendingStatesCount === 0) {\n return;\n }\n\n // Reset the pending message count because all these messages will be removed from the queue.\n this._pendingMessagesCount = 0;\n\n const messageBatchQueue = new Deque<IPendingMessage>();\n\n // Process exactly `pendingStatesCount` items in the queue as it represents the number of states that were\n // pending when we connected. This is important because the `reSubmitFn` might add more items in the queue\n // which must not be replayed.\n while (pendingStatesCount > 0) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const pendingState = this.pendingStates.shift()!;\n switch (pendingState.type) {\n case \"message\":\n assert(pendingState.opMetadata?.batch !== false || messageBatchQueue.length > 0,\n 0x41b /* We cannot process batches in chunks */);\n /**\n * We want to ensure grouped messages get processed in a batch.\n * Note: It is not possible for the PendingStateManager to receive a partially acked batch. It will\n * either receive the whole batch ack or nothing at all.\n */\n if (messageBatchQueue.length > 0 || pendingState.opMetadata?.batch) {\n messageBatchQueue.enqueue(pendingState);\n } else {\n this.stateHandler.reSubmit(\n pendingState.messageType,\n pendingState.content,\n pendingState.localOpMetadata,\n pendingState.opMetadata);\n }\n break;\n case \"flush\":\n /**\n * A \"flush\" call can indicate the end of a batch.\n * We can't rely on the \"batch\" property in the message metadata as it gets\n * updated elsewhere and it is not the same object instance that gets updated.\n */\n if (messageBatchQueue.length > 0) {\n this.stateHandler.orderSequentially(() => {\n while (messageBatchQueue.length > 0) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const message = messageBatchQueue.dequeue()!;\n this.stateHandler.reSubmit(\n message.messageType,\n message.content,\n message.localOpMetadata,\n message.opMetadata);\n }\n });\n }\n assert(messageBatchQueue.length === 0, 0x41c /* cannot flush in the middle of a batch */);\n this.stateHandler.flush();\n break;\n default:\n break;\n }\n pendingStatesCount--;\n }\n\n // There are some cases where ops are stashed but not flushed. We need to ensure they are resubmitted\n while (messageBatchQueue.length > 0) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const message = messageBatchQueue.dequeue()!;\n this.stateHandler.reSubmit(\n message.messageType,\n message.content,\n message.localOpMetadata,\n message.opMetadata);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"pendingStateManager.js","sourceRoot":"","sources":["../src/pendingStateManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IDisposable } from \"@fluidframework/common-definitions\";\nimport { assert, Lazy } from \"@fluidframework/common-utils\";\nimport { ICriticalContainerError } from \"@fluidframework/container-definitions\";\nimport { DataProcessingError } from \"@fluidframework/container-utils\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport Deque from \"double-ended-queue\";\nimport { ContainerMessageType } from \"./containerRuntime\";\nimport { pkgVersion } from \"./packageVersion\";\n\n/**\n * This represents a message that has been submitted and is added to the pending queue when `submit` is called on the\n * ContainerRuntime. This message has either not been ack'd by the server or has not been submitted to the server yet.\n */\nexport interface IPendingMessage {\n\ttype: \"message\";\n\tmessageType: ContainerMessageType;\n\tclientSequenceNumber: number;\n\treferenceSequenceNumber: number;\n\tcontent: any;\n\tlocalOpMetadata: unknown;\n\topMetadata: Record<string, unknown> | undefined;\n}\n\n/**\n * This represents an explicit flush call and is added to the pending queue when flush is called on the ContainerRuntime\n * to flush pending messages.\n * ! TODO: Remove in \"2.0.0-internal.5.0.0\" AB#2496\n */\nexport interface IPendingFlush {\n\ttype: \"flush\";\n}\n\n/** ! TODO: Remove in \"2.0.0-internal.5.0.0\" AB#2496 */\nexport type IPendingState = IPendingMessage | IPendingFlush;\n\nexport interface IPendingLocalState {\n\t/**\n\t * list of pending states, including ops and batch information\n\t */\n\tpendingStates: IPendingState[];\n}\n\nexport interface IRuntimeStateHandler {\n\tconnected(): boolean;\n\tclientId(): string | undefined;\n\tclose(error?: ICriticalContainerError): void;\n\tapplyStashedOp: (\n\t\ttype: ContainerMessageType,\n\t\tcontent: ISequencedDocumentMessage,\n\t) => Promise<unknown>;\n\treSubmit(\n\t\ttype: ContainerMessageType,\n\t\tcontent: any,\n\t\tlocalOpMetadata: unknown,\n\t\topMetadata: Record<string, unknown> | undefined,\n\t): void;\n\trollback(type: ContainerMessageType, content: any, localOpMetadata: unknown): void;\n\torderSequentially(callback: () => void): void;\n}\n\n/**\n * PendingStateManager is responsible for maintaining the messages that have not been sent or have not yet been\n * acknowledged by the server. It also maintains the batch information for both automatically and manually flushed\n * batches along with the messages.\n * When the Container reconnects, it replays the pending states, which includes manual flushing\n * of messages and triggering resubmission of unacked ops.\n *\n * It verifies that all the ops are acked, are received in the right order and batch information is correct.\n */\nexport class PendingStateManager implements IDisposable {\n\tprivate readonly pendingMessages = new Deque<IPendingMessage>();\n\tprivate readonly initialMessages = new Deque<IPendingMessage>();\n\tprivate readonly disposeOnce = new Lazy<void>(() => {\n\t\tthis.initialMessages.clear();\n\t\tthis.pendingMessages.clear();\n\t});\n\n\tpublic get pendingMessagesCount(): number {\n\t\treturn this.pendingMessages.length;\n\t}\n\n\t// Indicates whether we are processing a batch.\n\tprivate isProcessingBatch: boolean = false;\n\n\t// This stores the first message in the batch that we are processing. This is used to verify that we get\n\t// the correct batch metadata.\n\tprivate pendingBatchBeginMessage: ISequencedDocumentMessage | undefined;\n\n\tprivate clientId: string | undefined;\n\n\t/**\n\t * Called to check if there are any pending messages in the pending message queue.\n\t * @returns A boolean indicating whether there are messages or not.\n\t */\n\tpublic hasPendingMessages(): boolean {\n\t\treturn !this.pendingMessages.isEmpty() || !this.initialMessages.isEmpty();\n\t}\n\n\tpublic getLocalState(): IPendingLocalState | undefined {\n\t\tassert(\n\t\t\tthis.initialMessages.isEmpty(),\n\t\t\t0x2e9 /* \"Must call getLocalState() after applying initial states\" */,\n\t\t);\n\t\tif (!this.pendingMessages.isEmpty()) {\n\t\t\treturn {\n\t\t\t\t// delete localOpMetadata since it may not be serializable\n\t\t\t\t// and will be regenerated by applyStashedOp()\n\t\t\t\tpendingStates: this.pendingMessages\n\t\t\t\t\t.toArray()\n\t\t\t\t\t.map((message) => ({ ...message, localOpMetadata: undefined })),\n\t\t\t};\n\t\t}\n\t}\n\n\tconstructor(\n\t\tprivate readonly stateHandler: IRuntimeStateHandler,\n\t\tinitialLocalState: IPendingLocalState | undefined,\n\t) {\n\t\t/**\n\t\t * Convert old local state format to the new format\n\t\t * The old format contained \"flush\" messages as the indicator of batch ends\n\t\t * The new format instead uses batch metadata on the last message to indicate batch ends\n\t\t * ! TODO: Remove this conversion in \"2.0.0-internal.5.0.0\" as version from \"2.0.0-internal.4.0.0\" will be new format\n\t\t * AB#2496 tracks removal\n\t\t */\n\t\tif (initialLocalState?.pendingStates) {\n\t\t\tconst pendingStates = initialLocalState?.pendingStates;\n\t\t\tlet currentlyBatching = false;\n\t\t\tfor (let i = 0; i < pendingStates.length; i++) {\n\t\t\t\tconst initialState = pendingStates[i];\n\n\t\t\t\t// Skip over \"flush\" messages\n\t\t\t\tif (initialState.type === \"message\") {\n\t\t\t\t\tif (initialState.opMetadata?.batch) {\n\t\t\t\t\t\tcurrentlyBatching = true;\n\t\t\t\t\t} else if (initialState.opMetadata?.batch === false) {\n\t\t\t\t\t\tcurrentlyBatching = false;\n\t\t\t\t\t} else if (\n\t\t\t\t\t\t// End of batch if we are currently batching and this is last message or next message is flush\n\t\t\t\t\t\tcurrentlyBatching &&\n\t\t\t\t\t\t(i === pendingStates.length - 1 || pendingStates[i + 1].type === \"flush\")\n\t\t\t\t\t) {\n\t\t\t\t\t\tcurrentlyBatching = false;\n\t\t\t\t\t\tinitialState.opMetadata = { ...initialState.opMetadata, batch: false };\n\t\t\t\t\t}\n\t\t\t\t\tthis.initialMessages.push(initialState);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic get disposed() {\n\t\treturn this.disposeOnce.evaluated;\n\t}\n\tpublic readonly dispose = () => this.disposeOnce.value;\n\n\t/**\n\t * Called when a message is submitted locally. Adds the message and the associated details to the pending state\n\t * queue.\n\t * @param type - The container message type.\n\t * @param content - The message content.\n\t * @param localOpMetadata - The local metadata associated with the message.\n\t */\n\tpublic onSubmitMessage(\n\t\ttype: ContainerMessageType,\n\t\treferenceSequenceNumber: number,\n\t\tcontent: any,\n\t\tlocalOpMetadata: unknown,\n\t\topMetadata: Record<string, unknown> | undefined,\n\t) {\n\t\tconst pendingMessage: IPendingMessage = {\n\t\t\ttype: \"message\",\n\t\t\tmessageType: type,\n\t\t\tclientSequenceNumber: -1, // dummy value (not to be used anywhere)\n\t\t\treferenceSequenceNumber,\n\t\t\tcontent,\n\t\t\tlocalOpMetadata,\n\t\t\topMetadata,\n\t\t};\n\n\t\tthis.pendingMessages.push(pendingMessage);\n\t}\n\n\t/**\n\t * Applies stashed ops at their reference sequence number so they are ready to be ACKed or resubmitted\n\t * @param seqNum - Sequence number at which to apply ops. Will apply all ops if seqNum is undefined.\n\t */\n\tpublic async applyStashedOpsAt(seqNum?: number) {\n\t\t// apply stashed ops at sequence number\n\t\twhile (!this.initialMessages.isEmpty()) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tconst nextMessage = this.initialMessages.peekFront()!;\n\t\t\tif (seqNum !== undefined) {\n\t\t\t\tif (nextMessage.referenceSequenceNumber > seqNum) {\n\t\t\t\t\tbreak; // nothing left to do at this sequence number\n\t\t\t\t}\n\t\t\t\tif (nextMessage.referenceSequenceNumber < seqNum) {\n\t\t\t\t\tthrow new Error(\"loaded from snapshot too recent to apply stashed ops\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// applyStashedOp will cause the DDS to behave as if it has sent the op but not actually send it\n\t\t\tconst localOpMetadata = await this.stateHandler.applyStashedOp(\n\t\t\t\tnextMessage.messageType,\n\t\t\t\tnextMessage.content,\n\t\t\t);\n\t\t\tnextMessage.localOpMetadata = localOpMetadata;\n\n\t\t\t// then we push onto pendingMessages which will cause PendingStateManager to resubmit when we connect\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tthis.pendingMessages.push(this.initialMessages.shift()!);\n\t\t}\n\t}\n\n\t/**\n\t * Processes a local message once its ack'd by the server. It verifies that there was no data corruption and that\n\t * the batch information was preserved for batch messages.\n\t * @param message - The message that got ack'd and needs to be processed.\n\t */\n\tpublic processPendingLocalMessage(message: ISequencedDocumentMessage): unknown {\n\t\t// Pre-processing part - This may be the start of a batch.\n\t\tthis.maybeProcessBatchBegin(message);\n\n\t\t// Get the next message from the pending queue. Verify a message exists.\n\t\tconst pendingMessage = this.pendingMessages.peekFront();\n\t\tassert(\n\t\t\tpendingMessage !== undefined,\n\t\t\t0x169 /* \"No pending message found for this remote message\" */,\n\t\t);\n\t\tthis.pendingMessages.shift();\n\n\t\tif (pendingMessage.messageType !== message.type) {\n\t\t\t// Close the container because this could indicate data corruption.\n\t\t\tthis.stateHandler.close(\n\t\t\t\tDataProcessingError.create(\n\t\t\t\t\t\"pending local message type mismatch\",\n\t\t\t\t\t\"unexpectedAckReceived\",\n\t\t\t\t\tmessage,\n\t\t\t\t\t{\n\t\t\t\t\t\texpectedMessageType: pendingMessage.messageType,\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst pendingMessageContent = JSON.stringify(pendingMessage.content);\n\t\tconst messageContent = JSON.stringify(message.contents);\n\n\t\t// Stringified content does not match\n\t\tif (pendingMessageContent !== messageContent) {\n\t\t\t// Close the container because this could indicate data corruption.\n\t\t\tthis.stateHandler.close(\n\t\t\t\tDataProcessingError.create(\n\t\t\t\t\t\"pending local message content mismatch\",\n\t\t\t\t\t\"unexpectedAckReceived\",\n\t\t\t\t\tmessage,\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Post-processing part - If we are processing a batch then this could be the last message in the batch.\n\t\tthis.maybeProcessBatchEnd(message);\n\n\t\treturn pendingMessage.localOpMetadata;\n\t}\n\n\t/**\n\t * This message could be the first message in batch. If so, set batch state marking the beginning of a batch.\n\t * @param message - The message that is being processed.\n\t */\n\tprivate maybeProcessBatchBegin(message: ISequencedDocumentMessage) {\n\t\t// This message is the first in a batch if the \"batch\" property on the metadata is set to true\n\t\tif (message.metadata?.batch) {\n\t\t\t// We should not already be processing a batch and there should be no pending batch begin message.\n\t\t\tassert(\n\t\t\t\t!this.isProcessingBatch && this.pendingBatchBeginMessage === undefined,\n\t\t\t\t0x16b /* \"The pending batch state indicates we are already processing a batch\" */,\n\t\t\t);\n\n\t\t\t// Set the pending batch state indicating we have started processing a batch.\n\t\t\tthis.pendingBatchBeginMessage = message;\n\t\t\tthis.isProcessingBatch = true;\n\t\t}\n\t}\n\n\t/**\n\t * This message could be the last message in batch. If so, clear batch state since the batch is complete.\n\t * @param message - The message that is being processed.\n\t */\n\tprivate maybeProcessBatchEnd(message: ISequencedDocumentMessage) {\n\t\tif (!this.isProcessingBatch) {\n\t\t\treturn;\n\t\t}\n\n\t\t// There should be a pending batch begin message.\n\t\tassert(\n\t\t\tthis.pendingBatchBeginMessage !== undefined,\n\t\t\t0x16d /* \"There is no pending batch begin message\" */,\n\t\t);\n\n\t\tconst batchEndMetadata = message.metadata?.batch;\n\t\tif (this.pendingMessages.isEmpty() || batchEndMetadata === false) {\n\t\t\t// Get the batch begin metadata from the first message in the batch.\n\t\t\tconst batchBeginMetadata = this.pendingBatchBeginMessage.metadata?.batch;\n\n\t\t\t// There could be just a single message in the batch. If so, it should not have any batch metadata. If there\n\t\t\t// are multiple messages in the batch, verify that we got the correct batch begin and end metadata.\n\t\t\tif (this.pendingBatchBeginMessage === message) {\n\t\t\t\tassert(\n\t\t\t\t\tbatchBeginMetadata === undefined,\n\t\t\t\t\t0x16e /* \"Batch with single message should not have batch metadata\" */,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tif (batchBeginMetadata !== true || batchEndMetadata !== false) {\n\t\t\t\t\tthis.stateHandler.close(\n\t\t\t\t\t\tDataProcessingError.create(\n\t\t\t\t\t\t\t\"Pending batch inconsistency\", // Formerly known as asserts 0x16f and 0x170\n\t\t\t\t\t\t\t\"processPendingLocalMessage\",\n\t\t\t\t\t\t\tmessage,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\truntimeVersion: pkgVersion,\n\t\t\t\t\t\t\t\tbatchClientId: this.pendingBatchBeginMessage.clientId,\n\t\t\t\t\t\t\t\tclientId: this.stateHandler.clientId(),\n\t\t\t\t\t\t\t\thasBatchStart: batchBeginMetadata === true,\n\t\t\t\t\t\t\t\thasBatchEnd: batchEndMetadata === false,\n\t\t\t\t\t\t\t\tmessageType: message.type,\n\t\t\t\t\t\t\t\tpendingMessagesCount: this.pendingMessagesCount,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Clear the pending batch state now that we have processed the entire batch.\n\t\t\tthis.pendingBatchBeginMessage = undefined;\n\t\t\tthis.isProcessingBatch = false;\n\t\t}\n\t}\n\n\t/**\n\t * Called when the Container's connection state changes. If the Container gets connected, it replays all the pending\n\t * states in its queue. This includes triggering resubmission of unacked ops.\n\t */\n\tpublic replayPendingStates() {\n\t\tassert(\n\t\t\tthis.stateHandler.connected(),\n\t\t\t0x172 /* \"The connection state is not consistent with the runtime\" */,\n\t\t);\n\n\t\t// This assert suggests we are about to send same ops twice, which will result in data loss.\n\t\tassert(\n\t\t\tthis.clientId !== this.stateHandler.clientId(),\n\t\t\t0x173 /* \"replayPendingStates called twice for same clientId!\" */,\n\t\t);\n\t\tthis.clientId = this.stateHandler.clientId();\n\n\t\tassert(\n\t\t\tthis.initialMessages.isEmpty(),\n\t\t\t0x174 /* \"initial states should be empty before replaying pending\" */,\n\t\t);\n\n\t\tlet pendingMessagesCount = this.pendingMessages.length;\n\t\tif (pendingMessagesCount === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Process exactly `pendingMessagesCount` items in the queue as it represents the number of messages that were\n\t\t// pending when we connected. This is important because the `reSubmitFn` might add more items in the queue\n\t\t// which must not be replayed.\n\t\twhile (pendingMessagesCount > 0) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tlet pendingMessage = this.pendingMessages.shift()!;\n\t\t\tpendingMessagesCount--;\n\t\t\tassert(\n\t\t\t\tpendingMessage.opMetadata?.batch !== false,\n\t\t\t\t0x41b /* We cannot process batches in chunks */,\n\t\t\t);\n\n\t\t\t/**\n\t\t\t * We want to ensure grouped messages get processed in a batch.\n\t\t\t * Note: It is not possible for the PendingStateManager to receive a partially acked batch. It will\n\t\t\t * either receive the whole batch ack or nothing at all.\n\t\t\t */\n\t\t\tif (pendingMessage.opMetadata?.batch) {\n\t\t\t\tassert(\n\t\t\t\t\tpendingMessagesCount > 0,\n\t\t\t\t\t0x554 /* Last pending message cannot be a batch begin */,\n\t\t\t\t);\n\n\t\t\t\tthis.stateHandler.orderSequentially(() => {\n\t\t\t\t\twhile (pendingMessagesCount >= 0) {\n\t\t\t\t\t\t// check is >= because batch end may be last pending message\n\t\t\t\t\t\tthis.stateHandler.reSubmit(\n\t\t\t\t\t\t\tpendingMessage.messageType,\n\t\t\t\t\t\t\tpendingMessage.content,\n\t\t\t\t\t\t\tpendingMessage.localOpMetadata,\n\t\t\t\t\t\t\tpendingMessage.opMetadata,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (pendingMessage.opMetadata?.batch === false) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tassert(pendingMessagesCount > 0, 0x555 /* No batch end found */);\n\n\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\t\t\tpendingMessage = this.pendingMessages.shift()!;\n\t\t\t\t\t\tpendingMessagesCount--;\n\t\t\t\t\t\tassert(\n\t\t\t\t\t\t\tpendingMessage.opMetadata?.batch !== true,\n\t\t\t\t\t\t\t0x556 /* Batch start needs a corresponding batch end */,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.stateHandler.reSubmit(\n\t\t\t\t\tpendingMessage.messageType,\n\t\t\t\t\tpendingMessage.content,\n\t\t\t\t\tpendingMessage.localOpMetadata,\n\t\t\t\t\tpendingMessage.opMetadata,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  /*!
3
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
4
3
  * Licensed under the MIT License.
@@ -1 +1 @@
1
- {"version":3,"file":"scheduleManager.d.ts","sourceRoot":"","sources":["../src/scheduleManager.ts"],"names":[],"mappings":";AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AACnG,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAiBtE;;;;;;;;GAQG;AACH,qBAAa,eAAe;IAMpB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAR3B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,QAAQ,CAAS;gBAGJ,YAAY,EAAE,aAAa,CAAC,yBAAyB,EAAE,gBAAgB,CAAC,EACxE,OAAO,EAAE,YAAY,EAC7B,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,gBAAgB;IAStC,kBAAkB,CAAC,OAAO,EAAE,yBAAyB;IAcrD,iBAAiB,CAAC,KAAK,EAAE,GAAG,GAAG,SAAS,EAAE,OAAO,EAAE,yBAAyB;CAwBtF"}
1
+ {"version":3,"file":"scheduleManager.d.ts","sourceRoot":"","sources":["../src/scheduleManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AACnG,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAkBtE;;;;;;;;GAQG;AACH,qBAAa,eAAe;IAM1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM;IARxB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,QAAQ,CAAS;gBAGP,YAAY,EAAE,aAAa,CAAC,yBAAyB,EAAE,gBAAgB,CAAC,EACxE,OAAO,EAAE,YAAY,EAC7B,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,gBAAgB;IASnC,kBAAkB,CAAC,OAAO,EAAE,yBAAyB;IAgBrD,iBAAiB,CAAC,KAAK,EAAE,GAAG,GAAG,SAAS,EAAE,OAAO,EAAE,yBAAyB;CAwBnF"}
@@ -7,7 +7,6 @@ const driver_utils_1 = require("@fluidframework/driver-utils");
7
7
  const container_utils_1 = require("@fluidframework/container-utils");
8
8
  const deltaScheduler_1 = require("./deltaScheduler");
9
9
  const packageVersion_1 = require("./packageVersion");
10
- const connectionTelemetry_1 = require("./connectionTelemetry");
11
10
  /**
12
11
  * This class has the following responsibilities:
13
12
  *
@@ -143,7 +142,7 @@ class ScheduleManagerCore {
143
142
  }
144
143
  resumeQueue(startBatch, messageEndBatch) {
145
144
  const endBatch = messageEndBatch.sequenceNumber;
146
- const duration = this.localPaused ? (common_utils_1.performance.now() - this.timePaused) : undefined;
145
+ const duration = this.localPaused ? common_utils_1.performance.now() - this.timePaused : undefined;
147
146
  this.batchCount++;
148
147
  if (this.batchCount % 1000 === 1) {
149
148
  this.logger.sendTelemetryEvent({
@@ -161,15 +160,6 @@ class ScheduleManagerCore {
161
160
  return;
162
161
  }
163
162
  this.localPaused = false;
164
- // Random round number - we want to know when batch waiting paused op processing.
165
- if (duration !== undefined && duration > connectionTelemetry_1.latencyThreshold) {
166
- this.logger.sendErrorEvent({
167
- eventName: "MaxBatchWaitTimeExceeded",
168
- duration,
169
- sequenceNumber: endBatch,
170
- length: endBatch - startBatch,
171
- });
172
- }
173
163
  this.deltaManager.inbound.resume();
174
164
  }
175
165
  /**
@@ -179,6 +169,8 @@ class ScheduleManagerCore {
179
169
  (0, common_utils_1.assert)(this.deltaManager.inbound.length !== 0, 0x298 /* "we have something in the queue that generates this event" */);
180
170
  (0, common_utils_1.assert)((this.currentBatchClientId === undefined) === (this.pauseSequenceNumber === undefined), 0x299 /* "non-synchronized state" */);
181
171
  const metadata = message.metadata;
172
+ // batchMetadata will be true for the message that starts a batch, false for the one that ends it, and
173
+ // undefined for all other messages.
182
174
  const batchMetadata = metadata === null || metadata === void 0 ? void 0 : metadata.batch;
183
175
  // Protocol messages are never part of a runtime batch of messages
184
176
  if (!(0, driver_utils_1.isRuntimeMessage)(message)) {
@@ -201,20 +193,18 @@ class ScheduleManagerCore {
201
193
  (0, common_utils_1.assert)(!this.localPaused, 0x29d /* "we should be processing ops when there is no active batch" */);
202
194
  return;
203
195
  }
204
- // If the client ID changes then we can move the pause point. If it stayed the same then we need to check.
205
- // If batchMetadata is not undefined then if it's true we've begun a new batch - if false we've ended
206
- // the previous one
207
- if (this.currentBatchClientId !== undefined || batchMetadata === false) {
208
- if (this.currentBatchClientId !== message.clientId) {
209
- // "Batch not closed, yet message from another client!"
210
- throw new container_utils_1.DataCorruptionError("OpBatchIncomplete", Object.assign({ runtimeVersion: packageVersion_1.pkgVersion, batchClientId: this.currentBatchClientId, pauseSequenceNumber: this.pauseSequenceNumber, localBatch: this.currentBatchClientId === this.getClientId(), localMessage: message.clientId === this.getClientId() }, (0, container_utils_1.extractSafePropertiesFromMessage)(message)));
211
- }
196
+ // If we got here, the message is part of a batch. Either starting, in progress, or ending.
197
+ // If this is not the start of the batch, error out if the message was sent by a client other than the one that
198
+ // started the current batch (it should not be possible for ops from other clients to get interleaved with a batch).
199
+ if (this.currentBatchClientId !== undefined &&
200
+ this.currentBatchClientId !== message.clientId) {
201
+ throw new container_utils_1.DataCorruptionError("OpBatchIncomplete", Object.assign({ runtimeVersion: packageVersion_1.pkgVersion, batchClientId: this.currentBatchClientId, pauseSequenceNumber: this.pauseSequenceNumber, localBatch: this.currentBatchClientId === this.getClientId(), localMessage: message.clientId === this.getClientId() }, (0, container_utils_1.extractSafePropertiesFromMessage)(message)));
212
202
  }
213
203
  // The queue is
214
204
  // 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:
215
205
  // - in afterOpProcessing() - processing ops until reaching start of incomplete batch
216
- // - here (batchMetadata == false below), when queue was empty and start of batch showed up.
217
- // 2. resumed when batch end comes in (batchMetadata === true case below)
206
+ // - here, when queue was empty and start of batch showed up (batchMetadata === true below).
207
+ // 2. resumed when batch end comes in (batchMetadata === false below)
218
208
  if (batchMetadata) {
219
209
  (0, common_utils_1.assert)(this.currentBatchClientId === undefined, 0x29e /* "there can't be active batch" */);
220
210
  (0, common_utils_1.assert)(!this.localPaused, 0x29f /* "we should be processing ops when there is no active batch" */);