@fluidframework/container-runtime 2.0.0-rc.2.0.1 → 2.0.0-rc.3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (554) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/api-report/container-runtime.api.md +471 -52
  3. package/dist/batchTracker.d.ts +1 -1
  4. package/dist/batchTracker.d.ts.map +1 -1
  5. package/dist/batchTracker.js +4 -4
  6. package/dist/batchTracker.js.map +1 -1
  7. package/dist/blobManager.d.ts +33 -30
  8. package/dist/blobManager.d.ts.map +1 -1
  9. package/dist/blobManager.js +82 -107
  10. package/dist/blobManager.js.map +1 -1
  11. package/dist/channelCollection.d.ts +27 -22
  12. package/dist/channelCollection.d.ts.map +1 -1
  13. package/dist/channelCollection.js +189 -165
  14. package/dist/channelCollection.js.map +1 -1
  15. package/dist/connectionTelemetry.d.ts +3 -3
  16. package/dist/connectionTelemetry.d.ts.map +1 -1
  17. package/dist/connectionTelemetry.js +17 -17
  18. package/dist/connectionTelemetry.js.map +1 -1
  19. package/dist/containerHandleContext.d.ts.map +1 -1
  20. package/dist/containerHandleContext.js +2 -2
  21. package/dist/containerHandleContext.js.map +1 -1
  22. package/dist/containerRuntime.d.ts +42 -39
  23. package/dist/containerRuntime.d.ts.map +1 -1
  24. package/dist/containerRuntime.js +425 -292
  25. package/dist/containerRuntime.js.map +1 -1
  26. package/dist/dataStore.d.ts +1 -1
  27. package/dist/dataStore.d.ts.map +1 -1
  28. package/dist/dataStore.js +8 -8
  29. package/dist/dataStore.js.map +1 -1
  30. package/dist/dataStoreContext.d.ts +58 -19
  31. package/dist/dataStoreContext.d.ts.map +1 -1
  32. package/dist/dataStoreContext.js +171 -114
  33. package/dist/dataStoreContext.js.map +1 -1
  34. package/dist/dataStoreContexts.d.ts +1 -0
  35. package/dist/dataStoreContexts.d.ts.map +1 -1
  36. package/dist/dataStoreContexts.js +12 -11
  37. package/dist/dataStoreContexts.js.map +1 -1
  38. package/dist/dataStoreRegistry.d.ts +5 -1
  39. package/dist/dataStoreRegistry.d.ts.map +1 -1
  40. package/dist/dataStoreRegistry.js +4 -4
  41. package/dist/dataStoreRegistry.js.map +1 -1
  42. package/dist/deltaManagerSummarizerProxy.d.ts +1 -1
  43. package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -1
  44. package/dist/deltaManagerSummarizerProxy.js.map +1 -1
  45. package/dist/deltaScheduler.d.ts +1 -1
  46. package/dist/deltaScheduler.d.ts.map +1 -1
  47. package/dist/deltaScheduler.js +6 -6
  48. package/dist/deltaScheduler.js.map +1 -1
  49. package/dist/error.d.ts +1 -1
  50. package/dist/error.d.ts.map +1 -1
  51. package/dist/error.js +4 -4
  52. package/dist/error.js.map +1 -1
  53. package/dist/gc/garbageCollection.d.ts +3 -2
  54. package/dist/gc/garbageCollection.d.ts.map +1 -1
  55. package/dist/gc/garbageCollection.js +23 -23
  56. package/dist/gc/garbageCollection.js.map +1 -1
  57. package/dist/gc/gcConfigs.d.ts +2 -2
  58. package/dist/gc/gcConfigs.d.ts.map +1 -1
  59. package/dist/gc/gcConfigs.js +4 -5
  60. package/dist/gc/gcConfigs.js.map +1 -1
  61. package/dist/gc/gcDefinitions.d.ts +4 -5
  62. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  63. package/dist/gc/gcDefinitions.js.map +1 -1
  64. package/dist/gc/gcHelpers.d.ts +5 -1
  65. package/dist/gc/gcHelpers.d.ts.map +1 -1
  66. package/dist/gc/gcHelpers.js +21 -12
  67. package/dist/gc/gcHelpers.js.map +1 -1
  68. package/dist/gc/gcSummaryStateTracker.d.ts +2 -2
  69. package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -1
  70. package/dist/gc/gcSummaryStateTracker.js +11 -11
  71. package/dist/gc/gcSummaryStateTracker.js.map +1 -1
  72. package/dist/gc/gcTelemetry.d.ts +2 -1
  73. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  74. package/dist/gc/gcTelemetry.js +11 -9
  75. package/dist/gc/gcTelemetry.js.map +1 -1
  76. package/dist/gc/gcUnreferencedStateTracker.d.ts.map +1 -1
  77. package/dist/gc/gcUnreferencedStateTracker.js +6 -6
  78. package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
  79. package/dist/gc/index.d.ts +1 -1
  80. package/dist/gc/index.d.ts.map +1 -1
  81. package/dist/gc/index.js +2 -1
  82. package/dist/gc/index.js.map +1 -1
  83. package/dist/index.d.ts +5 -2
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +12 -2
  86. package/dist/index.js.map +1 -1
  87. package/dist/legacy.d.ts +91 -0
  88. package/dist/messageTypes.d.ts +11 -5
  89. package/dist/messageTypes.d.ts.map +1 -1
  90. package/dist/messageTypes.js +4 -0
  91. package/dist/messageTypes.js.map +1 -1
  92. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  93. package/dist/opLifecycle/batchManager.js.map +1 -1
  94. package/dist/opLifecycle/definitions.d.ts +2 -20
  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.map +1 -1
  102. package/dist/opLifecycle/opCompressor.js +5 -6
  103. package/dist/opLifecycle/opCompressor.js.map +1 -1
  104. package/dist/opLifecycle/opDecompressor.d.ts +15 -4
  105. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  106. package/dist/opLifecycle/opDecompressor.js +62 -63
  107. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  108. package/dist/opLifecycle/opGroupingManager.d.ts +2 -1
  109. package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
  110. package/dist/opLifecycle/opGroupingManager.js +14 -16
  111. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  112. package/dist/opLifecycle/opSplitter.d.ts +12 -4
  113. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  114. package/dist/opLifecycle/opSplitter.js +63 -53
  115. package/dist/opLifecycle/opSplitter.js.map +1 -1
  116. package/dist/opLifecycle/outbox.d.ts +2 -1
  117. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  118. package/dist/opLifecycle/outbox.js +30 -29
  119. package/dist/opLifecycle/outbox.js.map +1 -1
  120. package/dist/opLifecycle/remoteMessageProcessor.d.ts +8 -0
  121. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  122. package/dist/opLifecycle/remoteMessageProcessor.js +36 -32
  123. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  124. package/dist/packageVersion.d.ts +1 -1
  125. package/dist/packageVersion.js +1 -1
  126. package/dist/packageVersion.js.map +1 -1
  127. package/dist/pendingStateManager.d.ts +1 -1
  128. package/dist/pendingStateManager.d.ts.map +1 -1
  129. package/dist/pendingStateManager.js +18 -18
  130. package/dist/pendingStateManager.js.map +1 -1
  131. package/dist/public.d.ts +12 -0
  132. package/dist/scheduleManager.d.ts +1 -1
  133. package/dist/scheduleManager.d.ts.map +1 -1
  134. package/dist/scheduleManager.js +28 -24
  135. package/dist/scheduleManager.js.map +1 -1
  136. package/dist/storageServiceWithAttachBlobs.d.ts +2 -2
  137. package/dist/storageServiceWithAttachBlobs.d.ts.map +1 -1
  138. package/dist/storageServiceWithAttachBlobs.js +2 -2
  139. package/dist/storageServiceWithAttachBlobs.js.map +1 -1
  140. package/dist/summary/documentSchema.d.ts +209 -0
  141. package/dist/summary/documentSchema.d.ts.map +1 -0
  142. package/dist/summary/documentSchema.js +390 -0
  143. package/dist/summary/documentSchema.js.map +1 -0
  144. package/dist/summary/index.d.ts +2 -1
  145. package/dist/summary/index.d.ts.map +1 -1
  146. package/dist/summary/index.js +4 -1
  147. package/dist/summary/index.js.map +1 -1
  148. package/dist/summary/orderedClientElection.d.ts +2 -2
  149. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  150. package/dist/summary/orderedClientElection.js +12 -7
  151. package/dist/summary/orderedClientElection.js.map +1 -1
  152. package/dist/summary/runWhileConnectedCoordinator.d.ts +1 -1
  153. package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
  154. package/dist/summary/runWhileConnectedCoordinator.js +3 -3
  155. package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
  156. package/dist/summary/runningSummarizer.d.ts +3 -3
  157. package/dist/summary/runningSummarizer.d.ts.map +1 -1
  158. package/dist/summary/runningSummarizer.js +16 -16
  159. package/dist/summary/runningSummarizer.js.map +1 -1
  160. package/dist/summary/summarizer.d.ts +3 -2
  161. package/dist/summary/summarizer.d.ts.map +1 -1
  162. package/dist/summary/summarizer.js +13 -13
  163. package/dist/summary/summarizer.js.map +1 -1
  164. package/dist/summary/summarizerClientElection.d.ts +2 -2
  165. package/dist/summary/summarizerClientElection.d.ts.map +1 -1
  166. package/dist/summary/summarizerClientElection.js.map +1 -1
  167. package/dist/summary/summarizerHeuristics.d.ts +1 -1
  168. package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
  169. package/dist/summary/summarizerHeuristics.js +2 -2
  170. package/dist/summary/summarizerHeuristics.js.map +1 -1
  171. package/dist/summary/summarizerNode/summarizerNode.d.ts +3 -2
  172. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  173. package/dist/summary/summarizerNode/summarizerNode.js +28 -28
  174. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  175. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -1
  176. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  177. package/dist/summary/summarizerNode/summarizerNodeUtils.js +3 -3
  178. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  179. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +2 -1
  180. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  181. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +14 -14
  182. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  183. package/dist/summary/summarizerTypes.d.ts +5 -3
  184. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  185. package/dist/summary/summarizerTypes.js.map +1 -1
  186. package/dist/summary/summaryCollection.d.ts +2 -2
  187. package/dist/summary/summaryCollection.d.ts.map +1 -1
  188. package/dist/summary/summaryCollection.js +7 -7
  189. package/dist/summary/summaryCollection.js.map +1 -1
  190. package/dist/summary/summaryFormat.d.ts +6 -17
  191. package/dist/summary/summaryFormat.d.ts.map +1 -1
  192. package/dist/summary/summaryFormat.js +8 -8
  193. package/dist/summary/summaryFormat.js.map +1 -1
  194. package/dist/summary/summaryGenerator.d.ts +4 -3
  195. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  196. package/dist/summary/summaryGenerator.js +17 -17
  197. package/dist/summary/summaryGenerator.js.map +1 -1
  198. package/dist/summary/summaryManager.d.ts +1 -1
  199. package/dist/summary/summaryManager.d.ts.map +1 -1
  200. package/dist/summary/summaryManager.js +15 -14
  201. package/dist/summary/summaryManager.js.map +1 -1
  202. package/internal.d.ts +11 -0
  203. package/legacy.d.ts +11 -0
  204. package/lib/batchTracker.d.ts +1 -1
  205. package/lib/batchTracker.d.ts.map +1 -1
  206. package/lib/batchTracker.js +2 -2
  207. package/lib/batchTracker.js.map +1 -1
  208. package/lib/blobManager.d.ts +33 -30
  209. package/lib/blobManager.d.ts.map +1 -1
  210. package/lib/blobManager.js +48 -73
  211. package/lib/blobManager.js.map +1 -1
  212. package/lib/channelCollection.d.ts +27 -22
  213. package/lib/channelCollection.d.ts.map +1 -1
  214. package/lib/channelCollection.js +132 -108
  215. package/lib/channelCollection.js.map +1 -1
  216. package/lib/connectionTelemetry.d.ts +3 -3
  217. package/lib/connectionTelemetry.d.ts.map +1 -1
  218. package/lib/connectionTelemetry.js +3 -3
  219. package/lib/connectionTelemetry.js.map +1 -1
  220. package/lib/containerHandleContext.d.ts.map +1 -1
  221. package/lib/containerHandleContext.js +1 -1
  222. package/lib/containerHandleContext.js.map +1 -1
  223. package/lib/containerRuntime.d.ts +42 -39
  224. package/lib/containerRuntime.d.ts.map +1 -1
  225. package/lib/containerRuntime.js +276 -141
  226. package/lib/containerRuntime.js.map +1 -1
  227. package/lib/dataStore.d.ts +1 -1
  228. package/lib/dataStore.d.ts.map +1 -1
  229. package/lib/dataStore.js +3 -3
  230. package/lib/dataStore.js.map +1 -1
  231. package/lib/dataStoreContext.d.ts +58 -19
  232. package/lib/dataStoreContext.d.ts.map +1 -1
  233. package/lib/dataStoreContext.js +110 -53
  234. package/lib/dataStoreContext.js.map +1 -1
  235. package/lib/dataStoreContexts.d.ts +1 -0
  236. package/lib/dataStoreContexts.d.ts.map +1 -1
  237. package/lib/dataStoreContexts.js +3 -2
  238. package/lib/dataStoreContexts.js.map +1 -1
  239. package/lib/dataStoreRegistry.d.ts +5 -1
  240. package/lib/dataStoreRegistry.d.ts.map +1 -1
  241. package/lib/dataStoreRegistry.js +1 -1
  242. package/lib/dataStoreRegistry.js.map +1 -1
  243. package/lib/deltaManagerSummarizerProxy.d.ts +1 -1
  244. package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -1
  245. package/lib/deltaManagerSummarizerProxy.js.map +1 -1
  246. package/lib/deltaScheduler.d.ts +1 -1
  247. package/lib/deltaScheduler.d.ts.map +1 -1
  248. package/lib/deltaScheduler.js +1 -1
  249. package/lib/deltaScheduler.js.map +1 -1
  250. package/lib/error.d.ts +1 -1
  251. package/lib/error.d.ts.map +1 -1
  252. package/lib/error.js +2 -2
  253. package/lib/error.js.map +1 -1
  254. package/lib/gc/garbageCollection.d.ts +3 -2
  255. package/lib/gc/garbageCollection.d.ts.map +1 -1
  256. package/lib/gc/garbageCollection.js +8 -8
  257. package/lib/gc/garbageCollection.js.map +1 -1
  258. package/lib/gc/gcConfigs.d.ts +2 -2
  259. package/lib/gc/gcConfigs.d.ts.map +1 -1
  260. package/lib/gc/gcConfigs.js +4 -5
  261. package/lib/gc/gcConfigs.js.map +1 -1
  262. package/lib/gc/gcDefinitions.d.ts +4 -5
  263. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  264. package/lib/gc/gcDefinitions.js.map +1 -1
  265. package/lib/gc/gcHelpers.d.ts +5 -1
  266. package/lib/gc/gcHelpers.d.ts.map +1 -1
  267. package/lib/gc/gcHelpers.js +10 -2
  268. package/lib/gc/gcHelpers.js.map +1 -1
  269. package/lib/gc/gcSummaryStateTracker.d.ts +2 -2
  270. package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -1
  271. package/lib/gc/gcSummaryStateTracker.js +2 -2
  272. package/lib/gc/gcSummaryStateTracker.js.map +1 -1
  273. package/lib/gc/gcTelemetry.d.ts +2 -1
  274. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  275. package/lib/gc/gcTelemetry.js +4 -2
  276. package/lib/gc/gcTelemetry.js.map +1 -1
  277. package/lib/gc/gcUnreferencedStateTracker.d.ts.map +1 -1
  278. package/lib/gc/gcUnreferencedStateTracker.js +2 -2
  279. package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
  280. package/lib/gc/index.d.ts +1 -1
  281. package/lib/gc/index.d.ts.map +1 -1
  282. package/lib/gc/index.js +1 -1
  283. package/lib/gc/index.js.map +1 -1
  284. package/lib/index.d.ts +5 -2
  285. package/lib/index.d.ts.map +1 -1
  286. package/lib/index.js +5 -2
  287. package/lib/index.js.map +1 -1
  288. package/lib/legacy.d.ts +91 -0
  289. package/lib/messageTypes.d.ts +11 -5
  290. package/lib/messageTypes.d.ts.map +1 -1
  291. package/lib/messageTypes.js +4 -0
  292. package/lib/messageTypes.js.map +1 -1
  293. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  294. package/lib/opLifecycle/batchManager.js.map +1 -1
  295. package/lib/opLifecycle/definitions.d.ts +2 -20
  296. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  297. package/lib/opLifecycle/definitions.js.map +1 -1
  298. package/lib/opLifecycle/index.d.ts +3 -3
  299. package/lib/opLifecycle/index.d.ts.map +1 -1
  300. package/lib/opLifecycle/index.js +2 -2
  301. package/lib/opLifecycle/index.js.map +1 -1
  302. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  303. package/lib/opLifecycle/opCompressor.js +2 -3
  304. package/lib/opLifecycle/opCompressor.js.map +1 -1
  305. package/lib/opLifecycle/opDecompressor.d.ts +15 -4
  306. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  307. package/lib/opLifecycle/opDecompressor.js +61 -62
  308. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  309. package/lib/opLifecycle/opGroupingManager.d.ts +2 -1
  310. package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
  311. package/lib/opLifecycle/opGroupingManager.js +9 -12
  312. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  313. package/lib/opLifecycle/opSplitter.d.ts +12 -4
  314. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  315. package/lib/opLifecycle/opSplitter.js +47 -38
  316. package/lib/opLifecycle/opSplitter.js.map +1 -1
  317. package/lib/opLifecycle/outbox.d.ts +2 -1
  318. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  319. package/lib/opLifecycle/outbox.js +19 -18
  320. package/lib/opLifecycle/outbox.js.map +1 -1
  321. package/lib/opLifecycle/remoteMessageProcessor.d.ts +8 -0
  322. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  323. package/lib/opLifecycle/remoteMessageProcessor.js +36 -32
  324. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  325. package/lib/packageVersion.d.ts +1 -1
  326. package/lib/packageVersion.js +1 -1
  327. package/lib/packageVersion.js.map +1 -1
  328. package/lib/pendingStateManager.d.ts +1 -1
  329. package/lib/pendingStateManager.d.ts.map +1 -1
  330. package/lib/pendingStateManager.js +2 -2
  331. package/lib/pendingStateManager.js.map +1 -1
  332. package/lib/public.d.ts +12 -0
  333. package/lib/scheduleManager.d.ts +1 -1
  334. package/lib/scheduleManager.d.ts.map +1 -1
  335. package/lib/scheduleManager.js +7 -3
  336. package/lib/scheduleManager.js.map +1 -1
  337. package/lib/storageServiceWithAttachBlobs.d.ts +2 -2
  338. package/lib/storageServiceWithAttachBlobs.d.ts.map +1 -1
  339. package/lib/storageServiceWithAttachBlobs.js +1 -1
  340. package/lib/storageServiceWithAttachBlobs.js.map +1 -1
  341. package/lib/summary/documentSchema.d.ts +209 -0
  342. package/lib/summary/documentSchema.d.ts.map +1 -0
  343. package/lib/summary/documentSchema.js +386 -0
  344. package/lib/summary/documentSchema.js.map +1 -0
  345. package/lib/summary/index.d.ts +2 -1
  346. package/lib/summary/index.d.ts.map +1 -1
  347. package/lib/summary/index.js +1 -0
  348. package/lib/summary/index.js.map +1 -1
  349. package/lib/summary/orderedClientElection.d.ts +2 -2
  350. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  351. package/lib/summary/orderedClientElection.js +7 -2
  352. package/lib/summary/orderedClientElection.js.map +1 -1
  353. package/lib/summary/runWhileConnectedCoordinator.d.ts +1 -1
  354. package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
  355. package/lib/summary/runWhileConnectedCoordinator.js +1 -1
  356. package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
  357. package/lib/summary/runningSummarizer.d.ts +3 -3
  358. package/lib/summary/runningSummarizer.d.ts.map +1 -1
  359. package/lib/summary/runningSummarizer.js +3 -3
  360. package/lib/summary/runningSummarizer.js.map +1 -1
  361. package/lib/summary/summarizer.d.ts +3 -2
  362. package/lib/summary/summarizer.d.ts.map +1 -1
  363. package/lib/summary/summarizer.js +3 -3
  364. package/lib/summary/summarizer.js.map +1 -1
  365. package/lib/summary/summarizerClientElection.d.ts +2 -2
  366. package/lib/summary/summarizerClientElection.d.ts.map +1 -1
  367. package/lib/summary/summarizerClientElection.js.map +1 -1
  368. package/lib/summary/summarizerHeuristics.d.ts +1 -1
  369. package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
  370. package/lib/summary/summarizerHeuristics.js +1 -1
  371. package/lib/summary/summarizerHeuristics.js.map +1 -1
  372. package/lib/summary/summarizerNode/summarizerNode.d.ts +3 -2
  373. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  374. package/lib/summary/summarizerNode/summarizerNode.js +5 -5
  375. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  376. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -1
  377. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  378. package/lib/summary/summarizerNode/summarizerNodeUtils.js +1 -1
  379. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  380. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +2 -1
  381. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  382. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +3 -3
  383. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  384. package/lib/summary/summarizerTypes.d.ts +5 -3
  385. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  386. package/lib/summary/summarizerTypes.js.map +1 -1
  387. package/lib/summary/summaryCollection.d.ts +2 -2
  388. package/lib/summary/summaryCollection.d.ts.map +1 -1
  389. package/lib/summary/summaryCollection.js +1 -1
  390. package/lib/summary/summaryCollection.js.map +1 -1
  391. package/lib/summary/summaryFormat.d.ts +6 -17
  392. package/lib/summary/summaryFormat.d.ts.map +1 -1
  393. package/lib/summary/summaryFormat.js +3 -3
  394. package/lib/summary/summaryFormat.js.map +1 -1
  395. package/lib/summary/summaryGenerator.d.ts +4 -3
  396. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  397. package/lib/summary/summaryGenerator.js +4 -4
  398. package/lib/summary/summaryGenerator.js.map +1 -1
  399. package/lib/summary/summaryManager.d.ts +1 -1
  400. package/lib/summary/summaryManager.d.ts.map +1 -1
  401. package/lib/summary/summaryManager.js +9 -8
  402. package/lib/summary/summaryManager.js.map +1 -1
  403. package/package.json +57 -65
  404. package/src/batchTracker.ts +4 -3
  405. package/src/blobManager.ts +100 -77
  406. package/src/channelCollection.ts +223 -167
  407. package/src/connectionTelemetry.ts +12 -12
  408. package/src/containerHandleContext.ts +3 -2
  409. package/src/containerRuntime.ts +481 -277
  410. package/src/dataStore.ts +9 -4
  411. package/src/dataStoreContext.ts +201 -97
  412. package/src/dataStoreContexts.ts +5 -2
  413. package/src/dataStoreRegistry.ts +3 -2
  414. package/src/deltaManagerSummarizerProxy.ts +1 -1
  415. package/src/deltaScheduler.ts +2 -1
  416. package/src/error.ts +2 -2
  417. package/src/gc/garbageCollection.ts +21 -20
  418. package/src/gc/gcConfigs.ts +15 -18
  419. package/src/gc/gcDefinitions.ts +6 -8
  420. package/src/gc/gcHelpers.ts +22 -5
  421. package/src/gc/gcSummaryStateTracker.ts +7 -5
  422. package/src/gc/gcTelemetry.ts +13 -7
  423. package/src/gc/gcUnreferencedStateTracker.ts +3 -2
  424. package/src/gc/index.ts +1 -0
  425. package/src/index.ts +22 -1
  426. package/src/messageTypes.ts +20 -6
  427. package/src/opLifecycle/README.md +89 -0
  428. package/src/opLifecycle/batchManager.ts +1 -0
  429. package/src/opLifecycle/definitions.ts +3 -21
  430. package/src/opLifecycle/index.ts +3 -9
  431. package/src/opLifecycle/opCompressor.ts +6 -5
  432. package/src/opLifecycle/opDecompressor.ts +90 -100
  433. package/src/opLifecycle/opGroupingManager.ts +12 -14
  434. package/src/opLifecycle/opSplitter.ts +76 -48
  435. package/src/opLifecycle/outbox.ts +30 -38
  436. package/src/opLifecycle/remoteMessageProcessor.ts +43 -55
  437. package/src/packageVersion.ts +1 -1
  438. package/src/pendingStateManager.ts +6 -6
  439. package/src/scheduleManager.ts +10 -8
  440. package/src/storageServiceWithAttachBlobs.ts +2 -2
  441. package/src/summary/documentSchema.ts +631 -0
  442. package/src/summary/index.ts +10 -1
  443. package/src/summary/orderedClientElection.ts +7 -7
  444. package/src/summary/runWhileConnectedCoordinator.ts +3 -2
  445. package/src/summary/runningSummarizer.ts +22 -20
  446. package/src/summary/summarizer.ts +17 -15
  447. package/src/summary/summarizerClientElection.ts +3 -2
  448. package/src/summary/summarizerHeuristics.ts +4 -2
  449. package/src/summary/summarizerNode/summarizerNode.ts +20 -18
  450. package/src/summary/summarizerNode/summarizerNodeUtils.ts +3 -2
  451. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +16 -8
  452. package/src/summary/summarizerTypes.ts +7 -3
  453. package/src/summary/summaryCollection.ts +3 -3
  454. package/src/summary/summaryFormat.ts +14 -26
  455. package/src/summary/summaryGenerator.ts +12 -15
  456. package/src/summary/summaryManager.ts +16 -13
  457. package/api-extractor-cjs.json +0 -8
  458. package/dist/container-runtime-alpha.d.ts +0 -1753
  459. package/dist/container-runtime-beta.d.ts +0 -268
  460. package/dist/container-runtime-public.d.ts +0 -268
  461. package/dist/container-runtime-untrimmed.d.ts +0 -1893
  462. package/lib/container-runtime-alpha.d.ts +0 -1753
  463. package/lib/container-runtime-beta.d.ts +0 -268
  464. package/lib/container-runtime-public.d.ts +0 -268
  465. package/lib/container-runtime-untrimmed.d.ts +0 -1893
  466. package/lib/test/batchTracker.spec.js +0 -88
  467. package/lib/test/batchTracker.spec.js.map +0 -1
  468. package/lib/test/blobManager.spec.js +0 -835
  469. package/lib/test/blobManager.spec.js.map +0 -1
  470. package/lib/test/channelCollection.spec.js +0 -141
  471. package/lib/test/channelCollection.spec.js.map +0 -1
  472. package/lib/test/containerRuntime.spec.js +0 -1748
  473. package/lib/test/containerRuntime.spec.js.map +0 -1
  474. package/lib/test/dataStoreContext.spec.js +0 -801
  475. package/lib/test/dataStoreContext.spec.js.map +0 -1
  476. package/lib/test/dataStoreCreation.spec.js +0 -312
  477. package/lib/test/dataStoreCreation.spec.js.map +0 -1
  478. package/lib/test/dataStoreRegistry.spec.js +0 -26
  479. package/lib/test/dataStoreRegistry.spec.js.map +0 -1
  480. package/lib/test/fuzz/fuzzUtils.js +0 -66
  481. package/lib/test/fuzz/fuzzUtils.js.map +0 -1
  482. package/lib/test/fuzz/summarizer.fuzz.spec.js +0 -31
  483. package/lib/test/fuzz/summarizer.fuzz.spec.js.map +0 -1
  484. package/lib/test/fuzz/summarizerFuzzMocks.js +0 -162
  485. package/lib/test/fuzz/summarizerFuzzMocks.js.map +0 -1
  486. package/lib/test/fuzz/summarizerFuzzSuite.js +0 -106
  487. package/lib/test/fuzz/summarizerFuzzSuite.js.map +0 -1
  488. package/lib/test/gc/garbageCollection.spec.js +0 -1465
  489. package/lib/test/gc/garbageCollection.spec.js.map +0 -1
  490. package/lib/test/gc/gcConfigs.spec.js +0 -690
  491. package/lib/test/gc/gcConfigs.spec.js.map +0 -1
  492. package/lib/test/gc/gcHelpers.spec.js +0 -110
  493. package/lib/test/gc/gcHelpers.spec.js.map +0 -1
  494. package/lib/test/gc/gcReferenceGraphAlgorithm.spec.js +0 -68
  495. package/lib/test/gc/gcReferenceGraphAlgorithm.spec.js.map +0 -1
  496. package/lib/test/gc/gcStats.spec.js +0 -391
  497. package/lib/test/gc/gcStats.spec.js.map +0 -1
  498. package/lib/test/gc/gcSummaryStateTracker.spec.js +0 -228
  499. package/lib/test/gc/gcSummaryStateTracker.spec.js.map +0 -1
  500. package/lib/test/gc/gcTelemetry.spec.js +0 -530
  501. package/lib/test/gc/gcTelemetry.spec.js.map +0 -1
  502. package/lib/test/gc/gcUnitTestHelpers.js +0 -29
  503. package/lib/test/gc/gcUnitTestHelpers.js.map +0 -1
  504. package/lib/test/gc/gcUnreferencedStateTracker.spec.js +0 -192
  505. package/lib/test/gc/gcUnreferencedStateTracker.spec.js.map +0 -1
  506. package/lib/test/getPendingBlobs.spec.js +0 -193
  507. package/lib/test/getPendingBlobs.spec.js.map +0 -1
  508. package/lib/test/hardwareStats.spec.js +0 -93
  509. package/lib/test/hardwareStats.spec.js.map +0 -1
  510. package/lib/test/index.js +0 -6
  511. package/lib/test/index.js.map +0 -1
  512. package/lib/test/opLifecycle/OpGroupingManager.spec.js +0 -225
  513. package/lib/test/opLifecycle/OpGroupingManager.spec.js.map +0 -1
  514. package/lib/test/opLifecycle/batchManager.spec.js +0 -189
  515. package/lib/test/opLifecycle/batchManager.spec.js.map +0 -1
  516. package/lib/test/opLifecycle/opCompressor.spec.js +0 -74
  517. package/lib/test/opLifecycle/opCompressor.spec.js.map +0 -1
  518. package/lib/test/opLifecycle/opDecompressor.spec.js +0 -218
  519. package/lib/test/opLifecycle/opDecompressor.spec.js.map +0 -1
  520. package/lib/test/opLifecycle/opSplitter.spec.js +0 -272
  521. package/lib/test/opLifecycle/opSplitter.spec.js.map +0 -1
  522. package/lib/test/opLifecycle/outbox.spec.js +0 -675
  523. package/lib/test/opLifecycle/outbox.spec.js.map +0 -1
  524. package/lib/test/opLifecycle/remoteMessageProcessor.spec.js +0 -196
  525. package/lib/test/opLifecycle/remoteMessageProcessor.spec.js.map +0 -1
  526. package/lib/test/pendingStateManager.spec.js +0 -329
  527. package/lib/test/pendingStateManager.spec.js.map +0 -1
  528. package/lib/test/scheduleManager.spec.js +0 -270
  529. package/lib/test/scheduleManager.spec.js.map +0 -1
  530. package/lib/test/summarizerNode.spec.js +0 -326
  531. package/lib/test/summarizerNode.spec.js.map +0 -1
  532. package/lib/test/summarizerNodeWithGc.spec.js +0 -318
  533. package/lib/test/summarizerNodeWithGc.spec.js.map +0 -1
  534. package/lib/test/summary/orderedClientElection.spec.js +0 -535
  535. package/lib/test/summary/orderedClientElection.spec.js.map +0 -1
  536. package/lib/test/summary/runningSummarizer.spec.js +0 -1349
  537. package/lib/test/summary/runningSummarizer.spec.js.map +0 -1
  538. package/lib/test/summary/summarizer.spec.js +0 -29
  539. package/lib/test/summary/summarizer.spec.js.map +0 -1
  540. package/lib/test/summary/summarizerClientElection.spec.js +0 -436
  541. package/lib/test/summary/summarizerClientElection.spec.js.map +0 -1
  542. package/lib/test/summary/summarizerHeuristics.spec.js +0 -289
  543. package/lib/test/summary/summarizerHeuristics.spec.js.map +0 -1
  544. package/lib/test/summary/summaryCollection.spec.js +0 -200
  545. package/lib/test/summary/summaryCollection.spec.js.map +0 -1
  546. package/lib/test/summary/summaryManager.spec.js +0 -430
  547. package/lib/test/summary/summaryManager.spec.js.map +0 -1
  548. package/lib/test/summary/testQuorumClients.js +0 -34
  549. package/lib/test/summary/testQuorumClients.js.map +0 -1
  550. package/lib/test/throttler.spec.js +0 -175
  551. package/lib/test/throttler.spec.js.map +0 -1
  552. package/lib/test/types/validateContainerRuntimePrevious.generated.js +0 -180
  553. package/lib/test/types/validateContainerRuntimePrevious.generated.js.map +0 -1
  554. /package/{dist → lib}/tsdoc-metadata.json +0 -0
@@ -1,1465 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
- import { strict as assert } from "assert";
6
- import { useFakeTimers, spy } from "sinon";
7
- import { ContainerErrorTypes, } from "@fluidframework/container-definitions";
8
- import { SummaryType } from "@fluidframework/protocol-definitions";
9
- import { gcBlobPrefix, gcTreeKey, gcDeletedBlobKey, channelsTreeName, gcTombstoneBlobKey, } from "@fluidframework/runtime-definitions";
10
- import { MockLogger, mixinMonitoringContext, tagCodeArtifacts, createChildLogger, } from "@fluidframework/telemetry-utils";
11
- import { concatGarbageCollectionStates, GarbageCollector, GCNodeType, defaultSessionExpiryDurationMs, oneDayMs, stableGCVersion, UnreferencedState, defaultSweepGracePeriodMs, GarbageCollectionMessageType, } from "../../gc/index.js";
12
- import { ContainerMessageType } from "../../messageTypes.js";
13
- import { dataStoreAttributesBlobName, metadataBlobName, } from "../../summary/index.js";
14
- import { pkgVersion } from "../../packageVersion.js";
15
- import { createTestConfigProvider } from "./gcUnitTestHelpers.js";
16
- describe("Garbage Collection Tests", () => {
17
- const defaultSnapshotCacheExpiryMs = 5 * 24 * 60 * 60 * 1000;
18
- const defaultTombstoneTimeoutMs = defaultSessionExpiryDurationMs + defaultSnapshotCacheExpiryMs + oneDayMs;
19
- // Nodes in the reference graph.
20
- const nodes = ["/node1", "/node2", "/node3", "/node4", "/node5", "/node6"];
21
- const testPkgPath = ["testPkg"];
22
- const configProvider = createTestConfigProvider();
23
- let mockLogger;
24
- let mc;
25
- let clock;
26
- // The default GC data returned by `getGCData` on which GC is run. Update this to update the referenced graph.
27
- let defaultGCData = { gcNodes: {} };
28
- // Returns a dummy snapshot tree to be built upon.
29
- const getDummySnapshotTree = () => {
30
- return {
31
- blobs: {},
32
- trees: {},
33
- };
34
- };
35
- /**
36
- * Called when sweep runs. It deleted the nodes from defaultGCData.
37
- */
38
- function deleteSweepReadyNodes(sweepReadyRoutes) {
39
- for (const nodeId of sweepReadyRoutes) {
40
- assert(defaultGCData.gcNodes[nodeId] !== undefined, `Deleted node ${nodeId} doesn't exist`);
41
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
42
- delete defaultGCData.gcNodes[nodeId];
43
- }
44
- return sweepReadyRoutes;
45
- }
46
- function createGarbageCollector(params = {}) {
47
- const { createParams = {}, gcBlobsMap = new Map(), gcMetadata = {}, closeFn = () => { }, isSummarizerClient = true, getGCData = async () => defaultGCData, } = params;
48
- const getNodeType = (nodePath) => {
49
- if (nodePath.split("/").length !== 2) {
50
- return GCNodeType.Other;
51
- }
52
- return GCNodeType.DataStore;
53
- };
54
- // The runtime to be passed to the garbage collector.
55
- const gcRuntime = {
56
- updateStateBeforeGC: async () => { },
57
- getGCData,
58
- updateUsedRoutes: (usedRoutes) => {
59
- return { totalNodeCount: 0, unusedNodeCount: 0 };
60
- },
61
- updateUnusedRoutes: (unusedRoutes) => { },
62
- deleteSweepReadyNodes,
63
- updateTombstonedRoutes: (tombstoneRoutes) => { },
64
- getNodeType,
65
- getCurrentReferenceTimestampMs: () => Date.now(),
66
- closeFn,
67
- };
68
- let metadata = createParams.metadata;
69
- const existing = createParams.baseSnapshot !== undefined;
70
- // For existing, add container runtime metadata which is required for GC to be enabled.
71
- if (existing) {
72
- metadata = {
73
- ...metadata,
74
- ...gcMetadata,
75
- gcFeature: gcMetadata.gcFeature ?? stableGCVersion,
76
- summaryFormatVersion: 1,
77
- message: undefined,
78
- };
79
- }
80
- return GarbageCollector.create({
81
- ...createParams,
82
- runtime: gcRuntime,
83
- gcOptions: createParams.gcOptions ?? {},
84
- baseSnapshot: createParams.baseSnapshot,
85
- baseLogger: createChildLogger({ logger: mc.logger }),
86
- existing,
87
- metadata,
88
- createContainerMetadata: {
89
- createContainerRuntimeVersion: pkgVersion,
90
- createContainerTimestamp: Date.now(),
91
- },
92
- isSummarizerClient,
93
- readAndParseBlob: async (id) => gcBlobsMap.get(id),
94
- getNodePackagePath: async (nodeId) => testPkgPath,
95
- getLastSummaryTimestampMs: () => Date.now(),
96
- submitMessage: (message) => { },
97
- sessionExpiryTimerStarted: createParams.sessionExpiryTimerStarted,
98
- });
99
- }
100
- let gc;
101
- before(() => {
102
- clock = useFakeTimers();
103
- });
104
- beforeEach(() => {
105
- gc = undefined;
106
- mockLogger = new MockLogger();
107
- mc = mixinMonitoringContext(mockLogger, configProvider);
108
- });
109
- afterEach(() => {
110
- clock.reset();
111
- mockLogger.clear();
112
- configProvider.clear();
113
- defaultGCData = { gcNodes: {} };
114
- gc?.dispose();
115
- });
116
- after(() => {
117
- clock.restore();
118
- });
119
- describe("Session expiry", () => {
120
- it("Session expiry closes container", () => {
121
- let closeCalled = false;
122
- function closeCalledAfterExactTicks(ticks) {
123
- clock.tick(ticks - 1);
124
- if (closeCalled) {
125
- return false;
126
- }
127
- clock.tick(1);
128
- return closeCalled;
129
- }
130
- gc = createGarbageCollector({
131
- closeFn: () => {
132
- closeCalled = true;
133
- },
134
- });
135
- assert(closeCalledAfterExactTicks(defaultSessionExpiryDurationMs), "Close should have been called at exactly defaultSessionExpiryDurationMs");
136
- });
137
- it("Session expiry is adjusted by sessionExpiryTimerStarted", () => {
138
- let closeCalled = false;
139
- const sessionExpiryTimerStarted = defaultSessionExpiryDurationMs - 1; // arbitrary number
140
- clock.tick(sessionExpiryTimerStarted + defaultSessionExpiryDurationMs - 1);
141
- gc = createGarbageCollector({
142
- createParams: { sessionExpiryTimerStarted },
143
- closeFn: () => {
144
- closeCalled = true;
145
- },
146
- });
147
- assert(closeCalled === false, "Close should not have been called");
148
- clock.tick(1);
149
- assert(closeCalled, "Close should have been called");
150
- });
151
- it("it throws when already expired", () => {
152
- clock.tick(defaultSessionExpiryDurationMs + 1);
153
- assert.throws(() => createGarbageCollector({
154
- createParams: { sessionExpiryTimerStarted: 1 },
155
- closeFn: () => {
156
- throw new Error("Session expired");
157
- },
158
- }));
159
- });
160
- });
161
- describe("addedOutboundReference", () => {
162
- it("ignores target relative URLs", () => {
163
- const garbageCollector = createGarbageCollector();
164
- const spies = {
165
- telemetryTracker: { nodeUsed: spy(garbageCollector.telemetryTracker, "nodeUsed") },
166
- };
167
- garbageCollector.addedOutboundReference("/from", "to/relative/url");
168
- mockLogger.assertMatch([{ eventName: "GarbageCollector:InvalidRelativeOutboundRoute", category: "error" }], "Expected error log");
169
- assert.equal(spies.telemetryTracker.nodeUsed.callCount, 0, "nodeUsed should not be called");
170
- });
171
- it("tracks target absolute URLs", () => {
172
- const garbageCollector = createGarbageCollector();
173
- const spies = {
174
- telemetryTracker: { nodeUsed: spy(garbageCollector.telemetryTracker, "nodeUsed") },
175
- };
176
- garbageCollector.addedOutboundReference("/from", "/to/absolute/url");
177
- mockLogger.assertMatchNone([{ eventName: "GarbageCollector:InvalidRelativeOutboundRoute" }], "unexpected events logged");
178
- assert.equal(spies.telemetryTracker.nodeUsed.callCount, 1, "nodeUsed should have been called");
179
- });
180
- });
181
- describe("Tombstone and Sweep", () => {
182
- it("Tombstone then Delete", async () => {
183
- // Simple starting reference graph - root and two nodes
184
- defaultGCData.gcNodes["/"] = [nodes[0], nodes[1]];
185
- defaultGCData.gcNodes[nodes[0]] = [];
186
- defaultGCData.gcNodes[nodes[1]] = [];
187
- // Sweep enabled
188
- gc = createGarbageCollector({ createParams: { gcOptions: { enableGCSweep: true } } });
189
- // These spies will let us monitor how each of these functions are called (or not) during runSweepPhase.
190
- // The original behavior of the function is preserved, but we can check how it was called.
191
- const spies = {
192
- updateTombstonedRoutes: spy(gc.runtime, "updateTombstonedRoutes"),
193
- submitMessage: spy(gc, "submitMessage"),
194
- };
195
- // Nodes 0 and 1 are referenced
196
- await gc.collectGarbage({});
197
- // Unreference 0
198
- defaultGCData.gcNodes["/"] = [nodes[1]];
199
- clock.tick(10);
200
- await gc.collectGarbage({});
201
- // Erase the spy's tracking of calls up to this point - I just want to observe what happens next.
202
- spies.updateTombstonedRoutes.resetHistory();
203
- // Skip to TombstoneReady state
204
- clock.tick(defaultTombstoneTimeoutMs);
205
- await gc.collectGarbage({});
206
- assert(spies.updateTombstonedRoutes.calledWith([nodes[0]]), "updateTombstonedRoutes should be called with node 0");
207
- assert.equal(spies.submitMessage.callCount, 0, "submitMessage should not be called yet, didn't pass Grace Period yet");
208
- spies.updateTombstonedRoutes.resetHistory();
209
- // Skip past Sweep Grace Period. GC Sweep op should be submitted
210
- clock.tick(defaultSweepGracePeriodMs);
211
- await gc.collectGarbage({});
212
- assert.equal(spies.submitMessage.callCount, 1, "submitMessage should be called since Sweep is enabled");
213
- assert(spies.updateTombstonedRoutes.alwaysCalledWith([]), "No additional nodes should be Tombstoned");
214
- });
215
- it("Sweep Disabled - Should Tombstone SweepReady nodes", async () => {
216
- defaultGCData.gcNodes["/"] = [nodes[0], nodes[1]];
217
- defaultGCData.gcNodes[nodes[0]] = [];
218
- defaultGCData.gcNodes[nodes[1]] = [];
219
- gc = createGarbageCollector();
220
- const spies = {
221
- updateTombstonedRoutes: spy(gc.runtime, "updateTombstonedRoutes"),
222
- submitMessage: spy(gc, "submitMessage"),
223
- };
224
- // Nodes 0 and 1 are referenced
225
- await gc.collectGarbage({});
226
- // Unreference 0
227
- defaultGCData.gcNodes["/"] = [nodes[1]];
228
- clock.tick(10);
229
- await gc.collectGarbage({});
230
- // Skip all the way past Sweep Grace Period. But Sweep is disabled, so Tombstone should happen
231
- clock.tick(defaultTombstoneTimeoutMs + defaultSweepGracePeriodMs);
232
- assert.equal(gc.unreferencedNodesState.get(nodes[0])?.state, "SweepReady", "Node 0 should be SweepReady (not TombstoneReady)");
233
- spies.updateTombstonedRoutes.resetHistory();
234
- await gc.collectGarbage({});
235
- assert(spies.updateTombstonedRoutes.calledWith([nodes[0]]), "updateTombstonedRoutes should be called with node 0");
236
- assert.equal(spies.submitMessage.callCount, 0, "submitMessage should not be called since Sweep is disabled");
237
- });
238
- it("Autorecovery upon loading a Tombstoned node", async () => {
239
- // Simple starting reference graph - root and two nodes
240
- defaultGCData.gcNodes["/"] = [nodes[0], nodes[1]];
241
- defaultGCData.gcNodes[nodes[0]] = [];
242
- defaultGCData.gcNodes[nodes[1]] = [];
243
- // Simulate GC Data with a route missing (nodes[0] is referenced but missing here)
244
- // We'll return this from getGCData unless fullGC is passed in.
245
- const corruptedGCData = JSON.parse(JSON.stringify(defaultGCData));
246
- corruptedGCData.gcNodes["/"] = [nodes[1]];
247
- // getGCData set up to sometimes return the corrupted data
248
- gc = createGarbageCollector({
249
- getGCData: async (fullGC) => {
250
- return fullGC ? defaultGCData : corruptedGCData;
251
- },
252
- });
253
- // These spies will let us monitor how each of these functions are called (or not) during runSweepPhase.
254
- // The original behavior of the function is preserved, but we can check how it was called.
255
- const spies = {
256
- gc: {
257
- submitMessage: spy(gc, "submitMessage"),
258
- addedOutboundReference: spy(gc, "addedOutboundReference"),
259
- runGC: spy(gc, "runGC"),
260
- },
261
- };
262
- // Nodes 0 and 1 are referenced (use correct gcData via fullGC true)
263
- // Simulate successful summary ack to clear doesGCStateNeedReset flag so it doesn't interfere (it leads to fullGC too)
264
- await gc.collectGarbage({ fullGC: true });
265
- await gc.summaryStateTracker.refreshLatestSummary({
266
- isSummaryTracked: true,
267
- isSummaryNewer: false,
268
- });
269
- assert(!gc.unreferencedNodesState.has(nodes[0]), "node 0 should not be unreferenced to start");
270
- // Now run GC again - this time with the corrupted data (via fullGC false)
271
- clock.tick(10);
272
- await gc.collectGarbage({ fullGC: false });
273
- assert.equal(gc.unreferencedNodesState.get(nodes[0])?.state, UnreferencedState.Active, // This means recently unreferenced
274
- "node 0 should appear unreferenced after running GC with corrupted GC Data");
275
- // Let node 0 become Tombstoned and verify it
276
- clock.tick(defaultTombstoneTimeoutMs);
277
- await gc.collectGarbage({});
278
- assert.equal(gc.unreferencedNodesState.get(nodes[0])?.state, UnreferencedState.TombstoneReady, "node 0 should be TombstoneReady");
279
- assert.deepEqual(gc.tombstones, [nodes[0]], "node 0 should be in the Tombstones list");
280
- // Simulate usage to trigger TombstoneLoaded op.
281
- gc.nodeUpdated(nodes[0], "Loaded");
282
- const [gcTombstoneLoadedMessage] = spies.gc.submitMessage.args[0];
283
- assert.deepEqual(gcTombstoneLoadedMessage, {
284
- type: ContainerMessageType.GC,
285
- contents: {
286
- type: GarbageCollectionMessageType.TombstoneLoaded,
287
- nodePath: nodes[0],
288
- },
289
- compatDetails: { behavior: "Ignore" },
290
- }, "submitted message not as expected");
291
- // Plumb the message to processMessage fn to trigger autorecovery
292
- // Autorecovery: addedOutboundReference should be called with the tombstoned node, which should transition to "Active" state
293
- gc.processMessage(gcTombstoneLoadedMessage, true /* local */);
294
- assert.deepEqual(spies.gc.addedOutboundReference.args[0], ["/", nodes[0], /* autorecovery: */ true], "addedOutboundReference should be called with node 0");
295
- assert.equal(gc.unreferencedNodesState.get(nodes[0])?.state, UnreferencedState.Active, "node 0 should be unreferenced but 'Active' after initial Autorecovery moment (it was TombstoneReady)");
296
- // Simulate a successful GC/Summary.
297
- // GC Data corruption should be fixed (nodes[0] should be referenced again) and autorecovery fullGC state should be reset
298
- spies.gc.runGC.resetHistory();
299
- await gc.collectGarbage({});
300
- assert(spies.gc.runGC.calledWith(/* fullGC: */ true), "runGC should be called with fullGC true");
301
- assert(gc.summaryStateTracker.fullGCModeForAutoRecovery, "fullGCModeForAutoRecovery should NOT have been reset to false yet");
302
- await gc.summaryStateTracker.refreshLatestSummary({
303
- isSummaryTracked: true,
304
- isSummaryNewer: false,
305
- });
306
- assert(!gc.summaryStateTracker.fullGCModeForAutoRecovery, "fullGCModeForAutoRecovery should have been reset to false now");
307
- // Lastly, confirm that the node was successfully restored
308
- // (not actually that interesting since we're hardcoding the GC Data, but still)
309
- assert(!gc.unreferencedNodesState.has(nodes[0]), "node 0 should not be unreferenced after repairing GC Data");
310
- });
311
- });
312
- describe("errors when unreferenced objects are used after they are inactive / deleted", () => {
313
- // Mock node loaded and changed activity for all the nodes in the graph.
314
- async function mockNodeChangesAndRunGC(garbageCollector) {
315
- nodes.forEach((nodeId) => {
316
- garbageCollector.nodeUpdated(nodeId, "Loaded", Date.now(), testPkgPath);
317
- garbageCollector.nodeUpdated(nodeId, "Changed", Date.now(), testPkgPath);
318
- });
319
- await garbageCollector.collectGarbage({});
320
- }
321
- beforeEach(async () => {
322
- // Set up the reference graph such that all nodes are referenced. Add in a couple of cycles in the graph.
323
- // Here's a diagram showing the references:
324
- // 0 - 1 - 2 - 3
325
- // | / /
326
- // |-/-------/
327
- defaultGCData.gcNodes["/"] = [nodes[0]];
328
- defaultGCData.gcNodes[nodes[0]] = [nodes[1]];
329
- defaultGCData.gcNodes[nodes[1]] = [nodes[0], nodes[2]];
330
- defaultGCData.gcNodes[nodes[2]] = [nodes[3]];
331
- defaultGCData.gcNodes[nodes[3]] = [nodes[0]];
332
- });
333
- const summarizerContainerTests = (timeout, mode, revivedEventName, changedEventName, loadedEventName, sweepGracePeriodMsOverride) => {
334
- // Validates that no unexpected event has been fired.
335
- function validateNoEvents() {
336
- mockLogger.assertMatchNone([
337
- { eventName: revivedEventName },
338
- { eventName: changedEventName },
339
- { eventName: loadedEventName },
340
- ], "unexpected events logged");
341
- }
342
- const sweepGracePeriodMs = sweepGracePeriodMsOverride ?? defaultSweepGracePeriodMs;
343
- const createGCOverride = (baseSnapshot, gcBlobsMap) => {
344
- const tombstoneTimeoutMs = mode === "tombstone"
345
- ? timeout
346
- : mode === "sweep"
347
- ? timeout - sweepGracePeriodMs
348
- : undefined;
349
- const gcOptions = { sweepGracePeriodMs };
350
- return createGarbageCollector({
351
- createParams: { baseSnapshot, gcOptions },
352
- gcBlobsMap,
353
- gcMetadata: {
354
- tombstoneTimeoutMs,
355
- },
356
- });
357
- };
358
- it("doesn't generate events for referenced nodes", async () => {
359
- const garbageCollector = createGCOverride();
360
- // Run garbage collection on the default GC data where everything is referenced.
361
- await garbageCollector.collectGarbage({});
362
- // Advance the clock just before the timeout and validate no events are generated.
363
- clock.tick(timeout - 1);
364
- await mockNodeChangesAndRunGC(garbageCollector);
365
- validateNoEvents();
366
- // Advance the clock to expire the timeout.
367
- clock.tick(1);
368
- // Update all nodes again. Validate that no unexpected events are generated since everything is referenced.
369
- await mockNodeChangesAndRunGC(garbageCollector);
370
- validateNoEvents();
371
- });
372
- it("generates events for nodes that are used after time out", async () => {
373
- const garbageCollector = createGCOverride();
374
- // Remove node 2's reference from node 1. This should make node 2 and node 3 unreferenced.
375
- defaultGCData.gcNodes[nodes[1]] = [];
376
- await garbageCollector.collectGarbage({});
377
- // Advance the clock just before the timeout and validate no unexpected events are logged.
378
- clock.tick(timeout - 1);
379
- await mockNodeChangesAndRunGC(garbageCollector);
380
- validateNoEvents();
381
- // Expire the timeout and validate that all events for node 2 and node 3 are logged.
382
- clock.tick(1);
383
- await mockNodeChangesAndRunGC(garbageCollector);
384
- const expectedEvents = [];
385
- expectedEvents.push({
386
- eventName: loadedEventName,
387
- timeout,
388
- ...tagCodeArtifacts({ id: nodes[2], pkg: testPkgPath.join("/") }),
389
- createContainerRuntimeVersion: pkgVersion,
390
- }, {
391
- eventName: changedEventName,
392
- timeout,
393
- ...tagCodeArtifacts({ id: nodes[2], pkg: testPkgPath.join("/") }),
394
- createContainerRuntimeVersion: pkgVersion,
395
- }, {
396
- eventName: loadedEventName,
397
- timeout,
398
- ...tagCodeArtifacts({ id: nodes[3], pkg: testPkgPath.join("/") }),
399
- createContainerRuntimeVersion: pkgVersion,
400
- }, {
401
- eventName: changedEventName,
402
- timeout,
403
- ...tagCodeArtifacts({ id: nodes[3], pkg: testPkgPath.join("/") }),
404
- createContainerRuntimeVersion: pkgVersion,
405
- });
406
- mockLogger.assertMatch(expectedEvents, "all events not generated as expected", true /* inlineDetailsProp */);
407
- // Add reference from node 1 to node 3 and validate that we get a revived event.
408
- garbageCollector.addedOutboundReference(nodes[1], nodes[3]);
409
- await garbageCollector.collectGarbage({});
410
- mockLogger.assertMatch([
411
- {
412
- eventName: revivedEventName,
413
- timeout,
414
- ...tagCodeArtifacts({
415
- id: nodes[3],
416
- fromId: nodes[1],
417
- pkg: testPkgPath.join("/"),
418
- }),
419
- },
420
- ], "revived event not generated as expected", true /* inlineDetailsProp */);
421
- });
422
- it("generates only revived event when an inactive node is changed and revived", async () => {
423
- const garbageCollector = createGCOverride();
424
- // Remove node 2's reference from node 1. This should make node 2 and node 3 unreferenced.
425
- defaultGCData.gcNodes[nodes[1]] = [];
426
- await garbageCollector.collectGarbage({});
427
- // Advance the clock just before the timeout and validate no unexpected events are logged.
428
- clock.tick(timeout - 1);
429
- await mockNodeChangesAndRunGC(garbageCollector);
430
- validateNoEvents();
431
- // Expire the timeout and validate that only revived event is generated for node 2.
432
- clock.tick(1);
433
- garbageCollector.nodeUpdated(nodes[2], "Changed", Date.now(), testPkgPath);
434
- garbageCollector.nodeUpdated(nodes[2], "Loaded", Date.now(), testPkgPath);
435
- garbageCollector.addedOutboundReference(nodes[1], nodes[2]);
436
- await garbageCollector.collectGarbage({});
437
- for (const event of mockLogger.events) {
438
- assert.notStrictEqual(event.eventName, loadedEventName, "Unexpected loaded event logged");
439
- assert.notStrictEqual(event.eventName, changedEventName, "Unexpected changed event logged");
440
- }
441
- mockLogger.assertMatch([
442
- {
443
- eventName: revivedEventName,
444
- timeout,
445
- ...tagCodeArtifacts({
446
- id: nodes[2],
447
- fromId: nodes[1],
448
- pkg: testPkgPath.join("/"),
449
- }),
450
- },
451
- ], "revived event not logged as expected", true /* inlineDetailsProp */);
452
- });
453
- it("generates events once per node", async () => {
454
- const garbageCollector = createGCOverride();
455
- // Remove node 3's reference from node 2.
456
- defaultGCData.gcNodes[nodes[2]] = [];
457
- await garbageCollector.collectGarbage({});
458
- // Advance the clock just before the timeout and validate no unexpected events are logged.
459
- clock.tick(timeout - 1);
460
- await mockNodeChangesAndRunGC(garbageCollector);
461
- validateNoEvents();
462
- // Expire the timeout and validate that all events for node 2 and node 3 are logged.
463
- clock.tick(1);
464
- await mockNodeChangesAndRunGC(garbageCollector);
465
- const expectedEvents = [];
466
- expectedEvents.push({
467
- eventName: loadedEventName,
468
- timeout,
469
- ...tagCodeArtifacts({ id: nodes[3], pkg: testPkgPath.join("/") }),
470
- }, {
471
- eventName: changedEventName,
472
- timeout,
473
- ...tagCodeArtifacts({ id: nodes[3], pkg: testPkgPath.join("/") }),
474
- });
475
- mockLogger.assertMatch(expectedEvents, "all events not generated as expected", true /* inlineDetailsProp */);
476
- // Update all nodes again. There shouldn't be any more events since for each node the event is only once.
477
- await mockNodeChangesAndRunGC(garbageCollector);
478
- validateNoEvents();
479
- });
480
- /**
481
- * Here, the base snapshot contains nodes that have timed out. The test validates that we generate errors
482
- * when these nodes are used.
483
- */
484
- it("generates events for nodes that time out on load", async () => {
485
- // Create GC state where node 3's unreferenced time was > timeout ms ago.
486
- // This means this node should time out as soon as its data is loaded.
487
- // Create a snapshot tree to be used as the GC snapshot tree.
488
- const gcSnapshotTree = getDummySnapshotTree();
489
- const gcBlobId = "root";
490
- // Add a GC blob with key that start with `gcBlobPrefix` to the GC snapshot tree. The blob Id for this
491
- // is generated by server in real scenarios but we use a static id here for testing.
492
- gcSnapshotTree.blobs[`${gcBlobPrefix}_${gcBlobId}`] = gcBlobId;
493
- // Create a base snapshot that contains the GC snapshot tree.
494
- const baseSnapshot = getDummySnapshotTree();
495
- baseSnapshot.trees[gcTreeKey] = gcSnapshotTree;
496
- // Create GC state with node 3 expired. This will be returned when the garbage collector asks
497
- // for the GC blob with `gcBlobId`.
498
- const gcState = { gcNodes: {} };
499
- const node3Data = {
500
- outboundRoutes: [],
501
- unreferencedTimestampMs: Date.now() - (timeout + 100),
502
- };
503
- gcState.gcNodes[nodes[3]] = node3Data;
504
- const gcBlobMap = new Map([
505
- [gcBlobId, gcState],
506
- ]);
507
- const garbageCollector = createGCOverride(baseSnapshot, gcBlobMap);
508
- // Remove node 3's reference from node 2 so that it is still unreferenced.
509
- defaultGCData.gcNodes[nodes[2]] = [];
510
- // Run GC to trigger loading the GC details from the base summary. Will also generate Delete logs
511
- await garbageCollector.collectGarbage({});
512
- // Validate that all events are logged as expected.
513
- garbageCollector.nodeUpdated(nodes[3], "Loaded", Date.now(), testPkgPath);
514
- garbageCollector.nodeUpdated(nodes[3], "Changed", Date.now(), testPkgPath);
515
- await garbageCollector.collectGarbage({});
516
- mockLogger.assertMatch([
517
- {
518
- eventName: loadedEventName,
519
- timeout,
520
- ...tagCodeArtifacts({ id: nodes[3], pkg: testPkgPath.join("/") }),
521
- },
522
- {
523
- eventName: changedEventName,
524
- timeout,
525
- ...tagCodeArtifacts({ id: nodes[3], pkg: testPkgPath.join("/") }),
526
- },
527
- ], "all events not generated as expected", true /* inlineDetailsProp */);
528
- // Add reference from node 2 to node 3 and validate that revived event is logged.
529
- defaultGCData.gcNodes[nodes[2]] = [nodes[3]];
530
- garbageCollector.addedOutboundReference(nodes[2], nodes[3]);
531
- await garbageCollector.collectGarbage({});
532
- mockLogger.assertMatch([
533
- {
534
- eventName: revivedEventName,
535
- timeout,
536
- ...tagCodeArtifacts({
537
- id: nodes[3],
538
- fromId: nodes[2],
539
- pkg: testPkgPath.join("/"),
540
- }),
541
- },
542
- ], "revived event not generated as expected", true /* inlineDetailsProp */);
543
- });
544
- /**
545
- * Here, the base snapshot contains nodes that have timed out. The test validates that we generate errors
546
- * when these nodes are used via trailing ops. It simulates trailing ops by calling `nodeUsed` (called when
547
- * ops are processed) before initializing garbage collector.
548
- */
549
- it("generates events for nodes that time out on load - nodes are used via trailing ops", async () => {
550
- // Create GC state where node 3's unreferenced time was > timeout ms ago.
551
- // This means this node should time out as soon as its data is loaded.
552
- // Create a snapshot tree to be used as the GC snapshot tree.
553
- const gcSnapshotTree = getDummySnapshotTree();
554
- const gcBlobId = "root";
555
- // Add a GC blob with key that start with `gcBlobPrefix` to the GC snapshot tree. The blob Id for this
556
- // is generated by server in real scenarios but we use a static id here for testing.
557
- gcSnapshotTree.blobs[`${gcBlobPrefix}_${gcBlobId}`] = gcBlobId;
558
- // Create a base snapshot that contains the GC snapshot tree.
559
- const baseSnapshot = getDummySnapshotTree();
560
- baseSnapshot.trees[gcTreeKey] = gcSnapshotTree;
561
- // Create GC state with node 3 expired. This will be returned when the garbage collector asks
562
- // for the GC blob with `gcBlobId`.
563
- const gcState = { gcNodes: {} };
564
- const node3Data = {
565
- outboundRoutes: [],
566
- unreferencedTimestampMs: Date.now() - (timeout + 100),
567
- };
568
- gcState.gcNodes[nodes[3]] = node3Data;
569
- const gcBlobMap = new Map([
570
- [gcBlobId, gcState],
571
- ]);
572
- const garbageCollector = createGCOverride(baseSnapshot, gcBlobMap);
573
- // Initialize the base state which happens during runtime initialization.
574
- await garbageCollector.initializeBaseState();
575
- // Simulate sending op and loading the node in unreferenced state.
576
- garbageCollector.nodeUpdated(nodes[3], "Loaded", Date.now(), testPkgPath);
577
- garbageCollector.nodeUpdated(nodes[3], "Changed", Date.now(), testPkgPath);
578
- // Log pending events explicitly. This is needed because summarizer clients don't log these events
579
- // until the next GC run which calls this function.
580
- await garbageCollector.telemetryTracker.logPendingEvents(garbageCollector.mc.logger);
581
- mockLogger.assertMatch([
582
- {
583
- eventName: loadedEventName,
584
- timeout,
585
- ...tagCodeArtifacts({ id: nodes[3], pkg: testPkgPath.join("/") }),
586
- },
587
- {
588
- eventName: changedEventName,
589
- timeout,
590
- ...tagCodeArtifacts({ id: nodes[3], pkg: testPkgPath.join("/") }),
591
- },
592
- ], "all events not generated as expected", true /* inlineDetailsProp */);
593
- // Add reference from node 2 to node 3 and validate that revived event is logged. Node 3 needs to be
594
- // deleted from unreferencedNodesState otherwise revived events are not logged.
595
- garbageCollector.addedOutboundReference(nodes[2], nodes[3]);
596
- garbageCollector.unreferencedNodesState.delete(nodes[3]);
597
- await garbageCollector.telemetryTracker.logPendingEvents(garbageCollector.mc.logger);
598
- mockLogger.assertMatch([
599
- {
600
- eventName: revivedEventName,
601
- timeout,
602
- ...tagCodeArtifacts({
603
- id: nodes[3],
604
- fromId: nodes[2],
605
- pkg: testPkgPath.join("/"),
606
- }),
607
- },
608
- ], "revived event not generated as expected", true /* inlineDetailsProp */);
609
- });
610
- /**
611
- * Here, the base snapshot contains nodes that have timed out and the GC blob in snapshot is in old format. The
612
- * test validates that we generate errors when these nodes are used.
613
- */
614
- it("generates events for nodes that time out on load - old snapshot format", async () => {
615
- // Create GC details for node 3's GC blob whose unreferenced time was > timeout ms ago.
616
- // This means this node should time out as soon as its data is loaded.
617
- const node3GCDetails = {
618
- gcData: { gcNodes: { "/": [] } },
619
- unrefTimestamp: Date.now() - (timeout + 100),
620
- };
621
- const node3Snapshot = getDummySnapshotTree();
622
- const gcBlobId = "node3GCDetails";
623
- const attributesBlobId = "attributesBlob";
624
- node3Snapshot.blobs[gcTreeKey] = gcBlobId;
625
- node3Snapshot.blobs[dataStoreAttributesBlobName] = attributesBlobId;
626
- // Create a base snapshot that contains snapshot tree of node 3.
627
- const channelsTree = getDummySnapshotTree();
628
- channelsTree.trees[nodes[3].slice(1)] = node3Snapshot;
629
- const baseSnapshot = getDummySnapshotTree();
630
- baseSnapshot.trees[channelsTreeName] = channelsTree;
631
- // Set up the getNodeGCDetails function to return the GC details for node 3 when asked by garbage collector.
632
- const gcBlobMap = new Map([
633
- [gcBlobId, node3GCDetails],
634
- [attributesBlobId, {}],
635
- ]);
636
- const garbageCollector = createGCOverride(baseSnapshot, gcBlobMap);
637
- // Remove node 3's reference from node 2 so that it is still unreferenced. The GC details from the base
638
- // summary is not loaded until the first time GC is run, so do that immediately.
639
- defaultGCData.gcNodes[nodes[2]] = [];
640
- await garbageCollector.collectGarbage({});
641
- // Validate that no events are generated since none of the timeouts have passed
642
- garbageCollector.nodeUpdated(nodes[3], "Loaded", Date.now(), testPkgPath);
643
- garbageCollector.nodeUpdated(nodes[3], "Changed", Date.now(), testPkgPath);
644
- await garbageCollector.collectGarbage({});
645
- mockLogger.assertMatchNone([
646
- {
647
- eventName: loadedEventName,
648
- timeout,
649
- ...tagCodeArtifacts({ id: nodes[3], pkg: testPkgPath.join("/") }),
650
- },
651
- {
652
- eventName: changedEventName,
653
- timeout,
654
- ...tagCodeArtifacts({ id: nodes[3], pkg: testPkgPath.join("/") }),
655
- },
656
- ], "all events not generated as expected", true /* inlineDetailsProp */);
657
- // No revived events should be logged as no timeouts should have occurred
658
- garbageCollector.addedOutboundReference(nodes[2], nodes[3]);
659
- await garbageCollector.collectGarbage({});
660
- mockLogger.assertMatchNone([
661
- {
662
- eventName: revivedEventName,
663
- timeout,
664
- ...tagCodeArtifacts({
665
- id: nodes[3],
666
- fromId: nodes[2],
667
- pkg: testPkgPath.join("/"),
668
- }),
669
- },
670
- ], "revived event not generated as expected", true /* inlineDetailsProp */);
671
- });
672
- /**
673
- * Here, the base snapshot contains nodes that have timed out and the GC data in snapshot is present in multiple
674
- * blobs. The test validates that we generate errors when these nodes are used.
675
- */
676
- it(`generates events for nodes that time out on load - multi blob GC data`, async () => {
677
- const gcBlobMap = new Map();
678
- const expiredTimestampMs = Date.now() - (timeout + 100);
679
- // Create three GC states to be added into separate GC blobs. Each GC state has a node whose unreferenced
680
- // time was > timeout ms ago. These three GC blobs are the added to the GC tree in summary.
681
- const blob1Id = "blob1";
682
- const blob1GCState = { gcNodes: {} };
683
- blob1GCState.gcNodes[nodes[1]] = {
684
- outboundRoutes: [],
685
- unreferencedTimestampMs: expiredTimestampMs,
686
- };
687
- gcBlobMap.set(blob1Id, blob1GCState);
688
- const blob2Id = "blob2";
689
- const blob2GCState = { gcNodes: {} };
690
- blob2GCState.gcNodes[nodes[2]] = {
691
- outboundRoutes: [],
692
- unreferencedTimestampMs: expiredTimestampMs,
693
- };
694
- gcBlobMap.set(blob2Id, blob2GCState);
695
- const blob3Id = "blob3";
696
- const blob3GCState = { gcNodes: {} };
697
- blob3GCState.gcNodes[nodes[3]] = {
698
- outboundRoutes: [],
699
- unreferencedTimestampMs: expiredTimestampMs,
700
- };
701
- gcBlobMap.set(blob3Id, blob3GCState);
702
- // Create a GC snapshot tree and add the above three GC blob ids to it.
703
- const gcSnapshotTree = getDummySnapshotTree();
704
- gcSnapshotTree.blobs[`${gcBlobPrefix}_${blob1Id}`] = blob1Id;
705
- gcSnapshotTree.blobs[`${gcBlobPrefix}_${blob2Id}`] = blob2Id;
706
- gcSnapshotTree.blobs[`${gcBlobPrefix}_${blob3Id}`] = blob3Id;
707
- // Create a base snapshot that contains the above GC snapshot tree.
708
- const baseSnapshot = getDummySnapshotTree();
709
- baseSnapshot.trees[gcTreeKey] = gcSnapshotTree;
710
- const garbageCollector = createGCOverride(baseSnapshot, gcBlobMap);
711
- // For the nodes in the GC snapshot blobs, remove their references from the default GC data.
712
- defaultGCData.gcNodes[nodes[0]] = [];
713
- defaultGCData.gcNodes[nodes[1]] = [];
714
- defaultGCData.gcNodes[nodes[2]] = [];
715
- await garbageCollector.collectGarbage({});
716
- // Validate that all events are logged as expected.
717
- garbageCollector.nodeUpdated(nodes[3], "Loaded", Date.now(), testPkgPath);
718
- garbageCollector.nodeUpdated(nodes[1], "Changed", Date.now(), testPkgPath);
719
- garbageCollector.nodeUpdated(nodes[2], "Changed", Date.now(), testPkgPath);
720
- await garbageCollector.collectGarbage({});
721
- mockLogger.assertMatch([
722
- {
723
- eventName: loadedEventName,
724
- timeout,
725
- ...tagCodeArtifacts({ id: nodes[3], pkg: testPkgPath.join("/") }),
726
- },
727
- {
728
- eventName: changedEventName,
729
- timeout,
730
- ...tagCodeArtifacts({ id: nodes[1], pkg: testPkgPath.join("/") }),
731
- },
732
- {
733
- eventName: changedEventName,
734
- timeout,
735
- ...tagCodeArtifacts({ id: nodes[2], pkg: testPkgPath.join("/") }),
736
- },
737
- ], "all events not generated as expected", true /* inlineDetailsProp */);
738
- });
739
- };
740
- describe("Inactive events (summarizer container)", () => {
741
- const inactiveTimeoutMs = 500;
742
- beforeEach(() => {
743
- configProvider.set("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs", inactiveTimeoutMs);
744
- });
745
- summarizerContainerTests(inactiveTimeoutMs, "inactive", "GarbageCollector:InactiveObject_Revived", "GarbageCollector:InactiveObject_Changed", "GarbageCollector:InactiveObject_Loaded");
746
- });
747
- describe("TombstoneReady events (summarizer container)", () => {
748
- summarizerContainerTests(defaultTombstoneTimeoutMs, "tombstone", "GarbageCollector:TombstoneReadyObject_Revived", "GarbageCollector:TombstoneReadyObject_Changed", "GarbageCollector:TombstoneReadyObject_Loaded");
749
- });
750
- describe("SweepReady events - No sweepGracePeriodMs (summarizer container)", () => {
751
- summarizerContainerTests(defaultTombstoneTimeoutMs, "sweep", // Jump straight to SweepReady given 0 delay
752
- "GarbageCollector:SweepReadyObject_Revived", "GarbageCollector:SweepReadyObject_Changed", "GarbageCollector:SweepReadyObject_Loaded", 0 /* sweepGracePeriodMsOverride */);
753
- });
754
- describe("SweepReady events - with sweepGracePeriodMs delay (summarizer container)", () => {
755
- summarizerContainerTests(defaultTombstoneTimeoutMs + defaultSweepGracePeriodMs, "sweep", "GarbageCollector:SweepReadyObject_Revived", "GarbageCollector:SweepReadyObject_Changed", "GarbageCollector:SweepReadyObject_Loaded");
756
- });
757
- describe("GC version changes", () => {
758
- function getSnapshotWithGCVersion(gcVersion) {
759
- // Create a snapshot tree to be used as the GC snapshot tree.
760
- const gcSnapshotTree = getDummySnapshotTree();
761
- const gcBlobId = "root";
762
- // Add a GC blob with key that start with `blob` to the GC snapshot tree. The blob Id for this
763
- // is generated by server in real scenarios but we use a static id here for testing.
764
- gcSnapshotTree.blobs[`${gcBlobPrefix}_${gcBlobId}`] = gcBlobId;
765
- // Create GC state with a node. This will be returned when the garbage collector asks for the GC blob
766
- // with `gcBlobId`.
767
- const gcState = { gcNodes: {} };
768
- const nodeData = {
769
- outboundRoutes: [],
770
- unreferencedTimestampMs: 123,
771
- };
772
- gcState.gcNodes[nodes[0]] = nodeData;
773
- // Create a tombstone blob. This will be returned when the garbage collector asks for tombstone blob.
774
- const gcTombstoneBlobId = "tombstone";
775
- gcSnapshotTree.blobs[gcTombstoneBlobKey] = gcTombstoneBlobId;
776
- const tombstones = [nodes[0]];
777
- // Create a deleted nodes blob. This will be returned when the garbage collector asks for deleted
778
- // nodes blob.
779
- const gcDeletedBlobId = "deletedNodes";
780
- gcSnapshotTree.blobs[gcDeletedBlobKey] = gcDeletedBlobId;
781
- const deletedBlobs = [nodes[0]];
782
- // Create a snapshot that contains the GC snapshot tree.
783
- const snapshotTree = getDummySnapshotTree();
784
- snapshotTree.trees[gcTreeKey] = gcSnapshotTree;
785
- const metadataBlobId = "metadata";
786
- const metadata = {
787
- gcFeature: gcVersion,
788
- summaryFormatVersion: 1,
789
- message: undefined,
790
- };
791
- snapshotTree.blobs[metadataBlobName] = metadataBlobId;
792
- const gcBlobsMap = new Map();
793
- gcBlobsMap.set(gcBlobId, gcState);
794
- gcBlobsMap.set(gcTombstoneBlobId, tombstones);
795
- gcBlobsMap.set(gcDeletedBlobId, deletedBlobs);
796
- gcBlobsMap.set(metadataBlobId, metadata);
797
- return { snapshotTree, gcBlobsMap };
798
- }
799
- function createGCOverride(gcFeature) {
800
- const gcMetadata = {
801
- gcFeature,
802
- };
803
- const { snapshotTree, gcBlobsMap } = getSnapshotWithGCVersion(gcFeature);
804
- return createGarbageCollector({
805
- createParams: { baseSnapshot: snapshotTree },
806
- gcBlobsMap,
807
- gcMetadata,
808
- });
809
- }
810
- it("reads all GC data from base snapshot when GC version does not change", async () => {
811
- const garbageCollector = createGCOverride(stableGCVersion);
812
- // GC state, tombstone state and deleted nodes should all be read from base snapshot.
813
- const baseSnapshotData = await garbageCollector.baseSnapshotDataP;
814
- assert(baseSnapshotData !== undefined, "base snapshot was not initialized correctly");
815
- assert(baseSnapshotData.gcState !== undefined, "GC state in base snapshot should not be available");
816
- assert(baseSnapshotData.tombstones !== undefined, "Tombstone state in base snapshot should be available");
817
- assert(baseSnapshotData.deletedNodes !== undefined, "Deleted nodes in base snapshot should be available");
818
- // Initialize from the base state and validate that tombstones and deleted state both have one entry
819
- // as per the base snapshot.
820
- await garbageCollector.initializeBaseState();
821
- assert.strictEqual(garbageCollector.tombstones.length, 1, "Expecting 1 tombstone node");
822
- assert.strictEqual(garbageCollector.deletedNodes.size, 1, "Expecting 1 deleted node");
823
- });
824
- it("discards GC state and tombstone state in base snapshot when GC version changes", async () => {
825
- const garbageCollector = createGCOverride(stableGCVersion + 1);
826
- // GC state and tombstone state should be discarded but deleted nodes should be read from base snapshot.
827
- const baseSnapshotData = await garbageCollector.baseSnapshotDataP;
828
- assert(baseSnapshotData !== undefined, "base snapshot was not initialized correctly");
829
- assert(baseSnapshotData.gcState === undefined, "GC state in base snapshot should be undefined when GC version changes");
830
- assert(baseSnapshotData.tombstones === undefined, "Tombstone state in base snapshot should be undefined when GC version changes");
831
- assert(baseSnapshotData.deletedNodes !== undefined, "Deleted nodes in base snapshot should be available");
832
- // Initialize from the base state and validate that tombstones has 0 entry because it was discarded.
833
- // Deleted nodes should have one entry because it is still used.
834
- await garbageCollector.initializeBaseState();
835
- assert.strictEqual(garbageCollector.tombstones.length, 0, "Expecting no tombstone nodes");
836
- assert.strictEqual(garbageCollector.deletedNodes.size, 1, "Expecting 1 deleted node");
837
- });
838
- });
839
- it("Unreferenced nodes transition through Inactive, TombstoneReady and SweepReady states", async () => {
840
- const inactiveTimeoutMs = 500;
841
- configProvider.set("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs", inactiveTimeoutMs);
842
- const garbageCollector = createGarbageCollector({});
843
- function validateUnreferencedStates(expectedUnreferencedStates) {
844
- // Base assumption is that all 6 nodes are still referenced (no state tracker aka 'undefined')
845
- // Then update this with the given expected unreferenced states
846
- const expectedStates = Object.assign([undefined, undefined, undefined, undefined, undefined, undefined], expectedUnreferencedStates);
847
- for (const [id, state] of expectedStates.entries()) {
848
- assert.equal(garbageCollector.unreferencedNodesState.get(nodes[id])?.state, state, `node ${id} should be ${state ?? "referenced"}`);
849
- }
850
- }
851
- // Remove node 2's reference from node 1. This should make node 2 and node 3 unreferenced.
852
- defaultGCData.gcNodes[nodes[1]] = [];
853
- await garbageCollector.collectGarbage({});
854
- // Advance the clock to trigger inactive timeout and validate that we get inactive events.
855
- clock.tick(inactiveTimeoutMs + 1);
856
- await garbageCollector.collectGarbage({});
857
- validateUnreferencedStates({ 2: "Inactive", 3: "Inactive" });
858
- // Advance the clock to trigger tombstoneTimeoutMs and validate that we get tombstone ready events.
859
- clock.tick(defaultTombstoneTimeoutMs - inactiveTimeoutMs);
860
- await garbageCollector.collectGarbage({});
861
- validateUnreferencedStates({ 2: "TombstoneReady", 3: "TombstoneReady" });
862
- // Advance the clock the sweep delay and validate that we get sweep ready events.
863
- clock.tick(defaultSweepGracePeriodMs);
864
- await garbageCollector.collectGarbage({});
865
- validateUnreferencedStates({ 2: "SweepReady", 3: "SweepReady" });
866
- });
867
- });
868
- describe("Deleted blobs in GC summary tree", () => {
869
- it("correctly reads and write deleted blobs in summary", async () => {
870
- // Set up the GC reference graph to have something to work with.
871
- defaultGCData.gcNodes["/"] = [nodes[0]];
872
- // Create a snapshot tree to be used as the GC snapshot tree.
873
- const gcSnapshotTree = getDummySnapshotTree();
874
- // Add a blob to the tree for deleted nodes.
875
- const deletedNodesBlobId = "deletedNodes";
876
- gcSnapshotTree.blobs[gcDeletedBlobKey] = deletedNodesBlobId;
877
- const deletedNodeIds = [...nodes];
878
- // Add deleted nodes list the blobs map that will service read and parse blob calls from GC.
879
- const gcBlobsMap = new Map([
880
- [deletedNodesBlobId, deletedNodeIds],
881
- ]);
882
- // Create a base snapshot that contains the GC snapshot tree.
883
- const baseSnapshot = getDummySnapshotTree();
884
- baseSnapshot.trees[gcTreeKey] = gcSnapshotTree;
885
- // Create and initialize garbage collector.
886
- const garbageCollector = createGarbageCollector({
887
- createParams: { baseSnapshot },
888
- gcBlobsMap,
889
- });
890
- await garbageCollector.initializeBaseState();
891
- // The nodes in deletedNodeIds should be marked as deleted.
892
- for (const nodeId of deletedNodeIds) {
893
- assert(garbageCollector.isNodeDeleted(nodeId) === true, `${nodeId} should be marked deleted`);
894
- }
895
- await garbageCollector.collectGarbage({});
896
- // Summarize with fullTree as true so that the deleted nodes are written as a blob and can be validated.
897
- const summaryTree = garbageCollector.summarize(true /* fullTree */, true /* trackState */);
898
- assert(summaryTree?.summary.type === SummaryType.Tree, "The summary should be a tree");
899
- // Get the deleted node ids from summary and validate that its the same as the one GC loaded from.
900
- const deletedNodesBlob = summaryTree.summary.tree[gcDeletedBlobKey];
901
- assert(deletedNodesBlob.type === SummaryType.Blob, "Deleted blob not present in summary");
902
- const deletedNodeIdsInSummary = JSON.parse(deletedNodesBlob.content);
903
- assert.deepStrictEqual(deletedNodeIdsInSummary, deletedNodeIds, "Unexpected deleted nodes in summary");
904
- });
905
- it("writes handle for deleted blobs when its unchanged", async () => {
906
- // Set up the GC reference graph to have something to work with.
907
- defaultGCData.gcNodes["/"] = [nodes[0]];
908
- // Create a snapshot tree to be used as the GC snapshot tree.
909
- const gcSnapshotTree = getDummySnapshotTree();
910
- // Add a blob to the tree for deleted nodes.
911
- const deletedNodesBlobId = "deletedNodes";
912
- gcSnapshotTree.blobs[gcDeletedBlobKey] = deletedNodesBlobId;
913
- const deletedNodeIds = [...nodes];
914
- // Add deleted nodes list the blobs map that will service read and parse blob calls from GC.
915
- const gcBlobsMap = new Map([
916
- [deletedNodesBlobId, deletedNodeIds],
917
- ]);
918
- // Create a base snapshot that contains the GC snapshot tree.
919
- const baseSnapshot = getDummySnapshotTree();
920
- baseSnapshot.trees[gcTreeKey] = gcSnapshotTree;
921
- // Create and initialize garbage collector.
922
- const garbageCollector = createGarbageCollector({
923
- createParams: { baseSnapshot },
924
- gcBlobsMap,
925
- });
926
- await garbageCollector.initializeBaseState();
927
- // Run GC and summarize. The summary should contain the deleted nodes.
928
- await garbageCollector.collectGarbage({});
929
- const gcSummary = garbageCollector.summarize(false /* fullTree */, true /* trackState */);
930
- assert(gcSummary?.summary.type === SummaryType.Tree, "The summary should be a tree");
931
- // Get the deleted node ids from summary and validate that its the same as the one GC loaded from.
932
- const deletedNodesBlob = gcSummary.summary.tree[gcDeletedBlobKey];
933
- assert(deletedNodesBlob.type === SummaryType.Handle, "Deleted nodes state should be a handle");
934
- await garbageCollector.refreshLatestSummary({
935
- isSummaryTracked: true,
936
- isSummaryNewer: true,
937
- });
938
- // Run GC and summarize again. The whole GC summary should now be a summary handle.
939
- await garbageCollector.collectGarbage({});
940
- const gcSummary2 = garbageCollector.summarize(false /* fullTree */, true /* trackState */);
941
- assert(gcSummary2?.summary.type === SummaryType.Handle, "The summary should be a handle");
942
- });
943
- });
944
- describe("GC completed runs", () => {
945
- const gcEndEvent = "GarbageCollector:GarbageCollection_end";
946
- it("increments GC completed runs in logged events correctly", async () => {
947
- const garbageCollector = createGarbageCollector();
948
- await garbageCollector.collectGarbage({});
949
- mockLogger.assertMatch([{ eventName: gcEndEvent, completedGCRuns: 0 }], "completedGCRuns should be 0 since this event was logged before first GC run completed");
950
- await garbageCollector.collectGarbage({});
951
- mockLogger.assertMatch([{ eventName: gcEndEvent, completedGCRuns: 1 }], "completedGCRuns should be 1 since this event was logged after first GC run completed");
952
- await garbageCollector.collectGarbage({});
953
- mockLogger.assertMatch([{ eventName: gcEndEvent, completedGCRuns: 2 }], "completedGCRuns should be 2 since this event was logged after second GC run completed");
954
- // The GC run count should reset for new garbage collector.
955
- const garbageCollector2 = createGarbageCollector();
956
- await garbageCollector2.collectGarbage({});
957
- mockLogger.assertMatch([{ eventName: gcEndEvent, completedGCRuns: 0 }], "completedGCRuns should be 0 since this event was logged before first GC run in new garbage collector");
958
- });
959
- });
960
- /*
961
- * These tests validate scenarios where nodes that are referenced between summaries have their unreferenced
962
- * timestamp updated. These scenarios fall into the following categories:
963
- * 1. Nodes transition from unreferenced -> referenced -> unreferenced between 2 summaries - In these scenarios
964
- * when GC runs, it should detect that the node was referenced and update its unreferenced timestamp.
965
- * 2. Unreferenced nodes are referenced from other unreferenced nodes - In this case, even though the node remains
966
- * unreferenced, its unreferenced timestamp should be updated.
967
- *
968
- * In these tests, V = nodes and E = edges between nodes. Root nodes that are always referenced are marked as *.
969
- */
970
- describe("References between summaries", () => {
971
- let garbageCollector;
972
- const nodeA = "/A";
973
- const nodeB = "/B";
974
- const nodeC = "/C";
975
- const nodeD = "/D";
976
- const nodeE = "/A/E";
977
- // Runs GC and returns the unreferenced timestamps of all nodes in the GC summary.
978
- async function getUnreferencedTimestamps() {
979
- // Advance the clock by 1 tick so that the unreferenced timestamp is updated in between runs.
980
- clock.tick(1);
981
- await garbageCollector.collectGarbage({});
982
- const summaryTree = garbageCollector.summarize(true, false)?.summary;
983
- assert(summaryTree !== undefined, "Nothing to summarize after running GC");
984
- assert(summaryTree.type === SummaryType.Tree, "Expecting a summary tree!");
985
- let rootGCState = { gcNodes: {} };
986
- for (const key of Object.keys(summaryTree.tree)) {
987
- // Skip blobs that do not start with the GC prefix.
988
- if (!key.startsWith(gcBlobPrefix)) {
989
- continue;
990
- }
991
- const gcBlob = summaryTree.tree[key];
992
- assert(gcBlob?.type === SummaryType.Blob, `GC blob not available`);
993
- const gcState = JSON.parse(gcBlob.content);
994
- // Merge the GC state of this blob into the root GC state.
995
- rootGCState = concatGarbageCollectionStates(rootGCState, gcState);
996
- }
997
- const nodeTimestamps = new Map();
998
- for (const [nodeId, nodeData] of Object.entries(rootGCState.gcNodes)) {
999
- nodeTimestamps.set(nodeId, nodeData.unreferencedTimestampMs);
1000
- }
1001
- return nodeTimestamps;
1002
- }
1003
- beforeEach(() => {
1004
- defaultGCData.gcNodes = {};
1005
- garbageCollector = createGarbageCollector();
1006
- });
1007
- describe("Nodes transitioning from unreferenced -> referenced -> unreferenced", () => {
1008
- /**
1009
- * Validates that we can detect references that were added and then removed.
1010
- * 1. Summary 1 at t1. V = [A*, B]. E = []. B has unreferenced time t1.
1011
- * 2. Reference from A to B added. E = [A -\> B].
1012
- * 3. Reference from A to B removed. E = [].
1013
- * 4. Summary 2 at t2. V = [A*, B]. E = []. B has unreferenced time t2.
1014
- * Validates that the unreferenced time for B is t2 which is \> t1.
1015
- */
1016
- it(`Scenario 1 - Reference added and then removed`, async () => {
1017
- // Initialize nodes A and B.
1018
- defaultGCData.gcNodes["/"] = [nodeA];
1019
- defaultGCData.gcNodes[nodeA] = [];
1020
- defaultGCData.gcNodes[nodeB] = [];
1021
- // 1. Run GC and generate summary 1. E = [].
1022
- const timestamps1 = await getUnreferencedTimestamps();
1023
- assert(timestamps1.get(nodeA) === undefined, "A should be referenced");
1024
- const nodeBTime1 = timestamps1.get(nodeB);
1025
- assert(nodeBTime1 !== undefined, "B should have unreferenced timestamp");
1026
- // 2. Add reference from A to B. E = [A -\> B].
1027
- garbageCollector.addedOutboundReference(nodeA, nodeB);
1028
- defaultGCData.gcNodes[nodeA] = [nodeB];
1029
- // 3. Remove reference from A to B. E = [].
1030
- defaultGCData.gcNodes[nodeA] = [];
1031
- // 4. Run GC and generate summary 2. E = [].
1032
- const timestamps2 = await getUnreferencedTimestamps();
1033
- assert(timestamps2.get(nodeA) === undefined, "A should be referenced");
1034
- const nodeBTime2 = timestamps2.get(nodeB);
1035
- assert(nodeBTime2 !== undefined && nodeBTime2 > nodeBTime1, "B's timestamp should have updated");
1036
- });
1037
- /**
1038
- * Validates that we can detect references that were added transitively and then removed.
1039
- * 1. Summary 1 at t1. V = [A*, B, C]. E = [B -\> C]. B and C have unreferenced time t2.
1040
- * 2. Reference from A to B added. E = [A -\> B, B -\> C].
1041
- * 3. Reference from B to C removed. E = [A -\> B].
1042
- * 4. Reference from A to B removed. E = [].
1043
- * 5. Summary 2 at t2. V = [A*, B, C]. E = []. B and C have unreferenced time t2.
1044
- * Validates that the unreferenced time for B and C is t2 which is \> t1.
1045
- */
1046
- it(`Scenario 2 - Reference transitively added and removed`, async () => {
1047
- // Initialize nodes A, B and C.
1048
- defaultGCData.gcNodes["/"] = [nodeA];
1049
- defaultGCData.gcNodes[nodeA] = [];
1050
- defaultGCData.gcNodes[nodeB] = [nodeC];
1051
- defaultGCData.gcNodes[nodeC] = [];
1052
- // 1. Run GC and generate summary 1. E = [B -\> C].
1053
- const timestamps1 = await getUnreferencedTimestamps();
1054
- assert(timestamps1.get(nodeA) === undefined, "A should be referenced");
1055
- const nodeBTime1 = timestamps1.get(nodeB);
1056
- const nodeCTime1 = timestamps1.get(nodeC);
1057
- assert(nodeBTime1 !== undefined, "B should have unreferenced timestamp");
1058
- assert(nodeCTime1 !== undefined, "C should have unreferenced timestamp");
1059
- // 2. Add reference from A to B. E = [A -\> B, B -\> C].
1060
- garbageCollector.addedOutboundReference(nodeA, nodeB);
1061
- defaultGCData.gcNodes[nodeA] = [nodeB];
1062
- // 3. Remove reference from B to C. E = [A -\> B].
1063
- defaultGCData.gcNodes[nodeB] = [];
1064
- // 4. Remove reference from A to B. E = [].
1065
- defaultGCData.gcNodes[nodeA] = [];
1066
- // 5. Run GC and generate summary 2. E = [].
1067
- const timestamps2 = await getUnreferencedTimestamps();
1068
- assert(timestamps2.get(nodeA) === undefined, "A should be referenced");
1069
- const nodeBTime2 = timestamps2.get(nodeB);
1070
- const nodeCTime2 = timestamps2.get(nodeC);
1071
- assert(nodeBTime2 !== undefined && nodeBTime2 > nodeBTime1, "B's timestamp should have updated");
1072
- assert(nodeCTime2 !== undefined && nodeCTime2 > nodeCTime1, "C's timestamp should have updated");
1073
- });
1074
- /**
1075
- * Validates that we can detect chain of references in which the first reference was added and then removed.
1076
- * 1. Summary 1 at t1. V = [A*, B, C, D]. E = [B -\> C, C -\> D]. B, C and D have unreferenced time t2.
1077
- * 2. Reference from A to B added. E = [A -\> B, B -\> C, C -\> D].
1078
- * 3. Reference from A to B removed. E = [B -\> C, C -\> D].
1079
- * 4. Summary 2 at t2. V = [A*, B, C, D]. E = [B -\> C, C -\> D]. B, C and D have unreferenced time t2.
1080
- * Validates that the unreferenced time for B, C and D is t2 which is \> t1.
1081
- */
1082
- it(`Scenario 3 - Reference added through chain of references and removed`, async () => {
1083
- // Initialize nodes A, B, C and D.
1084
- defaultGCData.gcNodes["/"] = [nodeA];
1085
- defaultGCData.gcNodes[nodeA] = [];
1086
- defaultGCData.gcNodes[nodeB] = [nodeC];
1087
- defaultGCData.gcNodes[nodeC] = [nodeD];
1088
- defaultGCData.gcNodes[nodeD] = [];
1089
- // 1. Run GC and generate summary 1. E = [B -\> C, C -\> D].
1090
- const timestamps1 = await getUnreferencedTimestamps();
1091
- assert(timestamps1.get(nodeA) === undefined, "A should be referenced");
1092
- const nodeBTime1 = timestamps1.get(nodeB);
1093
- const nodeCTime1 = timestamps1.get(nodeC);
1094
- const nodeDTime1 = timestamps1.get(nodeD);
1095
- assert(nodeBTime1 !== undefined, "B should have unreferenced timestamp");
1096
- assert(nodeCTime1 !== undefined, "C should have unreferenced timestamp");
1097
- assert(nodeDTime1 !== undefined, "D should have unreferenced timestamp");
1098
- // 2. Add reference from A to B. E = [A -\> B, B -\> C, C -\> D].
1099
- garbageCollector.addedOutboundReference(nodeA, nodeB);
1100
- defaultGCData.gcNodes[nodeA] = [nodeB];
1101
- // 3. Remove reference from A to B. E = [B -\> C, C -\> D].
1102
- defaultGCData.gcNodes[nodeA] = [];
1103
- // 4. Run GC and generate summary 2. E = [B -\> C, C -\> D].
1104
- const timestamps2 = await getUnreferencedTimestamps();
1105
- assert(timestamps2.get(nodeA) === undefined, "A should be referenced");
1106
- const nodeBTime2 = timestamps2.get(nodeB);
1107
- const nodeCTime2 = timestamps2.get(nodeC);
1108
- const nodeDTime2 = timestamps2.get(nodeD);
1109
- assert(nodeBTime2 !== undefined && nodeBTime2 > nodeBTime1, "B's timestamp should have updated");
1110
- assert(nodeCTime2 !== undefined && nodeCTime2 > nodeCTime1, "C's timestamp should have updated");
1111
- assert(nodeDTime2 !== undefined && nodeDTime2 > nodeDTime1, "D's timestamp should have updated");
1112
- });
1113
- /**
1114
- * Validates that we can detect references that were added and removed via new nodes.
1115
- * 1. Summary 1 at t1. V = [A*, C]. E = []. C has unreferenced time t1.
1116
- * 2. Node B is created. E = [].
1117
- * 3. Reference from A to B added. E = [A -\> B].
1118
- * 4. Reference from B to C added. E = [A -\> B, B -\> C].
1119
- * 5. Reference from B to C removed. E = [A -\> B].
1120
- * 6. Summary 2 at t2. V = [A*, B, C]. E = [A -\> B]. C has unreferenced time t2.
1121
- * Validates that the unreferenced time for C is t2 which is \> t1.
1122
- */
1123
- it(`Scenario 4 - Reference added via new nodes and removed`, async () => {
1124
- // Initialize nodes A, B and C.
1125
- defaultGCData.gcNodes["/"] = [nodeA];
1126
- defaultGCData.gcNodes[nodeA] = [];
1127
- defaultGCData.gcNodes[nodeC] = [];
1128
- // 1. Run GC and generate summary 1. E = [].
1129
- const timestamps1 = await getUnreferencedTimestamps();
1130
- assert(timestamps1.get(nodeA) === undefined, "A should be referenced");
1131
- const nodeCTime1 = timestamps1.get(nodeC);
1132
- assert(nodeCTime1 !== undefined, "C should have unreferenced timestamp");
1133
- // 2. Create node B, i.e., add B to GC data. E = [].
1134
- defaultGCData.gcNodes[nodeB] = [];
1135
- // 3. Add reference from A to B. E = [A -\> B].
1136
- garbageCollector.addedOutboundReference(nodeA, nodeB);
1137
- defaultGCData.gcNodes[nodeA] = [nodeB];
1138
- // 4. Add reference from B to C. E = [A -\> B, B -\> C].
1139
- garbageCollector.addedOutboundReference(nodeB, nodeC);
1140
- defaultGCData.gcNodes[nodeB] = [nodeC];
1141
- // 5. Remove reference from B to C. E = [A -\> B].
1142
- defaultGCData.gcNodes[nodeB] = [];
1143
- // 6. Run GC and generate summary 2. E = [A -\> B].
1144
- const timestamps2 = await getUnreferencedTimestamps();
1145
- assert(timestamps2.get(nodeA) === undefined, "A should be referenced");
1146
- assert(timestamps2.get(nodeB) === undefined, "B should be referenced");
1147
- const nodeCTime2 = timestamps2.get(nodeC);
1148
- assert(nodeCTime2 !== undefined && nodeCTime2 > nodeCTime1, "C's timestamp should have updated");
1149
- });
1150
- /**
1151
- * Validates that we can detect multiple references that were added and then removed by the same node.
1152
- * 1. Summary 1 at t1. V = [A*, B, C]. E = []. B and C have unreferenced time t1.
1153
- * 2. Reference from A to B added. E = [A -\> B].
1154
- * 3. Reference from A to C added. E = [A -\> B, A -\> C].
1155
- * 4. Reference from A to B removed. E = [A -\> C].
1156
- * 5. Reference from A to C removed. E = [].
1157
- * 6. Summary 2 at t2. V = [A*, B]. E = []. B and C have unreferenced time t2.
1158
- * Validates that the unreferenced time for B and C is t2 which is \> t1.
1159
- */
1160
- it(`Scenario 5 - Multiple references added and then removed by same node`, async () => {
1161
- // Initialize nodes A, B and C.
1162
- defaultGCData.gcNodes["/"] = [nodeA];
1163
- defaultGCData.gcNodes[nodeA] = [];
1164
- defaultGCData.gcNodes[nodeB] = [];
1165
- defaultGCData.gcNodes[nodeC] = [];
1166
- // 1. Run GC and generate summary 1. E = [].
1167
- const timestamps1 = await getUnreferencedTimestamps();
1168
- assert(timestamps1.get(nodeA) === undefined, "A should be referenced");
1169
- const nodeBTime1 = timestamps1.get(nodeB);
1170
- const nodeCTime1 = timestamps1.get(nodeC);
1171
- assert(nodeBTime1 !== undefined, "B should have unreferenced timestamp");
1172
- assert(nodeCTime1 !== undefined, "C should have unreferenced timestamp");
1173
- // 2. Add reference from A to B. E = [A -\> B].
1174
- garbageCollector.addedOutboundReference(nodeA, nodeB);
1175
- defaultGCData.gcNodes[nodeA] = [nodeB];
1176
- // 3. Add reference from A to C. E = [A -\> B, A -\> C].
1177
- garbageCollector.addedOutboundReference(nodeA, nodeC);
1178
- defaultGCData.gcNodes[nodeA] = [nodeB, nodeC];
1179
- // 4. Remove reference from A to B. E = [A -\> C].
1180
- defaultGCData.gcNodes[nodeA] = [nodeC];
1181
- // 5. Remove reference from A to C. E = [].
1182
- defaultGCData.gcNodes[nodeA] = [];
1183
- // 6. Run GC and generate summary 2. E = [].
1184
- const timestamps2 = await getUnreferencedTimestamps();
1185
- assert(timestamps2.get(nodeA) === undefined, "A should be referenced");
1186
- const nodeBTime2 = timestamps2.get(nodeB);
1187
- const nodeCTime2 = timestamps2.get(nodeC);
1188
- assert(nodeCTime2 !== undefined && nodeCTime2 > nodeCTime1, "C's timestamp should have updated");
1189
- assert(nodeBTime2 !== undefined && nodeBTime2 > nodeBTime1, "B's timestamp should have updated");
1190
- });
1191
- /**
1192
- * Validates that we generate error on detecting reference during GC that was not notified explicitly.
1193
- * 1. Summary 1 at t1. V = [A*]. E = [].
1194
- * 2. Node B is created. E = [].
1195
- * 3. Reference from A to B added without notifying GC. E = [A -\> B].
1196
- * 4. Summary 2 at t2. V = [A*, B]. E = [A -\> B].
1197
- * Validates that we log an error since B is detected as a referenced node but its reference was notified
1198
- * to GC.
1199
- */
1200
- it(`Scenario 6 - Reference added without notifying GC`, async () => {
1201
- // Initialize nodes A & D.
1202
- defaultGCData.gcNodes["/"] = [nodeA, nodeD];
1203
- defaultGCData.gcNodes[nodeA] = [];
1204
- defaultGCData.gcNodes[nodeD] = [];
1205
- // 1. Run GC and generate summary 1. E = [].
1206
- const timestamps1 = await getUnreferencedTimestamps();
1207
- assert(timestamps1.get(nodeA) === undefined, "A should be referenced");
1208
- assert(timestamps1.get(nodeD) === undefined, "D should be referenced");
1209
- // 2. Create nodes B & C. E = [].
1210
- defaultGCData.gcNodes[nodeB] = [];
1211
- defaultGCData.gcNodes[nodeC] = [];
1212
- // 3. Add reference from A to B, A to C, A to E, D to C, and E to A without calling addedOutboundReference.
1213
- // E = [A -\> B, A -\> C, A -\> E, D -\> C, E -\> A].
1214
- defaultGCData.gcNodes[nodeA] = [nodeB, nodeC, nodeE];
1215
- defaultGCData.gcNodes[nodeD] = [nodeC];
1216
- defaultGCData.gcNodes[nodeE] = [nodeA];
1217
- // 4. Add reference from A to D with calling addedOutboundReference
1218
- defaultGCData.gcNodes[nodeA].push(nodeD);
1219
- garbageCollector.addedOutboundReference(nodeA, nodeD);
1220
- // 5. Run GC and generate summary 2. E = [A -\> B, A -\> C, A -\> E, D -\> C, E -\> A].
1221
- await getUnreferencedTimestamps();
1222
- // Validate that we got the "gcUnknownOutboundReferences" error.
1223
- const unknownReferencesEvent = "GarbageCollector:gcUnknownOutboundReferences";
1224
- const eventsFound = mockLogger.matchEvents([
1225
- {
1226
- eventName: unknownReferencesEvent,
1227
- ...tagCodeArtifacts({
1228
- id: "/A",
1229
- routes: JSON.stringify(["/B", "/C"]),
1230
- }),
1231
- },
1232
- {
1233
- eventName: unknownReferencesEvent,
1234
- ...tagCodeArtifacts({
1235
- id: "/D",
1236
- routes: JSON.stringify(["/C"]),
1237
- }),
1238
- },
1239
- ], true /* inlineDetailsProp */);
1240
- assert(eventsFound, `Expected unknownReferenceEvent event!`);
1241
- });
1242
- });
1243
- describe("References to unreferenced nodes", () => {
1244
- /**
1245
- * Validates that we can detect references that are added from an unreferenced node to another.
1246
- * 1. Summary 1 at t1. V = [A*, B, C]. E = []. B and C have unreferenced time t1.
1247
- * 2. Reference from B to C. E = [B -\> C].
1248
- * 3. Summary 2 at t2. V = [A*, B, C]. E = [B -\> C]. B and C have unreferenced time t1.
1249
- * Validates that the unreferenced time for B and C is still t1.
1250
- */
1251
- it(`Scenario 1 - Reference added to unreferenced node`, async () => {
1252
- // Initialize nodes A, B and C.
1253
- defaultGCData.gcNodes["/"] = [nodeA];
1254
- defaultGCData.gcNodes[nodeA] = [];
1255
- defaultGCData.gcNodes[nodeB] = [];
1256
- defaultGCData.gcNodes[nodeC] = [];
1257
- // 1. Run GC and generate summary 1. E = [B -\> C].
1258
- const timestamps1 = await getUnreferencedTimestamps();
1259
- assert(timestamps1.get(nodeA) === undefined, "A should be referenced");
1260
- const nodeBTime1 = timestamps1.get(nodeB);
1261
- const nodeCTime1 = timestamps1.get(nodeC);
1262
- assert(nodeBTime1 !== undefined, "B should have unreferenced timestamp");
1263
- assert(nodeCTime1 !== undefined, "C should have unreferenced timestamp");
1264
- // 2. Add reference from B to C. E = [B -\> C].
1265
- garbageCollector.addedOutboundReference(nodeB, nodeC);
1266
- defaultGCData.gcNodes[nodeB] = [nodeC];
1267
- // 3. Run GC and generate summary 2. E = [B -\> C].
1268
- const timestamps2 = await getUnreferencedTimestamps();
1269
- assert(timestamps2.get(nodeA) === undefined, "A should be referenced");
1270
- const nodeBTime2 = timestamps2.get(nodeB);
1271
- const nodeCTime2 = timestamps2.get(nodeC);
1272
- assert(nodeBTime2 === nodeBTime1, "B's timestamp should be unchanged");
1273
- assert(nodeCTime2 !== undefined && nodeCTime2 > nodeCTime1, "C's timestamp should have updated");
1274
- });
1275
- /*
1276
- * Validates that we can detect references that are added from an unreferenced node to a list of
1277
- * unreferenced nodes, i.e., nodes with references to each other but are overall unreferenced.
1278
- * 1. Summary 1 at t1. V = [A*, B, C, D]. E = [C -\> D]. B, C and D have unreferenced time t1.
1279
- * 2. Op adds reference from B to C. E = [B -\> C, C -\> D].
1280
- * 3. Summary 2 at t2. V = [A*, B, C]. E = [B -\> C, C -\> D]. C and D have unreferenced time t2.
1281
- * Validates that the unreferenced time for C and D is t2 which is > t1.
1282
- */
1283
- it(`Scenario 2 - Reference added to a list of unreferenced nodes from an unreferenced node`, async () => {
1284
- // Initialize nodes A, B and C.
1285
- defaultGCData.gcNodes["/"] = [nodeA];
1286
- defaultGCData.gcNodes[nodeA] = [];
1287
- defaultGCData.gcNodes[nodeB] = [];
1288
- defaultGCData.gcNodes[nodeC] = [nodeD];
1289
- defaultGCData.gcNodes[nodeD] = [];
1290
- // 1. Run GC and generate summary 1. E = [B -\> C].
1291
- const timestamps1 = await getUnreferencedTimestamps();
1292
- assert(timestamps1.get(nodeA) === undefined, "A should be referenced");
1293
- const nodeBTime1 = timestamps1.get(nodeB);
1294
- const nodeCTime1 = timestamps1.get(nodeC);
1295
- const nodeDTime1 = timestamps1.get(nodeC);
1296
- assert(nodeBTime1 !== undefined, "B should have unreferenced timestamp");
1297
- assert(nodeCTime1 !== undefined, "C should have unreferenced timestamp");
1298
- assert(nodeDTime1 !== undefined, "C should have unreferenced timestamp");
1299
- // 2. Add reference from B to C. E = [B -\> C, C-\> D].
1300
- garbageCollector.addedOutboundReference(nodeB, nodeC);
1301
- defaultGCData.gcNodes[nodeB] = [nodeC];
1302
- // 3. Run GC and generate summary 2. E = [B -\> C. C -\> D].
1303
- const timestamps2 = await getUnreferencedTimestamps();
1304
- assert(timestamps2.get(nodeA) === undefined, "A should be referenced");
1305
- const nodeBTime2 = timestamps2.get(nodeB);
1306
- const nodeCTime2 = timestamps2.get(nodeC);
1307
- const nodeDTime2 = timestamps2.get(nodeD);
1308
- assert(nodeBTime2 === nodeBTime1, "B's timestamp should be unchanged");
1309
- assert(nodeCTime2 !== undefined && nodeCTime2 > nodeCTime1, "C's timestamp should have updated");
1310
- assert(nodeDTime2 !== undefined && nodeDTime2 > nodeDTime1, "D's timestamp should have updated");
1311
- });
1312
- /*
1313
- * Validates that we can detect references that are added from an unreferenced node to a list of
1314
- * unreferenced nodes, i.e., nodes with references to each other but are overall unreferenced. Then
1315
- * a reference between the list is removed
1316
- * 1. Summary 1 at t1. V = [A*, B, C, D]. E = [C -> D]. B, C and D have unreferenced time t1.
1317
- * 2. Op adds reference from B to C. E = [B -> C, C -> D].
1318
- * 3. Op removes reference from C to D. E = [B -> C].
1319
- * 4. Summary 2 at t2. V = [A*, B, C]. E = [B -> C]. C and D have unreferenced time t2.
1320
- * Validates that the unreferenced time for C and D is t2 which is > t1.
1321
- */
1322
- it(`Scenario 3 - Reference added to a list of unreferenced nodes and a reference is removed`, async () => {
1323
- // Initialize nodes A, B and C.
1324
- defaultGCData.gcNodes["/"] = [nodeA];
1325
- defaultGCData.gcNodes[nodeA] = [];
1326
- defaultGCData.gcNodes[nodeB] = [];
1327
- defaultGCData.gcNodes[nodeC] = [nodeD];
1328
- defaultGCData.gcNodes[nodeD] = [];
1329
- // 1. Run GC and generate summary 1. E = [B -\> C].
1330
- const timestamps1 = await getUnreferencedTimestamps();
1331
- assert(timestamps1.get(nodeA) === undefined, "A should be referenced");
1332
- const nodeBTime1 = timestamps1.get(nodeB);
1333
- const nodeCTime1 = timestamps1.get(nodeC);
1334
- const nodeDTime1 = timestamps1.get(nodeC);
1335
- assert(nodeBTime1 !== undefined, "B should have unreferenced timestamp");
1336
- assert(nodeCTime1 !== undefined, "C should have unreferenced timestamp");
1337
- assert(nodeDTime1 !== undefined, "C should have unreferenced timestamp");
1338
- // 2. Add reference from B to C. E = [B -\> C, C-\> D].
1339
- garbageCollector.addedOutboundReference(nodeB, nodeC);
1340
- defaultGCData.gcNodes[nodeB] = [nodeC];
1341
- // 3. Remove reference from C to D. E = [B -\> C].
1342
- defaultGCData.gcNodes[nodeC] = [];
1343
- // 3. Run GC and generate summary 2. E = [B -\> C].
1344
- const timestamps2 = await getUnreferencedTimestamps();
1345
- assert(timestamps2.get(nodeA) === undefined, "A should be referenced");
1346
- const nodeBTime2 = timestamps2.get(nodeB);
1347
- const nodeCTime2 = timestamps2.get(nodeC);
1348
- const nodeDTime2 = timestamps2.get(nodeD);
1349
- assert(nodeBTime2 === nodeBTime1, "B's timestamp should be unchanged");
1350
- assert(nodeCTime2 !== undefined && nodeCTime2 > nodeCTime1, "C's timestamp should have updated");
1351
- assert(nodeDTime2 !== undefined && nodeDTime2 > nodeDTime1, "D's timestamp should have updated");
1352
- });
1353
- });
1354
- });
1355
- describe("No changes to GC between summaries", () => {
1356
- const fullTree = false;
1357
- const trackState = true;
1358
- let garbageCollector;
1359
- beforeEach(() => {
1360
- // Initialize nodes A & D.
1361
- defaultGCData.gcNodes = {};
1362
- defaultGCData.gcNodes["/"] = nodes;
1363
- });
1364
- const checkGCSummaryType = (summary, expectedBlobType, summaryNumber) => {
1365
- assert(summary !== undefined, `Expected a summary on ${summaryNumber} summarize`);
1366
- assert(summary.summary.type === expectedBlobType, `Expected summary type ${expectedBlobType} on ${summaryNumber} summarize, got ${summary.summary.type}`);
1367
- };
1368
- it("creates a blob handle when no version specified", async () => {
1369
- garbageCollector = createGarbageCollector();
1370
- await garbageCollector.collectGarbage({});
1371
- const tree1 = garbageCollector.summarize(fullTree, trackState);
1372
- checkGCSummaryType(tree1, SummaryType.Tree, "first");
1373
- await garbageCollector.refreshLatestSummary({
1374
- isSummaryTracked: true,
1375
- isSummaryNewer: true,
1376
- });
1377
- await garbageCollector.collectGarbage({});
1378
- const tree2 = garbageCollector.summarize(fullTree, trackState);
1379
- checkGCSummaryType(tree2, SummaryType.Handle, "second");
1380
- });
1381
- });
1382
- it("resets gc state when loading from an old snapshot format", async () => {
1383
- // Create GC details for node 3's GC blob whose unreferenced time was > timeout ms ago.
1384
- // This means this node should time out as soon as its data is loaded.
1385
- const node3GCDetails = {
1386
- gcData: { gcNodes: { "/": [] } },
1387
- unrefTimestamp: Date.now() - defaultTombstoneTimeoutMs * 100,
1388
- };
1389
- const node3Snapshot = getDummySnapshotTree();
1390
- const gcBlobId = "node3GCDetails";
1391
- const attributesBlobId = "attributesBlob";
1392
- node3Snapshot.blobs[gcTreeKey] = gcBlobId;
1393
- node3Snapshot.blobs[dataStoreAttributesBlobName] = attributesBlobId;
1394
- // Create a base snapshot that contains snapshot tree of node 3.
1395
- const channelsTree = getDummySnapshotTree();
1396
- channelsTree.trees[nodes[3].slice(1)] = node3Snapshot;
1397
- const baseSnapshot = getDummySnapshotTree();
1398
- baseSnapshot.trees[channelsTreeName] = channelsTree;
1399
- // Set up the getNodeGCDetails function to return the GC details for node 3 when asked by garbage collector.
1400
- const gcBlobsMap = new Map([
1401
- [gcBlobId, node3GCDetails],
1402
- [attributesBlobId, {}],
1403
- ]);
1404
- const garbageCollector = createGarbageCollector({
1405
- createParams: { baseSnapshot },
1406
- gcBlobsMap,
1407
- gcMetadata: {
1408
- tombstoneTimeoutMs: defaultTombstoneTimeoutMs,
1409
- },
1410
- });
1411
- // GC state and tombstone state should be discarded but deleted nodes should be read from base snapshot.
1412
- const baseSnapshotData = await garbageCollector.baseSnapshotDataP;
1413
- assert(baseSnapshotData === undefined, "base snapshot should not be defined for old snapshots where we wrote the gc data in the channels");
1414
- // Initialize from the base state and validate that tombstones has 0 entry because it was discarded.
1415
- // Deleted nodes should have one entry because it is still used.
1416
- await garbageCollector.initializeBaseState();
1417
- assert.strictEqual(garbageCollector.tombstones.length, 0, "Expecting 0 tombstone nodes");
1418
- assert.strictEqual(garbageCollector.deletedNodes.size, 0, "Expecting 0 deleted nodes");
1419
- });
1420
- describe("Future GC op type compatibility", () => {
1421
- const gcMessageFromFuture = {
1422
- type: "FUTURE_MESSAGE",
1423
- hello: "HELLO",
1424
- };
1425
- let garbageCollector;
1426
- beforeEach(async () => {
1427
- garbageCollector = createGarbageCollector({
1428
- createParams: { gcOptions: { enableGCSweep: true } },
1429
- });
1430
- });
1431
- it("can submit GC op compat behavior", async () => {
1432
- const gcWithPrivates = garbageCollector;
1433
- const containerRuntimeGCMessage = {
1434
- type: ContainerMessageType.GC,
1435
- contents: gcMessageFromFuture,
1436
- compatDetails: { behavior: "Ignore" },
1437
- };
1438
- assert.doesNotThrow(() => gcWithPrivates.submitMessage(containerRuntimeGCMessage), "Cannot submit GC message with compatDetails");
1439
- });
1440
- it("process remote op with unrecognized type and 'Ignore' compat behavior", async () => {
1441
- const containerRuntimeGCMessage = {
1442
- type: ContainerMessageType.GC,
1443
- contents: gcMessageFromFuture,
1444
- compatDetails: { behavior: "Ignore" },
1445
- };
1446
- garbageCollector.processMessage(containerRuntimeGCMessage, false /* local */);
1447
- });
1448
- it("process remote op with unrecognized type and 'FailToProcess' compat behavior", async () => {
1449
- const containerRuntimeGCMessage = {
1450
- type: ContainerMessageType.GC,
1451
- contents: gcMessageFromFuture,
1452
- compatDetails: { behavior: "FailToProcess" },
1453
- };
1454
- assert.throws(() => garbageCollector.processMessage(containerRuntimeGCMessage, false /* local */), (error) => error.errorType === ContainerErrorTypes.dataProcessingError, "Garbage collection message of unknown type FROM_THE_FUTURE");
1455
- });
1456
- it("process remote op with unrecognized type and no compat behavior", async () => {
1457
- const containerRuntimeGCMessage = {
1458
- type: ContainerMessageType.GC,
1459
- contents: gcMessageFromFuture,
1460
- };
1461
- assert.throws(() => garbageCollector.processMessage(containerRuntimeGCMessage, false /* local */), (error) => error.errorType === ContainerErrorTypes.dataProcessingError, "Garbage collection message of unknown type FROM_THE_FUTURE");
1462
- });
1463
- });
1464
- });
1465
- //# sourceMappingURL=garbageCollection.spec.js.map