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

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 (340) hide show
  1. package/.eslintrc.js +21 -10
  2. package/.mocharc.js +2 -2
  3. package/api-extractor.json +2 -2
  4. package/dist/batchTracker.d.ts +1 -2
  5. package/dist/batchTracker.d.ts.map +1 -1
  6. package/dist/batchTracker.js +2 -1
  7. package/dist/batchTracker.js.map +1 -1
  8. package/dist/blobManager.d.ts +53 -34
  9. package/dist/blobManager.d.ts.map +1 -1
  10. package/dist/blobManager.js +236 -124
  11. package/dist/blobManager.js.map +1 -1
  12. package/dist/connectionTelemetry.d.ts.map +1 -1
  13. package/dist/connectionTelemetry.js +11 -9
  14. package/dist/connectionTelemetry.js.map +1 -1
  15. package/dist/containerHandleContext.d.ts.map +1 -1
  16. package/dist/containerHandleContext.js +3 -1
  17. package/dist/containerHandleContext.js.map +1 -1
  18. package/dist/containerRuntime.d.ts +95 -46
  19. package/dist/containerRuntime.d.ts.map +1 -1
  20. package/dist/containerRuntime.js +288 -135
  21. package/dist/containerRuntime.js.map +1 -1
  22. package/dist/dataStore.d.ts.map +1 -1
  23. package/dist/dataStore.js +11 -9
  24. package/dist/dataStore.js.map +1 -1
  25. package/dist/dataStoreContext.d.ts +2 -1
  26. package/dist/dataStoreContext.d.ts.map +1 -1
  27. package/dist/dataStoreContext.js +38 -21
  28. package/dist/dataStoreContext.js.map +1 -1
  29. package/dist/dataStoreContexts.d.ts.map +1 -1
  30. package/dist/dataStoreContexts.js +7 -3
  31. package/dist/dataStoreContexts.js.map +1 -1
  32. package/dist/dataStoreRegistry.d.ts.map +1 -1
  33. package/dist/dataStoreRegistry.js +3 -1
  34. package/dist/dataStoreRegistry.js.map +1 -1
  35. package/dist/dataStores.d.ts +12 -9
  36. package/dist/dataStores.d.ts.map +1 -1
  37. package/dist/dataStores.js +68 -46
  38. package/dist/dataStores.js.map +1 -1
  39. package/dist/deltaScheduler.d.ts.map +1 -1
  40. package/dist/deltaScheduler.js +8 -3
  41. package/dist/deltaScheduler.js.map +1 -1
  42. package/dist/garbageCollection.d.ts +50 -26
  43. package/dist/garbageCollection.d.ts.map +1 -1
  44. package/dist/garbageCollection.js +348 -196
  45. package/dist/garbageCollection.js.map +1 -1
  46. package/dist/garbageCollectionConstants.d.ts +7 -3
  47. package/dist/garbageCollectionConstants.d.ts.map +1 -1
  48. package/dist/garbageCollectionConstants.js +10 -8
  49. package/dist/garbageCollectionConstants.js.map +1 -1
  50. package/dist/garbageCollectionHelpers.d.ts +15 -0
  51. package/dist/garbageCollectionHelpers.d.ts.map +1 -0
  52. package/dist/garbageCollectionHelpers.js +27 -0
  53. package/dist/garbageCollectionHelpers.js.map +1 -0
  54. package/dist/gcSweepReadyUsageDetection.d.ts +5 -5
  55. package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
  56. package/dist/gcSweepReadyUsageDetection.js +14 -10
  57. package/dist/gcSweepReadyUsageDetection.js.map +1 -1
  58. package/dist/index.d.ts +3 -4
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +3 -5
  61. package/dist/index.js.map +1 -1
  62. package/dist/opLifecycle/batchManager.d.ts +13 -1
  63. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  64. package/dist/opLifecycle/batchManager.js +48 -7
  65. package/dist/opLifecycle/batchManager.js.map +1 -1
  66. package/dist/opLifecycle/definitions.d.ts +25 -1
  67. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  68. package/dist/opLifecycle/definitions.js.map +1 -1
  69. package/dist/opLifecycle/index.d.ts +2 -2
  70. package/dist/opLifecycle/index.d.ts.map +1 -1
  71. package/dist/opLifecycle/index.js +2 -1
  72. package/dist/opLifecycle/index.js.map +1 -1
  73. package/dist/opLifecycle/opCompressor.d.ts +1 -1
  74. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  75. package/dist/opLifecycle/opCompressor.js +24 -10
  76. package/dist/opLifecycle/opCompressor.js.map +1 -1
  77. package/dist/opLifecycle/opDecompressor.d.ts +2 -1
  78. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  79. package/dist/opLifecycle/opDecompressor.js +33 -17
  80. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  81. package/dist/opLifecycle/opSplitter.d.ts +34 -2
  82. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  83. package/dist/opLifecycle/opSplitter.js +117 -5
  84. package/dist/opLifecycle/opSplitter.js.map +1 -1
  85. package/dist/opLifecycle/outbox.d.ts +5 -0
  86. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  87. package/dist/opLifecycle/outbox.js +38 -27
  88. package/dist/opLifecycle/outbox.js.map +1 -1
  89. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  90. package/dist/opLifecycle/remoteMessageProcessor.js +17 -2
  91. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  92. package/dist/opProperties.d.ts.map +1 -1
  93. package/dist/opProperties.js +1 -3
  94. package/dist/opProperties.js.map +1 -1
  95. package/dist/orderedClientElection.d.ts.map +1 -1
  96. package/dist/orderedClientElection.js +10 -4
  97. package/dist/orderedClientElection.js.map +1 -1
  98. package/dist/packageVersion.d.ts +1 -1
  99. package/dist/packageVersion.js +1 -1
  100. package/dist/packageVersion.js.map +1 -1
  101. package/dist/pendingStateManager.d.ts +4 -13
  102. package/dist/pendingStateManager.d.ts.map +1 -1
  103. package/dist/pendingStateManager.js +134 -161
  104. package/dist/pendingStateManager.js.map +1 -1
  105. package/dist/runWhileConnectedCoordinator.d.ts.map +1 -1
  106. package/dist/runWhileConnectedCoordinator.js.map +1 -1
  107. package/dist/runningSummarizer.d.ts.map +1 -1
  108. package/dist/runningSummarizer.js +34 -22
  109. package/dist/runningSummarizer.js.map +1 -1
  110. package/dist/scheduleManager.d.ts +0 -1
  111. package/dist/scheduleManager.d.ts.map +1 -1
  112. package/dist/scheduleManager.js +11 -21
  113. package/dist/scheduleManager.js.map +1 -1
  114. package/dist/serializedSnapshotStorage.d.ts.map +1 -1
  115. package/dist/serializedSnapshotStorage.js +3 -1
  116. package/dist/serializedSnapshotStorage.js.map +1 -1
  117. package/dist/summarizer.d.ts +2 -3
  118. package/dist/summarizer.d.ts.map +1 -1
  119. package/dist/summarizer.js +39 -18
  120. package/dist/summarizer.js.map +1 -1
  121. package/dist/summarizerClientElection.d.ts +1 -2
  122. package/dist/summarizerClientElection.d.ts.map +1 -1
  123. package/dist/summarizerClientElection.js +3 -30
  124. package/dist/summarizerClientElection.js.map +1 -1
  125. package/dist/summarizerHandle.d.ts.map +1 -1
  126. package/dist/summarizerHandle.js.map +1 -1
  127. package/dist/summarizerHeuristics.d.ts.map +1 -1
  128. package/dist/summarizerHeuristics.js +6 -9
  129. package/dist/summarizerHeuristics.js.map +1 -1
  130. package/dist/summarizerTypes.d.ts +22 -25
  131. package/dist/summarizerTypes.d.ts.map +1 -1
  132. package/dist/summarizerTypes.js.map +1 -1
  133. package/dist/summaryCollection.d.ts.map +1 -1
  134. package/dist/summaryCollection.js +18 -8
  135. package/dist/summaryCollection.js.map +1 -1
  136. package/dist/summaryFormat.d.ts.map +1 -1
  137. package/dist/summaryFormat.js +18 -11
  138. package/dist/summaryFormat.js.map +1 -1
  139. package/dist/summaryGenerator.d.ts.map +1 -1
  140. package/dist/summaryGenerator.js +32 -14
  141. package/dist/summaryGenerator.js.map +1 -1
  142. package/dist/summaryManager.d.ts.map +1 -1
  143. package/dist/summaryManager.js +21 -9
  144. package/dist/summaryManager.js.map +1 -1
  145. package/dist/throttler.d.ts +2 -2
  146. package/dist/throttler.d.ts.map +1 -1
  147. package/dist/throttler.js +4 -4
  148. package/dist/throttler.js.map +1 -1
  149. package/garbageCollection.md +15 -2
  150. package/lib/batchTracker.d.ts +1 -2
  151. package/lib/batchTracker.d.ts.map +1 -1
  152. package/lib/batchTracker.js +2 -1
  153. package/lib/batchTracker.js.map +1 -1
  154. package/lib/blobManager.d.ts +53 -34
  155. package/lib/blobManager.d.ts.map +1 -1
  156. package/lib/blobManager.js +239 -127
  157. package/lib/blobManager.js.map +1 -1
  158. package/lib/connectionTelemetry.d.ts.map +1 -1
  159. package/lib/connectionTelemetry.js +11 -9
  160. package/lib/connectionTelemetry.js.map +1 -1
  161. package/lib/containerHandleContext.d.ts.map +1 -1
  162. package/lib/containerHandleContext.js +3 -1
  163. package/lib/containerHandleContext.js.map +1 -1
  164. package/lib/containerRuntime.d.ts +95 -46
  165. package/lib/containerRuntime.d.ts.map +1 -1
  166. package/lib/containerRuntime.js +291 -138
  167. package/lib/containerRuntime.js.map +1 -1
  168. package/lib/dataStore.d.ts.map +1 -1
  169. package/lib/dataStore.js +11 -9
  170. package/lib/dataStore.js.map +1 -1
  171. package/lib/dataStoreContext.d.ts +2 -1
  172. package/lib/dataStoreContext.d.ts.map +1 -1
  173. package/lib/dataStoreContext.js +40 -23
  174. package/lib/dataStoreContext.js.map +1 -1
  175. package/lib/dataStoreContexts.d.ts.map +1 -1
  176. package/lib/dataStoreContexts.js +7 -3
  177. package/lib/dataStoreContexts.js.map +1 -1
  178. package/lib/dataStoreRegistry.d.ts.map +1 -1
  179. package/lib/dataStoreRegistry.js +3 -1
  180. package/lib/dataStoreRegistry.js.map +1 -1
  181. package/lib/dataStores.d.ts +12 -9
  182. package/lib/dataStores.d.ts.map +1 -1
  183. package/lib/dataStores.js +74 -52
  184. package/lib/dataStores.js.map +1 -1
  185. package/lib/deltaScheduler.d.ts.map +1 -1
  186. package/lib/deltaScheduler.js +9 -4
  187. package/lib/deltaScheduler.js.map +1 -1
  188. package/lib/garbageCollection.d.ts +50 -26
  189. package/lib/garbageCollection.d.ts.map +1 -1
  190. package/lib/garbageCollection.js +347 -195
  191. package/lib/garbageCollection.js.map +1 -1
  192. package/lib/garbageCollectionConstants.d.ts +7 -3
  193. package/lib/garbageCollectionConstants.d.ts.map +1 -1
  194. package/lib/garbageCollectionConstants.js +9 -7
  195. package/lib/garbageCollectionConstants.js.map +1 -1
  196. package/lib/garbageCollectionHelpers.d.ts +15 -0
  197. package/lib/garbageCollectionHelpers.d.ts.map +1 -0
  198. package/lib/garbageCollectionHelpers.js +23 -0
  199. package/lib/garbageCollectionHelpers.js.map +1 -0
  200. package/lib/gcSweepReadyUsageDetection.d.ts +5 -5
  201. package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
  202. package/lib/gcSweepReadyUsageDetection.js +14 -10
  203. package/lib/gcSweepReadyUsageDetection.js.map +1 -1
  204. package/lib/index.d.ts +3 -4
  205. package/lib/index.d.ts.map +1 -1
  206. package/lib/index.js +2 -3
  207. package/lib/index.js.map +1 -1
  208. package/lib/opLifecycle/batchManager.d.ts +13 -1
  209. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  210. package/lib/opLifecycle/batchManager.js +48 -7
  211. package/lib/opLifecycle/batchManager.js.map +1 -1
  212. package/lib/opLifecycle/definitions.d.ts +25 -1
  213. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  214. package/lib/opLifecycle/definitions.js.map +1 -1
  215. package/lib/opLifecycle/index.d.ts +2 -2
  216. package/lib/opLifecycle/index.d.ts.map +1 -1
  217. package/lib/opLifecycle/index.js +1 -1
  218. package/lib/opLifecycle/index.js.map +1 -1
  219. package/lib/opLifecycle/opCompressor.d.ts +1 -1
  220. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  221. package/lib/opLifecycle/opCompressor.js +24 -10
  222. package/lib/opLifecycle/opCompressor.js.map +1 -1
  223. package/lib/opLifecycle/opDecompressor.d.ts +2 -1
  224. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  225. package/lib/opLifecycle/opDecompressor.js +33 -17
  226. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  227. package/lib/opLifecycle/opSplitter.d.ts +34 -2
  228. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  229. package/lib/opLifecycle/opSplitter.js +116 -5
  230. package/lib/opLifecycle/opSplitter.js.map +1 -1
  231. package/lib/opLifecycle/outbox.d.ts +5 -0
  232. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  233. package/lib/opLifecycle/outbox.js +38 -27
  234. package/lib/opLifecycle/outbox.js.map +1 -1
  235. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  236. package/lib/opLifecycle/remoteMessageProcessor.js +17 -2
  237. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  238. package/lib/opProperties.d.ts.map +1 -1
  239. package/lib/opProperties.js +1 -3
  240. package/lib/opProperties.js.map +1 -1
  241. package/lib/orderedClientElection.d.ts.map +1 -1
  242. package/lib/orderedClientElection.js +10 -4
  243. package/lib/orderedClientElection.js.map +1 -1
  244. package/lib/packageVersion.d.ts +1 -1
  245. package/lib/packageVersion.js +1 -1
  246. package/lib/packageVersion.js.map +1 -1
  247. package/lib/pendingStateManager.d.ts +4 -13
  248. package/lib/pendingStateManager.d.ts.map +1 -1
  249. package/lib/pendingStateManager.js +134 -161
  250. package/lib/pendingStateManager.js.map +1 -1
  251. package/lib/runWhileConnectedCoordinator.d.ts.map +1 -1
  252. package/lib/runWhileConnectedCoordinator.js.map +1 -1
  253. package/lib/runningSummarizer.d.ts.map +1 -1
  254. package/lib/runningSummarizer.js +35 -23
  255. package/lib/runningSummarizer.js.map +1 -1
  256. package/lib/scheduleManager.d.ts +0 -1
  257. package/lib/scheduleManager.d.ts.map +1 -1
  258. package/lib/scheduleManager.js +11 -21
  259. package/lib/scheduleManager.js.map +1 -1
  260. package/lib/serializedSnapshotStorage.d.ts.map +1 -1
  261. package/lib/serializedSnapshotStorage.js +3 -1
  262. package/lib/serializedSnapshotStorage.js.map +1 -1
  263. package/lib/summarizer.d.ts +2 -3
  264. package/lib/summarizer.d.ts.map +1 -1
  265. package/lib/summarizer.js +39 -18
  266. package/lib/summarizer.js.map +1 -1
  267. package/lib/summarizerClientElection.d.ts +1 -2
  268. package/lib/summarizerClientElection.d.ts.map +1 -1
  269. package/lib/summarizerClientElection.js +3 -30
  270. package/lib/summarizerClientElection.js.map +1 -1
  271. package/lib/summarizerHandle.d.ts.map +1 -1
  272. package/lib/summarizerHandle.js.map +1 -1
  273. package/lib/summarizerHeuristics.d.ts.map +1 -1
  274. package/lib/summarizerHeuristics.js +6 -9
  275. package/lib/summarizerHeuristics.js.map +1 -1
  276. package/lib/summarizerTypes.d.ts +22 -25
  277. package/lib/summarizerTypes.d.ts.map +1 -1
  278. package/lib/summarizerTypes.js.map +1 -1
  279. package/lib/summaryCollection.d.ts.map +1 -1
  280. package/lib/summaryCollection.js +18 -8
  281. package/lib/summaryCollection.js.map +1 -1
  282. package/lib/summaryFormat.d.ts.map +1 -1
  283. package/lib/summaryFormat.js +20 -13
  284. package/lib/summaryFormat.js.map +1 -1
  285. package/lib/summaryGenerator.d.ts.map +1 -1
  286. package/lib/summaryGenerator.js +32 -14
  287. package/lib/summaryGenerator.js.map +1 -1
  288. package/lib/summaryManager.d.ts.map +1 -1
  289. package/lib/summaryManager.js +21 -9
  290. package/lib/summaryManager.js.map +1 -1
  291. package/lib/throttler.d.ts +2 -2
  292. package/lib/throttler.d.ts.map +1 -1
  293. package/lib/throttler.js +4 -4
  294. package/lib/throttler.js.map +1 -1
  295. package/package.json +27 -24
  296. package/prettier.config.cjs +1 -1
  297. package/src/batchTracker.ts +55 -50
  298. package/src/blobManager.ts +799 -593
  299. package/src/connectionTelemetry.ts +280 -249
  300. package/src/containerHandleContext.ts +27 -29
  301. package/src/containerRuntime.ts +3123 -2793
  302. package/src/dataStore.ts +172 -159
  303. package/src/dataStoreContext.ts +1048 -991
  304. package/src/dataStoreContexts.ts +178 -161
  305. package/src/dataStoreRegistry.ts +25 -20
  306. package/src/dataStores.ts +784 -711
  307. package/src/deltaScheduler.ts +158 -150
  308. package/src/garbageCollection.ts +1795 -1546
  309. package/src/garbageCollectionConstants.ts +10 -7
  310. package/src/garbageCollectionHelpers.ts +37 -0
  311. package/src/gcSweepReadyUsageDetection.ts +89 -83
  312. package/src/index.ts +67 -69
  313. package/src/opLifecycle/batchManager.ts +148 -86
  314. package/src/opLifecycle/definitions.ts +45 -19
  315. package/src/opLifecycle/index.ts +6 -5
  316. package/src/opLifecycle/opCompressor.ts +57 -39
  317. package/src/opLifecycle/opDecompressor.ts +104 -64
  318. package/src/opLifecycle/opSplitter.ts +226 -66
  319. package/src/opLifecycle/outbox.ts +206 -182
  320. package/src/opLifecycle/remoteMessageProcessor.ts +63 -47
  321. package/src/opProperties.ts +11 -9
  322. package/src/orderedClientElection.ts +489 -457
  323. package/src/packageVersion.ts +1 -1
  324. package/src/pendingStateManager.ts +379 -381
  325. package/src/runWhileConnectedCoordinator.ts +78 -71
  326. package/src/runningSummarizer.ts +619 -582
  327. package/src/scheduleManager.ts +299 -280
  328. package/src/serializedSnapshotStorage.ts +116 -111
  329. package/src/summarizer.ts +417 -381
  330. package/src/summarizerClientElection.ts +107 -129
  331. package/src/summarizerHandle.ts +11 -9
  332. package/src/summarizerHeuristics.ts +183 -186
  333. package/src/summarizerTypes.ts +344 -333
  334. package/src/summaryCollection.ts +378 -349
  335. package/src/summaryFormat.ts +146 -127
  336. package/src/summaryGenerator.ts +464 -406
  337. package/src/summaryManager.ts +377 -348
  338. package/src/throttler.ts +131 -122
  339. package/tsconfig.esnext.json +6 -6
  340. package/tsconfig.json +9 -13
@@ -26,10 +26,9 @@ const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
26
26
  const containerRuntime_1 = require("./containerRuntime");
27
27
  const dataStores_1 = require("./dataStores");
28
28
  const garbageCollectionConstants_1 = require("./garbageCollectionConstants");
29
+ const garbageCollectionHelpers_1 = require("./garbageCollectionHelpers");
29
30
  const gcSweepReadyUsageDetection_1 = require("./gcSweepReadyUsageDetection");
30
31
  const summaryFormat_1 = require("./summaryFormat");
31
- /** This is the current version of garbage collection. */
32
- const GCVersion = 1;
33
32
  /** The types of GC nodes in the GC reference graph. */
34
33
  exports.GCNodeType = {
35
34
  // Nodes that are for data stores.
@@ -143,25 +142,13 @@ exports.UnreferencedStateTracker = UnreferencedStateTracker;
143
142
  class GarbageCollector {
144
143
  constructor(createParams) {
145
144
  var _a, _b, _c, _d, _e, _f, _g, _h;
146
- /**
147
- * Tells whether the initial GC state needs to be reset. This can happen under 2 conditions:
148
- *
149
- * 1. The base snapshot contains GC state but GC is disabled. This will happen the first time GC is disabled after
150
- * it was enabled before. GC state needs to be removed from summary and all nodes should be marked referenced.
151
- *
152
- * 2. The base snapshot does not have GC state but GC is enabled. This will happen the very first time GC runs on
153
- * a document and the first time GC is enabled after is was disabled before.
154
- *
155
- * Note that the state needs reset only for the very first time summary is generated by this client. After that, the
156
- * state will be up-to-date and this flag will be reset.
157
- */
158
- this.initialStateNeedsReset = false;
159
- // The current GC version that this container is running.
160
- this.currentGCVersion = GCVersion;
161
145
  // Keeps a list of references (edges in the GC graph) between GC runs. Each entry has a node id and a list of
162
146
  // outbound routes from that node.
163
147
  this.newReferencesSinceLastRun = new Map();
148
+ // A list of nodes that have been tombstoned.
164
149
  this.tombstones = [];
150
+ // A list of nodes that have been deleted during sweep phase.
151
+ this.deletedNodes = new Set();
165
152
  // Map of node ids to their unreferenced state tracker.
166
153
  this.unreferencedNodesState = new Map();
167
154
  // Keeps track of unreferenced events that are logged for a node. This is used to limit the log generation to one
@@ -181,7 +168,14 @@ class GarbageCollector {
181
168
  const baseSnapshot = createParams.baseSnapshot;
182
169
  const metadata = createParams.metadata;
183
170
  const readAndParseBlob = createParams.readAndParseBlob;
184
- this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(createParams.baseLogger, "GarbageCollector", { all: { completedGCRuns: () => this.completedRuns } }));
171
+ this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(createParams.baseLogger, "GarbageCollector", {
172
+ all: { completedGCRuns: () => this.completedRuns },
173
+ }));
174
+ // If version upgrade is not enabled, fall back to the stable GC version.
175
+ this.currentGCVersion =
176
+ this.mc.config.getBoolean(garbageCollectionConstants_1.gcVersionUpgradeToV2Key) === true
177
+ ? garbageCollectionConstants_1.currentGCVersion
178
+ : garbageCollectionConstants_1.stableGCVersion;
185
179
  this.sweepReadyUsageHandler = new gcSweepReadyUsageDetection_1.SweepReadyUsageDetectionHandler(createParams.getContainerDiagnosticId(), this.mc, this.runtime.closeFn);
186
180
  let prevSummaryGCVersion;
187
181
  /**
@@ -195,8 +189,8 @@ class GarbageCollector {
195
189
  function computeSweepTimeout(sessionExpiryTimeoutMs) {
196
190
  const maxSnapshotCacheExpiryMs = 5 * garbageCollectionConstants_1.oneDayMs;
197
191
  const bufferMs = garbageCollectionConstants_1.oneDayMs;
198
- return sessionExpiryTimeoutMs &&
199
- (sessionExpiryTimeoutMs + maxSnapshotCacheExpiryMs + bufferMs);
192
+ return (sessionExpiryTimeoutMs &&
193
+ sessionExpiryTimeoutMs + maxSnapshotCacheExpiryMs + bufferMs);
200
194
  }
201
195
  /**
202
196
  * The following GC state is enabled during container creation and cannot be changed throughout its lifetime:
@@ -227,11 +221,11 @@ class GarbageCollector {
227
221
  // flag in GC options to false.
228
222
  this.gcEnabled = this.gcOptions.gcAllowed !== false;
229
223
  // The sweep phase has to be explicitly enabled by setting the sweepAllowed flag in GC options to true.
230
- // ...unless we're using the TestOverride
231
- this.sweepEnabled = this.gcOptions.sweepAllowed === true || testOverrideSweepTimeoutMs !== undefined;
224
+ this.sweepEnabled = this.gcOptions.sweepAllowed === true;
232
225
  // Set the Session Expiry only if the flag is enabled and GC is enabled.
233
226
  if (this.mc.config.getBoolean(garbageCollectionConstants_1.runSessionExpiryKey) && this.gcEnabled) {
234
- this.sessionExpiryTimeoutMs = (_c = this.gcOptions.sessionExpiryTimeoutMs) !== null && _c !== void 0 ? _c : garbageCollectionConstants_1.defaultSessionExpiryDurationMs;
227
+ this.sessionExpiryTimeoutMs =
228
+ (_c = this.gcOptions.sessionExpiryTimeoutMs) !== null && _c !== void 0 ? _c : garbageCollectionConstants_1.defaultSessionExpiryDurationMs;
235
229
  }
236
230
  this.sweepTimeoutMs =
237
231
  testOverrideSweepTimeoutMs !== null && testOverrideSweepTimeoutMs !== void 0 ? testOverrideSweepTimeoutMs : computeSweepTimeout(this.sessionExpiryTimeoutMs);
@@ -241,7 +235,9 @@ class GarbageCollector {
241
235
  // If Test Override config is set, override Session Expiry timeout.
242
236
  const overrideSessionExpiryTimeoutMs = this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.SessionExpiryMs");
243
237
  const timeoutMs = overrideSessionExpiryTimeoutMs !== null && overrideSessionExpiryTimeoutMs !== void 0 ? overrideSessionExpiryTimeoutMs : this.sessionExpiryTimeoutMs;
244
- this.sessionExpiryTimer = new common_utils_1.Timer(timeoutMs, () => { this.runtime.closeFn(new container_utils_1.ClientSessionExpiredError(`Client session expired.`, timeoutMs)); });
238
+ this.sessionExpiryTimer = new common_utils_1.Timer(timeoutMs, () => {
239
+ this.runtime.closeFn(new container_utils_1.ClientSessionExpiredError(`Client session expired.`, timeoutMs));
240
+ });
245
241
  this.sessionExpiryTimer.start();
246
242
  }
247
243
  // For existing document, the latest summary is the one that we loaded from. So, use its GC version as the
@@ -256,11 +252,12 @@ class GarbageCollector {
256
252
  *
257
253
  * These conditions can be overridden via runGCKey feature flag.
258
254
  */
259
- this.shouldRunGC = (_d = this.mc.config.getBoolean(garbageCollectionConstants_1.runGCKey)) !== null && _d !== void 0 ? _d : (
260
- // GC must be enabled for the document.
261
- this.gcEnabled
262
- // GC must not be disabled via GC options.
263
- && !this.gcOptions.disableGC);
255
+ this.shouldRunGC =
256
+ (_d = this.mc.config.getBoolean(garbageCollectionConstants_1.runGCKey)) !== null && _d !== void 0 ? _d :
257
+ // GC must be enabled for the document.
258
+ (this.gcEnabled &&
259
+ // GC must not be disabled via GC options.
260
+ !this.gcOptions.disableGC);
264
261
  /**
265
262
  * Whether sweep should run or not. The following conditions have to be met to run sweep:
266
263
  *
@@ -272,24 +269,26 @@ class GarbageCollector {
272
269
  * feature flag.
273
270
  */
274
271
  this.shouldRunSweep =
275
- this.shouldRunGC
276
- && this.sweepTimeoutMs !== undefined
277
- && ((_e = this.mc.config.getBoolean(garbageCollectionConstants_1.runSweepKey)) !== null && _e !== void 0 ? _e : this.sweepEnabled);
272
+ this.shouldRunGC &&
273
+ this.sweepTimeoutMs !== undefined &&
274
+ ((_e = this.mc.config.getBoolean(garbageCollectionConstants_1.runSweepKey)) !== null && _e !== void 0 ? _e : this.sweepEnabled);
278
275
  this.trackGCState = this.mc.config.getBoolean(garbageCollectionConstants_1.trackGCStateKey) === true;
279
276
  // Override inactive timeout if test config or gc options to override it is set.
280
- this.inactiveTimeoutMs = (_g = (_f = this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs")) !== null && _f !== void 0 ? _f : this.gcOptions.inactiveTimeoutMs) !== null && _g !== void 0 ? _g : garbageCollectionConstants_1.defaultInactiveTimeoutMs;
277
+ this.inactiveTimeoutMs =
278
+ (_g = (_f = this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs")) !== null && _f !== void 0 ? _f : this.gcOptions.inactiveTimeoutMs) !== null && _g !== void 0 ? _g : garbageCollectionConstants_1.defaultInactiveTimeoutMs;
281
279
  // Inactive timeout must be greater than sweep timeout since a node goes from active -> inactive -> sweep ready.
282
280
  if (this.sweepTimeoutMs !== undefined && this.inactiveTimeoutMs > this.sweepTimeoutMs) {
283
281
  throw new container_utils_1.UsageError("inactive timeout should not be greater than the sweep timeout");
284
282
  }
285
283
  // Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
286
- this.testMode = (_h = this.mc.config.getBoolean(garbageCollectionConstants_1.gcTestModeKey)) !== null && _h !== void 0 ? _h : this.gcOptions.runGCInTestMode === true;
287
- // Whether we are running in tombstone mode. This is true by default unless disabled via feature flags.
288
- this.tombstoneMode = this.mc.config.getBoolean(garbageCollectionConstants_1.disableTombstoneKey) !== true;
289
- // The GC state needs to be reset if the base snapshot contains GC tree and GC is disabled or it doesn't
290
- // contain GC tree and GC is enabled.
291
- const gcTreePresent = (baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.trees[garbageCollectionConstants_1.gcTreeKey]) !== undefined;
292
- this.initialStateNeedsReset = gcTreePresent !== this.shouldRunGC;
284
+ this.testMode =
285
+ (_h = this.mc.config.getBoolean(garbageCollectionConstants_1.gcTestModeKey)) !== null && _h !== void 0 ? _h : this.gcOptions.runGCInTestMode === true;
286
+ // Whether we are running in tombstone mode. This is enabled by default if sweep won't run. It can be disabled
287
+ // via feature flags.
288
+ this.tombstoneMode =
289
+ !this.shouldRunSweep && this.mc.config.getBoolean(garbageCollectionConstants_1.disableTombstoneKey) !== true;
290
+ // If GC ran in the container that generated the base snapshot, it will have a GC tree.
291
+ this.wasGCRunInLatestSummary = (baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.trees[runtime_definitions_1.gcTreeKey]) !== undefined;
293
292
  // Get the GC data from the base snapshot. Use LazyPromise because we only want to do this once since it
294
293
  // it involves fetching blobs from storage which is expensive.
295
294
  this.baseSnapshotDataP = new common_utils_1.LazyPromise(async () => {
@@ -299,18 +298,20 @@ class GarbageCollector {
299
298
  }
300
299
  try {
301
300
  // For newer documents, GC data should be present in the GC tree in the root of the snapshot.
302
- const gcSnapshotTree = baseSnapshot.trees[garbageCollectionConstants_1.gcTreeKey];
301
+ const gcSnapshotTree = baseSnapshot.trees[runtime_definitions_1.gcTreeKey];
303
302
  if (gcSnapshotTree !== undefined) {
304
- return getGCDataFromSnapshot(gcSnapshotTree, readAndParseBlob);
303
+ return (0, garbage_collector_1.getGCDataFromSnapshot)(gcSnapshotTree, readAndParseBlob);
305
304
  }
306
305
  // back-compat - Older documents will have the GC blobs in each data store's summary tree. Get them and
307
306
  // consolidate into IGarbageCollectionState format.
308
307
  // Add a node for the root node that is not present in older snapshot format.
309
- const gcState = { gcNodes: { "/": { outboundRoutes: [] } } };
308
+ const gcState = {
309
+ gcNodes: { "/": { outboundRoutes: [] } },
310
+ };
310
311
  const dataStoreSnapshotTree = (0, dataStores_1.getSummaryForDatastores)(baseSnapshot, metadata);
311
312
  (0, common_utils_1.assert)(dataStoreSnapshotTree !== undefined, 0x2a8 /* "Expected data store snapshot tree in base snapshot" */);
312
313
  for (const [dsId, dsSnapshotTree] of Object.entries(dataStoreSnapshotTree.trees)) {
313
- const blobId = dsSnapshotTree.blobs[runtime_definitions_1.gcBlobKey];
314
+ const blobId = dsSnapshotTree.blobs[runtime_definitions_1.gcTreeKey];
314
315
  if (blobId === undefined) {
315
316
  continue;
316
317
  }
@@ -330,14 +331,19 @@ class GarbageCollector {
330
331
  // Prefix the data store id to the GC node ids to make them relative to the root from being
331
332
  // relative to the data store. Similar to how its done in DataStore::getGCData.
332
333
  const rootId = id === "/" ? dsRootId : `${dsRootId}${id}`;
333
- gcState.gcNodes[rootId] = { outboundRoutes: Array.from(outboundRoutes) };
334
+ gcState.gcNodes[rootId] = {
335
+ outboundRoutes: Array.from(outboundRoutes),
336
+ };
334
337
  }
335
338
  (0, common_utils_1.assert)(gcState.gcNodes[dsRootId] !== undefined, 0x2a9 /* GC nodes for data store not in GC blob */);
336
- gcState.gcNodes[dsRootId].unreferencedTimestampMs = gcSummaryDetails.unrefTimestamp;
339
+ gcState.gcNodes[dsRootId].unreferencedTimestampMs =
340
+ gcSummaryDetails.unrefTimestamp;
337
341
  }
338
342
  // If there is only one node (root node just added above), either GC is disabled or we are loading from
339
343
  // the first summary generated by detached container. In both cases, GC was not run - return undefined.
340
- return Object.keys(gcState.gcNodes).length === 1 ? undefined : { gcState, tombstones: undefined };
344
+ return Object.keys(gcState.gcNodes).length === 1
345
+ ? undefined
346
+ : { gcState, tombstones: undefined, deletedNodes: undefined };
341
347
  }
342
348
  catch (error) {
343
349
  const dpe = container_utils_1.DataProcessingError.wrapIfUnrecognized(error, "FailedToInitializeGC");
@@ -351,7 +357,6 @@ class GarbageCollector {
351
357
  * GC state and updates their inactive or sweep ready state.
352
358
  */
353
359
  this.initializeGCStateFromBaseSnapshotP = new common_utils_1.LazyPromise(async () => {
354
- const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();
355
360
  /**
356
361
  * If there is no current reference timestamp, skip initialization. We need the current timestamp to track
357
362
  * how long objects have been unreferenced and if they can be deleted.
@@ -360,6 +365,7 @@ class GarbageCollector {
360
365
  * for this container and it is in read mode. In this scenario, there is no point in running GC anyway
361
366
  * because references in the container do not change without any ops, i.e., there is nothing to collect.
362
367
  */
368
+ const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();
363
369
  if (currentReferenceTimestampMs === undefined) {
364
370
  // Log an event so we can evaluate how often we run into this scenario.
365
371
  this.mc.logger.sendErrorEvent({
@@ -368,38 +374,24 @@ class GarbageCollector {
368
374
  });
369
375
  return;
370
376
  }
371
- const baseSnapshotData = await this.baseSnapshotDataP;
372
377
  /**
373
378
  * The base snapshot data will not be present if the container is loaded from:
374
379
  * 1. The first summary created by the detached container.
375
380
  * 2. A summary that was generated with GC disabled.
376
381
  * 3. A summary that was generated before GC even existed.
377
382
  */
383
+ const baseSnapshotData = await this.baseSnapshotDataP;
378
384
  if (baseSnapshotData === undefined) {
379
385
  return;
380
386
  }
381
- const gcNodes = {};
382
- for (const [nodeId, nodeData] of Object.entries(baseSnapshotData.gcState.gcNodes)) {
383
- if (nodeData.unreferencedTimestampMs !== undefined) {
384
- this.unreferencedNodesState.set(nodeId, new UnreferencedStateTracker(nodeData.unreferencedTimestampMs, this.inactiveTimeoutMs, currentReferenceTimestampMs, this.sweepTimeoutMs));
385
- }
386
- gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);
387
- }
388
- this.previousGCDataFromLastRun = { gcNodes };
389
- // If tracking state across summaries, update latest summary data from the base snapshot's GC data.
390
- if (this.trackGCState) {
391
- this.latestSummaryData = {
392
- serializedGCState: JSON.stringify(generateSortedGCState(baseSnapshotData.gcState)),
393
- serializedTombstones: JSON.stringify(baseSnapshotData.tombstones),
394
- };
395
- }
387
+ this.updateStateFromSnapshotData(baseSnapshotData, currentReferenceTimestampMs);
396
388
  });
397
- // Get the GC details for each node from the GC state in the base summary. This is returned in getBaseGCDetails
398
- // which the caller uses to initialize each node's GC state.
389
+ // Get the GC details from the GC state in the base summary. This is returned in getBaseGCDetails which is
390
+ // used to initialize the GC state of all the nodes in the container.
399
391
  this.baseGCDetailsP = new common_utils_1.LazyPromise(async () => {
400
392
  const baseSnapshotData = await this.baseSnapshotDataP;
401
393
  if (baseSnapshotData === undefined) {
402
- return new Map();
394
+ return {};
403
395
  }
404
396
  const gcNodes = {};
405
397
  for (const [nodeId, nodeData] of Object.entries(baseSnapshotData.gcState.gcNodes)) {
@@ -409,18 +401,7 @@ class GarbageCollector {
409
401
  // This is an optimization for space (vs performance) wherein we don't need to store the used routes of
410
402
  // each node in the summary.
411
403
  const usedRoutes = (0, garbage_collector_1.runGarbageCollection)(gcNodes, ["/"]).referencedNodeIds;
412
- const baseGCDetailsMap = (0, garbage_collector_1.unpackChildNodesGCDetails)({ gcData: { gcNodes }, usedRoutes });
413
- // Currently, the nodes may write the GC data. So, we need to update its base GC details with the
414
- // unreferenced timestamp. Once we start writing the GC data here, we won't need to do this anymore.
415
- for (const [nodeId, nodeData] of Object.entries(baseSnapshotData.gcState.gcNodes)) {
416
- if (nodeData.unreferencedTimestampMs !== undefined) {
417
- const dataStoreGCDetails = baseGCDetailsMap.get(nodeId.slice(1));
418
- if (dataStoreGCDetails !== undefined) {
419
- dataStoreGCDetails.unrefTimestamp = nodeData.unreferencedTimestampMs;
420
- }
421
- }
422
- }
423
- return baseGCDetailsMap;
404
+ return { gcData: { gcNodes }, usedRoutes };
424
405
  });
425
406
  // Log all the GC options and the state determined by the garbage collector. This is interesting only for the
426
407
  // summarizer client since it is the only one that runs GC. It also helps keep the telemetry less noisy.
@@ -441,39 +422,143 @@ class GarbageCollector {
441
422
  *
442
423
  * 2. GC was disabled and is now enabled. The GC state needs to be regenerated and added to summary.
443
424
  *
444
- * 3. The GC version in the latest summary is different from the current GC version. This can happen if:
425
+ * 3. GC is enabled and the latest summary state is refreshed from a snapshot that had GC disabled and vice-versa.
426
+ *
427
+ * 4. The GC version in the latest summary is different from the current GC version. This can happen if:
445
428
  *
446
- * 3.1. The summary this client loaded with has data from a different GC version.
429
+ * 4.1. The summary this client loaded with has data from a different GC version.
447
430
  *
448
- * 3.2. This client's latest summary was updated from a snapshot that has a different GC version.
431
+ * 4.2. This client's latest summary was updated from a snapshot that has a different GC version.
449
432
  */
450
433
  get summaryStateNeedsReset() {
451
- return this.initialStateNeedsReset ||
452
- (this.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion);
434
+ return (this.gcStateNeedsReset ||
435
+ (this.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion));
436
+ }
437
+ /**
438
+ * Tells whether the GC state needs to be reset. This can happen under 3 conditions:
439
+ *
440
+ * 1. The base snapshot contains GC state but GC is disabled. This will happen the first time GC is disabled after
441
+ * it was enabled before. GC state needs to be removed from summary and all nodes should be marked referenced.
442
+ *
443
+ * 2. The base snapshot does not have GC state but GC is enabled. This will happen the very first time GC runs on
444
+ * a document and the first time GC is enabled after is was disabled before.
445
+ *
446
+ * 3. GC is enabled and the latest summary state is refreshed from a snapshot that had GC disabled and vice-versa.
447
+ *
448
+ * Note that the state will be reset only once for the first summary generated after this returns true. After that,
449
+ * this will return false.
450
+ */
451
+ get gcStateNeedsReset() {
452
+ return this.wasGCRunInLatestSummary !== this.shouldRunGC;
453
453
  }
454
454
  /** Returns a list of all the configurations for garbage collection. */
455
455
  get configs() {
456
456
  return Object.assign({ gcEnabled: this.gcEnabled, sweepEnabled: this.sweepEnabled, runGC: this.shouldRunGC, runSweep: this.shouldRunSweep, testMode: this.testMode, tombstoneMode: this.tombstoneMode, sessionExpiry: this.sessionExpiryTimeoutMs, sweepTimeout: this.sweepTimeoutMs, inactiveTimeout: this.inactiveTimeoutMs, trackGCState: this.trackGCState }, this.gcOptions);
457
457
  }
458
458
  /**
459
- * Called during container initialization. Initialize the tombstone state so that object are marked as tombstones
460
- * before they are loaded or used. This is important to get accurate information of whether tombstoned object are
461
- * in use or not.
459
+ * Called during container initialization. Initialize from the tombstone state in the base snapshot. This is done
460
+ * during initialization so that deleted or tombstoned objects are marked as such before they are loaded or used.
462
461
  */
463
462
  async initializeBaseState() {
464
463
  const baseSnapshotData = await this.baseSnapshotDataP;
465
464
  /**
466
- * The base snapshot data or tombstone state will not be present if the container is loaded from:
465
+ * The base snapshot data will not be present if the container is loaded from:
467
466
  * 1. The first summary created by the detached container.
468
467
  * 2. A summary that was generated with GC disabled.
469
468
  * 3. A summary that was generated before GC even existed.
470
- * 4. A summary that was generated with tombstone feature disabled.
471
469
  */
472
- if (!this.tombstoneMode || (baseSnapshotData === null || baseSnapshotData === void 0 ? void 0 : baseSnapshotData.tombstones) === undefined) {
470
+ if (baseSnapshotData === undefined) {
473
471
  return;
474
472
  }
475
- this.tombstones = baseSnapshotData.tombstones;
476
- this.runtime.updateUnusedRoutes(this.tombstones, true /* tombstone */);
473
+ // Initialize the deleted nodes from the snapshot. This is done irrespective of whether sweep is enabled or not
474
+ // to identify deleted nodes' usage.
475
+ if (baseSnapshotData.deletedNodes !== undefined) {
476
+ this.deletedNodes = new Set(baseSnapshotData.deletedNodes);
477
+ }
478
+ // If running in tombstone mode, initialize the tombstone state from the snapshot. Also, notify the runtime of
479
+ // tombstone routes.
480
+ if (this.tombstoneMode && baseSnapshotData.tombstones !== undefined) {
481
+ this.tombstones = Array.from(baseSnapshotData.tombstones);
482
+ this.runtime.updateTombstonedRoutes(this.tombstones);
483
+ }
484
+ }
485
+ /**
486
+ * Update state from the given snapshot data. This is done during load and during refreshing state from a snapshot.
487
+ * All current tracking is reset and updated from the data in the snapshot.
488
+ * @param snapshotData - The snapshot data to update state from. If this is undefined, all GC state and tracking
489
+ * is reset.
490
+ * @param currentReferenceTimestampMs - The current reference timestamp for marking unreferenced nodes' unreferenced
491
+ * timestamp.
492
+ */
493
+ updateStateFromSnapshotData(snapshotData, currentReferenceTimestampMs) {
494
+ /**
495
+ * Note: "newReferencesSinceLastRun" is not reset here. This is done because there may be references since the
496
+ * snapshot that we are updating state from. For example, this client may have processed ops till seq#1000 and
497
+ * its refreshing state from a summary that happened at seq#900. In this case, there may be references between
498
+ * seq#901 and seq#1000 that we don't want to reset.
499
+ * Unfortunately, there is no way to track the seq# of ops that add references, so we choose to not reset any
500
+ * references here. This should be fine because, in the worst case, we may end up updating the unreferenced
501
+ * timestamp of a node which will delay its deletion. Although not ideal, this will only happen in rare
502
+ * scenarios, so it should be okay.
503
+ */
504
+ // Clear all existing unreferenced state tracking.
505
+ for (const [, nodeStateTracker] of this.unreferencedNodesState) {
506
+ nodeStateTracker.stopTracking();
507
+ }
508
+ this.unreferencedNodesState.clear();
509
+ // If running sweep, the tombstone state represents the list of nodes that have been deleted during sweep.
510
+ // If running in tombstone mode, the tombstone state represents the list of nodes that have been marked as
511
+ // tombstones.
512
+ // If this call is because we are refreshing from a snapshot due to an ack, it is likely that the GC state
513
+ // in the snapshot is newer than this client's. And so, the deleted / tombstone nodes need to be updated.
514
+ if (this.shouldRunSweep) {
515
+ const snapshotDeletedNodes = (snapshotData === null || snapshotData === void 0 ? void 0 : snapshotData.tombstones)
516
+ ? new Set(snapshotData.tombstones)
517
+ : undefined;
518
+ // If the snapshot contains deleted nodes that are not yet deleted by this client, ask the runtime to
519
+ // delete them.
520
+ if (snapshotDeletedNodes !== undefined) {
521
+ const newDeletedNodes = [];
522
+ for (const nodeId of snapshotDeletedNodes) {
523
+ if (!this.deletedNodes.has(nodeId)) {
524
+ newDeletedNodes.push(nodeId);
525
+ }
526
+ }
527
+ if (newDeletedNodes.length > 0) {
528
+ // Call container runtime to delete these nodes and add deleted nodes to this.deletedNodes.
529
+ }
530
+ }
531
+ }
532
+ else if (this.tombstoneMode) {
533
+ // The snapshot may contain more or fewer tombstone nodes than this client. Update tombstone state and
534
+ // notify the runtime to update its state as well.
535
+ this.tombstones = (snapshotData === null || snapshotData === void 0 ? void 0 : snapshotData.tombstones) ? Array.from(snapshotData.tombstones) : [];
536
+ this.runtime.updateTombstonedRoutes(this.tombstones);
537
+ }
538
+ // If there is no snapshot data, it means this snapshot was generated with GC disabled. Unset all GC state.
539
+ if (snapshotData === undefined) {
540
+ this.gcDataFromLastRun = undefined;
541
+ this.latestSummaryData = undefined;
542
+ return;
543
+ }
544
+ // Update unreferenced state tracking as per the GC state in the snapshot data and update gcDataFromLastRun
545
+ // to the GC data from the snapshot data.
546
+ const gcNodes = {};
547
+ for (const [nodeId, nodeData] of Object.entries(snapshotData.gcState.gcNodes)) {
548
+ if (nodeData.unreferencedTimestampMs !== undefined) {
549
+ this.unreferencedNodesState.set(nodeId, new UnreferencedStateTracker(nodeData.unreferencedTimestampMs, this.inactiveTimeoutMs, currentReferenceTimestampMs, this.sweepTimeoutMs));
550
+ }
551
+ gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);
552
+ }
553
+ this.gcDataFromLastRun = { gcNodes };
554
+ // If tracking state across summaries, update latest summary data from the snapshot's GC data.
555
+ if (this.trackGCState) {
556
+ this.latestSummaryData = {
557
+ serializedGCState: JSON.stringify(generateSortedGCState(snapshotData.gcState)),
558
+ serializedTombstones: JSON.stringify(snapshotData.tombstones),
559
+ serializedDeletedNodes: JSON.stringify(snapshotData.deletedNodes),
560
+ };
561
+ }
477
562
  }
478
563
  /**
479
564
  * Called when the connection state of the runtime changes, i.e., it connects or disconnects. GC subscribes to this
@@ -507,7 +592,9 @@ class GarbageCollector {
507
592
  var _a;
508
593
  const fullGC = (_a = options.fullGC) !== null && _a !== void 0 ? _a : (this.gcOptions.runFullGC === true || this.summaryStateNeedsReset);
509
594
  const logger = options.logger
510
- ? telemetry_utils_1.ChildLogger.create(options.logger, undefined, { all: { completedGCRuns: () => this.completedRuns } })
595
+ ? telemetry_utils_1.ChildLogger.create(options.logger, undefined, {
596
+ all: { completedGCRuns: () => this.completedRuns },
597
+ })
511
598
  : this.mc.logger;
512
599
  /**
513
600
  * If there is no current reference timestamp, skip running GC. We need the current timestamp to track
@@ -559,13 +646,13 @@ class GarbageCollector {
559
646
  // If we are running in GC test mode, delete objects for unused routes. This enables testing scenarios
560
647
  // involving access to deleted data.
561
648
  if (this.testMode) {
562
- this.runtime.updateUnusedRoutes(gcResult.deletedNodeIds, false /* tombstone */);
649
+ this.runtime.updateUnusedRoutes(gcResult.deletedNodeIds);
563
650
  }
564
651
  else if (this.tombstoneMode) {
565
- // If we are running in GC tombstone mode, tombstone objects for unused routes. This enables testing
566
- // scenarios involving access to "deleted" data without actually deleting the data from summaries.
567
- // Note: we will not tombstone in test mode
568
- this.runtime.updateUnusedRoutes(this.tombstones, true /* tombstone */);
652
+ // If we are running in GC tombstone mode, update tombstoned routes. This enables testing scenarios
653
+ // involving access to "deleted" data without actually deleting the data from summaries.
654
+ // Note: we will not tombstone in test mode.
655
+ this.runtime.updateTombstonedRoutes(this.tombstones);
569
656
  }
570
657
  // Log pending unreferenced events such as a node being used after inactive. This is done after GC runs and
571
658
  // updates its state so that we don't send false positives based on intermediate state. For example, we may get
@@ -580,78 +667,105 @@ class GarbageCollector {
580
667
  */
581
668
  summarize(fullTree, trackState, telemetryContext) {
582
669
  var _a;
583
- if (!this.shouldRunGC || this.previousGCDataFromLastRun === undefined) {
670
+ if (!this.shouldRunGC || this.gcDataFromLastRun === undefined) {
584
671
  return;
585
672
  }
586
673
  const gcState = { gcNodes: {} };
587
- for (const [nodeId, outboundRoutes] of Object.entries(this.previousGCDataFromLastRun.gcNodes)) {
674
+ for (const [nodeId, outboundRoutes] of Object.entries(this.gcDataFromLastRun.gcNodes)) {
588
675
  gcState.gcNodes[nodeId] = {
589
676
  outboundRoutes,
590
677
  unreferencedTimestampMs: (_a = this.unreferencedNodesState.get(nodeId)) === null || _a === void 0 ? void 0 : _a.unreferencedTimestampMs,
591
678
  };
592
679
  }
593
680
  const serializedGCState = JSON.stringify(generateSortedGCState(gcState));
681
+ // Serialize and write deleted nodes, if any. This is done irrespective of whether sweep is enabled or not so
682
+ // to identify deleted nodes' usage.
683
+ const serializedDeletedNodes = this.deletedNodes.size > 0
684
+ ? JSON.stringify(Array.from(this.deletedNodes).sort())
685
+ : undefined;
686
+ // If running in tombstone mode, serialize and write tombstones, if any.
594
687
  const serializedTombstones = this.tombstoneMode
595
- ? (this.tombstones.length > 0 ? JSON.stringify(this.tombstones.sort()) : undefined)
688
+ ? this.tombstones.length > 0
689
+ ? JSON.stringify(this.tombstones.sort())
690
+ : undefined
596
691
  : undefined;
597
692
  /**
598
- * Incremental summary of GC data - If any of the GC state or tombstone state hasn't changed since the last
599
- * summary, send summary handles for them. Otherwise, send the data in summary blobs.
693
+ * Incremental summary of GC data - If none of GC state, deleted nodes or tombstones changed since last summary,
694
+ * write summary handle instead of summary tree for GC.
695
+ * Otherwise, write the GC summary tree. In the tree, for each of these that changed, write a summary blob and
696
+ * for each of these that did not change, write a summary handle.
600
697
  */
601
698
  if (this.trackGCState) {
602
- this.pendingSummaryData = { serializedGCState, serializedTombstones };
699
+ this.pendingSummaryData = {
700
+ serializedGCState,
701
+ serializedTombstones,
702
+ serializedDeletedNodes,
703
+ };
603
704
  if (trackState && !fullTree && this.latestSummaryData !== undefined) {
604
- // If neither GC state or tombstone state changed, send a summary handle for the entire GC data.
605
- if (this.latestSummaryData.serializedGCState === serializedGCState
606
- && this.latestSummaryData.serializedTombstones === serializedTombstones) {
705
+ // If nothing changed since last summary, send a summary handle for the entire GC data.
706
+ if (this.latestSummaryData.serializedGCState === serializedGCState &&
707
+ this.latestSummaryData.serializedTombstones === serializedTombstones) {
607
708
  const stats = (0, runtime_utils_1.mergeStats)();
608
709
  stats.handleNodeCount++;
609
710
  return {
610
711
  summary: {
611
712
  type: protocol_definitions_1.SummaryType.Handle,
612
- handle: `/${garbageCollectionConstants_1.gcTreeKey}`,
713
+ handle: `/${runtime_definitions_1.gcTreeKey}`,
613
714
  handleType: protocol_definitions_1.SummaryType.Tree,
614
715
  },
615
716
  stats,
616
717
  };
617
718
  }
618
- // If either or both of GC state or tombstone state changed, build a GC summary tree.
619
- return this.buildGCSummaryTree(serializedGCState, serializedTombstones, true /* trackState */);
719
+ // If some state changed, build a GC summary tree.
720
+ return this.buildGCSummaryTree(serializedGCState, serializedTombstones, serializedDeletedNodes, true /* trackState */);
620
721
  }
621
722
  }
622
723
  // If not tracking GC state, build a GC summary tree without any summary handles.
623
- return this.buildGCSummaryTree(serializedGCState, serializedTombstones, false /* trackState */);
724
+ return this.buildGCSummaryTree(serializedGCState, serializedTombstones, serializedDeletedNodes, false /* trackState */);
624
725
  }
625
726
  /**
626
- * Builds the GC summary tree which contains GC state and tombstone state.
627
- * If trackState is false, both GC state and tombstone state are written as summary blobs.
628
- * If trackState is true, summary blob is written for GC state or tombstone state if they changed.
727
+ * Builds the GC summary tree which contains GC state, deleted nodes and tombstones.
728
+ * If trackState is false, all of GC state, deleted nodes and tombstones are written as summary blobs.
729
+ * If trackState is true, only states that changed are written. Rest are written as handles.
629
730
  * @param serializedGCState - The GC state serialized as string.
630
- * @param serializedTombstones - THe tombstone state serialized as string.
731
+ * @param serializedTombstones - The tombstone state serialized as string.
732
+ * @param serializedDeletedNodes - Deleted nodes serialized as string.
631
733
  * @param trackState - Whether we are tracking GC state across summaries.
632
734
  * @returns the GC summary tree.
633
735
  */
634
- buildGCSummaryTree(serializedGCState, serializedTombstones, trackState) {
635
- var _a, _b;
636
- const gcStateBlobKey = `${garbageCollectionConstants_1.gcBlobPrefix}_root`;
736
+ buildGCSummaryTree(serializedGCState, serializedTombstones, serializedDeletedNodes, trackState) {
737
+ var _a, _b, _c;
738
+ const gcStateBlobKey = `${runtime_definitions_1.gcBlobPrefix}_root`;
637
739
  const builder = new runtime_utils_1.SummaryTreeBuilder();
638
740
  // If the GC state hasn't changed, write a summary handle, else write a summary blob for it.
639
741
  if (((_a = this.latestSummaryData) === null || _a === void 0 ? void 0 : _a.serializedGCState) === serializedGCState && trackState) {
640
- builder.addHandle(gcStateBlobKey, protocol_definitions_1.SummaryType.Blob, `/${garbageCollectionConstants_1.gcTreeKey}/${gcStateBlobKey}`);
742
+ builder.addHandle(gcStateBlobKey, protocol_definitions_1.SummaryType.Blob, `/${runtime_definitions_1.gcTreeKey}/${gcStateBlobKey}`);
641
743
  }
642
744
  else {
643
745
  builder.addBlob(gcStateBlobKey, serializedGCState);
644
746
  }
645
- // If there is no tombstone data, return only the GC state.
646
- if (serializedTombstones === undefined) {
747
+ // If tombstones exist, write a summary handle if it hasn't changed. If it has changed, write a
748
+ // summary blob.
749
+ if (serializedTombstones !== undefined) {
750
+ if (((_b = this.latestSummaryData) === null || _b === void 0 ? void 0 : _b.serializedTombstones) === serializedTombstones &&
751
+ trackState) {
752
+ builder.addHandle(runtime_definitions_1.gcTombstoneBlobKey, protocol_definitions_1.SummaryType.Blob, `/${runtime_definitions_1.gcTreeKey}/${runtime_definitions_1.gcTombstoneBlobKey}`);
753
+ }
754
+ else {
755
+ builder.addBlob(runtime_definitions_1.gcTombstoneBlobKey, serializedTombstones);
756
+ }
757
+ }
758
+ // If there are no deleted nodes, return the summary tree.
759
+ if (serializedDeletedNodes === undefined) {
647
760
  return builder.getSummaryTree();
648
761
  }
649
- // If the tombstone state hasn't changed, write a summary handle, else write a summary blob for it.
650
- if (((_b = this.latestSummaryData) === null || _b === void 0 ? void 0 : _b.serializedTombstones) === serializedTombstones && trackState) {
651
- builder.addHandle(garbageCollectionConstants_1.gcTombstoneBlobKey, protocol_definitions_1.SummaryType.Blob, `/${garbageCollectionConstants_1.gcTreeKey}/${garbageCollectionConstants_1.gcTombstoneBlobKey}`);
762
+ // If the deleted nodes hasn't changed, write a summary handle, else write a summary blob for it.
763
+ if (((_c = this.latestSummaryData) === null || _c === void 0 ? void 0 : _c.serializedDeletedNodes) === serializedDeletedNodes &&
764
+ trackState) {
765
+ builder.addHandle(runtime_definitions_1.gcDeletedBlobKey, protocol_definitions_1.SummaryType.Blob, `/${runtime_definitions_1.gcTreeKey}/${runtime_definitions_1.gcDeletedBlobKey}`);
652
766
  }
653
767
  else {
654
- builder.addBlob(garbageCollectionConstants_1.gcTombstoneBlobKey, serializedTombstones);
768
+ builder.addBlob(runtime_definitions_1.gcDeletedBlobKey, serializedDeletedNodes);
655
769
  }
656
770
  return builder.getSummaryTree();
657
771
  }
@@ -668,50 +782,58 @@ class GarbageCollector {
668
782
  };
669
783
  }
670
784
  /**
671
- * Returns a map of node ids to their base GC details generated from the base summary. This is used by the caller
672
- * to initialize the GC state of the nodes.
785
+ * Returns a the GC details generated from the base summary. This is used to initialize the GC state of the nodes
786
+ * in the container.
673
787
  */
674
788
  async getBaseGCDetails() {
675
789
  return this.baseGCDetailsP;
676
790
  }
677
791
  /**
678
- * Called when the latest summary of the system has been refreshed. This will be used to update the state of the
679
- * latest summary tracked.
792
+ * Called to refresh the latest summary state. This happens when either a pending summary is acked or a snapshot
793
+ * is downloaded and should be used to update the state.
680
794
  */
681
- async latestSummaryStateRefreshed(result, readAndParseBlob) {
682
- if (!this.shouldRunGC || !result.latestSummaryUpdated) {
795
+ async refreshLatestSummary(result, proposalHandle, summaryRefSeq, readAndParseBlob) {
796
+ // If the latest summary was updated and the summary was tracked, this client is the one that generated this
797
+ // summary. So, update wasGCRunInLatestSummary.
798
+ // Note that this has to be updated if GC did not run too. Otherwise, `gcStateNeedsReset` will always return
799
+ // true in scenarios where GC is disabled but enabled in the snapshot we loaded from.
800
+ if (result.latestSummaryUpdated && result.wasSummaryTracked) {
801
+ this.wasGCRunInLatestSummary = this.shouldRunGC;
802
+ }
803
+ if (!result.latestSummaryUpdated || !this.shouldRunGC) {
683
804
  return;
684
805
  }
685
806
  // If the summary was tracked by this client, it was the one that generated the summary in the first place.
686
- // Basically, it was written in the current GC version.
807
+ // Update latest state from pending.
687
808
  if (result.wasSummaryTracked) {
688
809
  this.latestSummaryGCVersion = this.currentGCVersion;
689
- this.initialStateNeedsReset = false;
690
810
  if (this.trackGCState) {
691
811
  this.latestSummaryData = this.pendingSummaryData;
692
812
  this.pendingSummaryData = undefined;
693
813
  }
694
814
  return;
695
815
  }
696
- // If the summary was not tracked by this client, update latest GC version and blob from the snapshot in the
697
- // result as that is now the latest summary.
816
+ // If the summary was not tracked by this client, the state should be updated from the downloaded snapshot.
698
817
  const snapshot = result.snapshot;
699
818
  const metadataBlobId = snapshot.blobs[summaryFormat_1.metadataBlobName];
700
819
  if (metadataBlobId) {
701
820
  const metadata = await readAndParseBlob(metadataBlobId);
702
821
  this.latestSummaryGCVersion = (0, summaryFormat_1.getGCVersion)(metadata);
703
822
  }
704
- const gcSnapshotTree = snapshot.trees[garbageCollectionConstants_1.gcTreeKey];
705
- if (gcSnapshotTree !== undefined && this.trackGCState) {
706
- const latestGCData = await getGCDataFromSnapshot(gcSnapshotTree, readAndParseBlob);
707
- this.latestSummaryData = {
708
- serializedGCState: JSON.stringify(generateSortedGCState(latestGCData.gcState)),
709
- serializedTombstones: JSON.stringify(latestGCData.tombstones),
710
- };
823
+ // The current reference timestamp should be available if we are refreshing state from a snapshot. There has
824
+ // to be at least one op (summary op / ack, if nothing else) if a snapshot was taken.
825
+ const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();
826
+ if (currentReferenceTimestampMs === undefined) {
827
+ throw container_utils_1.DataProcessingError.create("No reference timestamp when updating GC state from snapshot", "refreshLatestSummary", undefined, { proposalHandle, summaryRefSeq, details: JSON.stringify(this.configs) });
711
828
  }
712
- else {
713
- this.latestSummaryData = undefined;
829
+ const gcSnapshotTree = snapshot.trees[runtime_definitions_1.gcTreeKey];
830
+ // If GC ran in the container that generated this snapshot, it will have a GC tree.
831
+ this.wasGCRunInLatestSummary = gcSnapshotTree !== undefined;
832
+ let latestGCData;
833
+ if (gcSnapshotTree !== undefined) {
834
+ latestGCData = await (0, garbage_collector_1.getGCDataFromSnapshot)(gcSnapshotTree, readAndParseBlob);
714
835
  }
836
+ this.updateStateFromSnapshotData(latestGCData, currentReferenceTimestampMs);
715
837
  this.pendingSummaryData = undefined;
716
838
  }
717
839
  /**
@@ -750,6 +872,30 @@ class GarbageCollector {
750
872
  if (nodeStateTracker && nodeStateTracker.state !== exports.UnreferencedState.Active) {
751
873
  this.inactiveNodeUsed("Revived", toNodePath, nodeStateTracker, fromNodePath);
752
874
  }
875
+ if (this.tombstones.includes(toNodePath)) {
876
+ const nodeType = this.runtime.getNodeType(toNodePath);
877
+ let eventName = "GC_Tombstone_SubDatastore_Revived";
878
+ if (nodeType === exports.GCNodeType.DataStore) {
879
+ eventName = "GC_Tombstone_Datastore_Revived";
880
+ }
881
+ else if (nodeType === exports.GCNodeType.Blob) {
882
+ eventName = "GC_Tombstone_Blob_Revived";
883
+ }
884
+ (0, garbageCollectionHelpers_1.sendGCUnexpectedUsageEvent)(this.mc, {
885
+ eventName,
886
+ category: "generic",
887
+ isSummarizerClient: this.isSummarizerClient,
888
+ url: (0, garbage_collector_1.trimLeadingSlashes)(toNodePath),
889
+ nodeType,
890
+ }, undefined /* packagePath */);
891
+ }
892
+ }
893
+ /**
894
+ * Returns whether a node with the given path has been deleted or not. This can be used by the runtime to identify
895
+ * cases where objects are used after they are deleted and throw / log errors accordingly.
896
+ */
897
+ isNodeDeleted(nodePath) {
898
+ return this.deletedNodes.has(nodePath);
753
899
  }
754
900
  dispose() {
755
901
  var _a;
@@ -766,7 +912,7 @@ class GarbageCollector {
766
912
  * @param currentReferenceTimestampMs - The timestamp to be used for unreferenced nodes' timestamp.
767
913
  */
768
914
  updateCurrentState(gcData, gcResult, currentReferenceTimestampMs) {
769
- this.previousGCDataFromLastRun = (0, garbage_collector_1.cloneGCData)(gcData);
915
+ this.gcDataFromLastRun = (0, garbage_collector_1.cloneGCData)(gcData);
770
916
  this.tombstones = [];
771
917
  this.newReferencesSinceLastRun.clear();
772
918
  // Iterate through the referenced nodes and stop tracking if they were unreferenced before.
@@ -815,19 +961,18 @@ class GarbageCollector {
815
961
  */
816
962
  updateStateSinceLastRun(currentGCData, logger) {
817
963
  // If we haven't run GC before there is nothing to do.
818
- if (this.previousGCDataFromLastRun === undefined) {
964
+ if (this.gcDataFromLastRun === undefined) {
819
965
  return;
820
966
  }
821
967
  // Find any references that haven't been identified correctly.
822
- const missingExplicitReferences = this.findMissingExplicitReferences(currentGCData, this.previousGCDataFromLastRun, this.newReferencesSinceLastRun);
968
+ const missingExplicitReferences = this.findMissingExplicitReferences(currentGCData, this.gcDataFromLastRun, this.newReferencesSinceLastRun);
823
969
  if (missingExplicitReferences.length > 0) {
824
970
  missingExplicitReferences.forEach((missingExplicitReference) => {
825
- const event = {
971
+ logger.sendErrorEvent({
826
972
  eventName: "gcUnknownOutboundReferences",
827
973
  gcNodeId: missingExplicitReference[0],
828
974
  gcRoutes: JSON.stringify(missingExplicitReference[1]),
829
- };
830
- logger.sendPerformanceEvent(event);
975
+ });
831
976
  });
832
977
  }
833
978
  // No references were added since the last run so we don't have to update reference states of any unreferenced
@@ -851,7 +996,7 @@ class GarbageCollector {
851
996
  * - We don't require DDSes handles to be stored in a referenced DDS.
852
997
  * - A new data store may have "root" DDSes already created and we don't detect them today.
853
998
  */
854
- const gcDataSuperSet = (0, garbage_collector_1.concatGarbageCollectionData)(this.previousGCDataFromLastRun, currentGCData);
999
+ const gcDataSuperSet = (0, garbage_collector_1.concatGarbageCollectionData)(this.gcDataFromLastRun, currentGCData);
855
1000
  const newOutboundRoutesSinceLastRun = [];
856
1001
  this.newReferencesSinceLastRun.forEach((outboundRoutes, sourceNodeId) => {
857
1002
  if (gcDataSuperSet.gcNodes[sourceNodeId] === undefined) {
@@ -869,7 +1014,10 @@ class GarbageCollector {
869
1014
  * Note that some of these nodes may be unreferenced now and if so, the current run will mark them as
870
1015
  * unreferenced and add unreferenced state.
871
1016
  */
872
- const gcResult = (0, garbage_collector_1.runGarbageCollection)(gcDataSuperSet.gcNodes, ["/", ...newOutboundRoutesSinceLastRun]);
1017
+ const gcResult = (0, garbage_collector_1.runGarbageCollection)(gcDataSuperSet.gcNodes, [
1018
+ "/",
1019
+ ...newOutboundRoutesSinceLastRun,
1020
+ ]);
873
1021
  for (const nodeId of gcResult.referencedNodeIds) {
874
1022
  const nodeStateTracker = this.unreferencedNodesState.get(nodeId);
875
1023
  if (nodeStateTracker !== undefined) {
@@ -894,7 +1042,7 @@ class GarbageCollector {
894
1042
  * @returns - a list of missing explicit references
895
1043
  */
896
1044
  findMissingExplicitReferences(currentGCData, previousGCData, explicitReferences) {
897
- (0, common_utils_1.assert)(previousGCData !== undefined, 0x2b7);
1045
+ (0, common_utils_1.assert)(previousGCData !== undefined, 0x2b7 /* "Can't validate correctness without GC data from last run" */);
898
1046
  const currentGraph = Object.entries(currentGCData.gcNodes);
899
1047
  const missingExplicitReferences = [];
900
1048
  currentGraph.forEach(([nodeId, currentOutboundRoutes]) => {
@@ -902,14 +1050,20 @@ class GarbageCollector {
902
1050
  const previousRoutes = (_a = previousGCData.gcNodes[nodeId]) !== null && _a !== void 0 ? _a : [];
903
1051
  const explicitRoutes = (_b = explicitReferences.get(nodeId)) !== null && _b !== void 0 ? _b : [];
904
1052
  const missingExplicitRoutes = [];
1053
+ /**
1054
+ * 1. For routes in the current GC data, routes that were not present in previous GC data and did not have
1055
+ * explicit references should be added to missing explicit routes list.
1056
+ * 2. Only include data store and blob routes since GC only works for these two.
1057
+ * Note: Due to a bug with de-duped blobs, only adding data store routes for now.
1058
+ * 3. Ignore DDS routes to their parent datastores since those were added implicitly. So, there won't be
1059
+ * explicit routes to them.
1060
+ */
905
1061
  currentOutboundRoutes.forEach((route) => {
906
- const isBlobOrDataStoreRoute = this.runtime.getNodeType(route) === exports.GCNodeType.Blob ||
907
- this.runtime.getNodeType(route) === exports.GCNodeType.DataStore;
908
- // Ignore implicitly added DDS routes to their parent datastores
909
- const notRouteFromDDSToParentDataStore = !nodeId.startsWith(route);
910
- if (isBlobOrDataStoreRoute &&
911
- notRouteFromDDSToParentDataStore &&
912
- (!previousRoutes.includes(route) && !explicitRoutes.includes(route))) {
1062
+ const nodeType = this.runtime.getNodeType(route);
1063
+ if ((nodeType === exports.GCNodeType.DataStore || nodeType === exports.GCNodeType.Blob) &&
1064
+ !nodeId.startsWith(route) &&
1065
+ !previousRoutes.includes(route) &&
1066
+ !explicitRoutes.includes(route)) {
913
1067
  missingExplicitRoutes.push(route);
914
1068
  }
915
1069
  });
@@ -941,7 +1095,7 @@ class GarbageCollector {
941
1095
  gcStats.nodeCount++;
942
1096
  // If there is no previous GC data, every node's state is generated and is considered as updated.
943
1097
  // Otherwise, find out if any node went from referenced to unreferenced or vice-versa.
944
- const stateUpdated = this.previousGCDataFromLastRun === undefined ||
1098
+ const stateUpdated = this.gcDataFromLastRun === undefined ||
945
1099
  this.unreferencedNodesState.has(nodeId) === referenced;
946
1100
  if (stateUpdated) {
947
1101
  gcStats.updatedNodeCount++;
@@ -981,7 +1135,8 @@ class GarbageCollector {
981
1135
  * this will give us a view into how much deleted content a container has.
982
1136
  */
983
1137
  logSweepEvents(logger, currentReferenceTimestampMs) {
984
- if (this.mc.config.getBoolean(garbageCollectionConstants_1.disableSweepLogKey) === true || this.sweepTimeoutMs === undefined) {
1138
+ if (this.mc.config.getBoolean(garbageCollectionConstants_1.disableSweepLogKey) === true ||
1139
+ this.sweepTimeoutMs === undefined) {
985
1140
  return;
986
1141
  }
987
1142
  this.unreferencedNodesState.forEach((nodeStateTracker, nodeId) => {
@@ -1016,7 +1171,8 @@ class GarbageCollector {
1016
1171
  // If there is no reference timestamp to work with, no ops have been processed after creation. If so, skip
1017
1172
  // logging as nothing interesting would have happened worth logging.
1018
1173
  // If the node is active, skip logging.
1019
- if (currentReferenceTimestampMs === undefined || nodeStateTracker.state === exports.UnreferencedState.Active) {
1174
+ if (currentReferenceTimestampMs === undefined ||
1175
+ nodeStateTracker.state === exports.UnreferencedState.Active) {
1020
1176
  return;
1021
1177
  }
1022
1178
  // We only care about data stores and attachment blobs for this telemetry since GC only marks these objects
@@ -1048,9 +1204,17 @@ class GarbageCollector {
1048
1204
  // Events generated:
1049
1205
  // InactiveObject_Loaded, SweepReadyObject_Loaded
1050
1206
  if (usageType === "Loaded") {
1051
- this.mc.logger.sendErrorEvent(Object.assign(Object.assign({}, propsToLog), { eventName: `${state}Object_${usageType}`, pkg: (0, runtime_utils_1.packagePathToTelemetryProperty)(packagePath), stack: (0, telemetry_utils_1.generateStack)() }));
1207
+ const event = Object.assign(Object.assign({}, propsToLog), { eventName: `${state}Object_${usageType}`, pkg: (0, runtime_utils_1.packagePathToTelemetryProperty)(packagePath), stack: (0, telemetry_utils_1.generateStack)() });
1208
+ // Do not log the inactive object x events as error events as they are not the best signal for
1209
+ // detecting something wrong with GC either from the partner or from the runtime itself.
1210
+ if (state === exports.UnreferencedState.Inactive) {
1211
+ this.mc.logger.sendTelemetryEvent(event);
1212
+ }
1213
+ else {
1214
+ this.mc.logger.sendErrorEvent(event);
1215
+ }
1052
1216
  }
1053
- // If SweepReady Usage Detection is enabed, the handler may close the interactive container.
1217
+ // If SweepReady Usage Detection is enabled, the handler may close the interactive container.
1054
1218
  // Once Sweep is fully implemented, this will be removed since the objects will be gone
1055
1219
  // and errors will arise elsewhere in the runtime
1056
1220
  if (state === exports.UnreferencedState.SweepReady) {
@@ -1073,44 +1237,30 @@ class GarbageCollector {
1073
1237
  * revived and a Revived event will be logged for it.
1074
1238
  */
1075
1239
  const nodeStateTracker = this.unreferencedNodesState.get(eventProps.id);
1076
- const active = nodeStateTracker === undefined || nodeStateTracker.state === exports.UnreferencedState.Active;
1240
+ const active = nodeStateTracker === undefined ||
1241
+ nodeStateTracker.state === exports.UnreferencedState.Active;
1077
1242
  if ((usageType === "Revived") === active) {
1078
1243
  const pkg = await this.getNodePackagePath(eventProps.id);
1079
- const fromPkg = eventProps.fromId ? await this.getNodePackagePath(eventProps.fromId) : undefined;
1080
- logger.sendErrorEvent(Object.assign(Object.assign({}, propsToLog), { eventName: `${state}Object_${usageType}`, pkg: pkg ? { value: pkg.join("/"), tag: telemetry_utils_1.TelemetryDataTag.CodeArtifact } : undefined, fromPkg: fromPkg ? { value: fromPkg.join("/"), tag: telemetry_utils_1.TelemetryDataTag.CodeArtifact } : undefined }));
1244
+ const fromPkg = eventProps.fromId
1245
+ ? await this.getNodePackagePath(eventProps.fromId)
1246
+ : undefined;
1247
+ const event = Object.assign(Object.assign({}, propsToLog), { eventName: `${state}Object_${usageType}`, pkg: pkg
1248
+ ? { value: pkg.join("/"), tag: telemetry_utils_1.TelemetryDataTag.CodeArtifact }
1249
+ : undefined, fromPkg: fromPkg
1250
+ ? { value: fromPkg.join("/"), tag: telemetry_utils_1.TelemetryDataTag.CodeArtifact }
1251
+ : undefined });
1252
+ if (state === exports.UnreferencedState.Inactive) {
1253
+ logger.sendTelemetryEvent(event);
1254
+ }
1255
+ else {
1256
+ logger.sendErrorEvent(event);
1257
+ }
1081
1258
  }
1082
1259
  }
1083
1260
  this.pendingEventsQueue = [];
1084
1261
  }
1085
1262
  }
1086
1263
  exports.GarbageCollector = GarbageCollector;
1087
- /**
1088
- * Gets the base garbage collection state from the given snapshot tree. It contains GC state and tombstone state.
1089
- * The GC state may be written into multiple blobs. Merge the GC state from all such blobs into one.
1090
- */
1091
- async function getGCDataFromSnapshot(gcSnapshotTree, readAndParseBlob) {
1092
- let rootGCState = { gcNodes: {} };
1093
- let tombstones;
1094
- for (const key of Object.keys(gcSnapshotTree.blobs)) {
1095
- if (key === garbageCollectionConstants_1.gcTombstoneBlobKey) {
1096
- tombstones = await readAndParseBlob(gcSnapshotTree.blobs[key]);
1097
- continue;
1098
- }
1099
- // Skip blobs that do not start with the GC prefix.
1100
- if (!key.startsWith(garbageCollectionConstants_1.gcBlobPrefix)) {
1101
- continue;
1102
- }
1103
- const blobId = gcSnapshotTree.blobs[key];
1104
- if (blobId === undefined) {
1105
- continue;
1106
- }
1107
- const gcState = await readAndParseBlob(blobId);
1108
- (0, common_utils_1.assert)(gcState !== undefined, 0x2ad /* "GC blob missing from snapshot" */);
1109
- // Merge the GC state of this blob into the root GC state.
1110
- rootGCState = (0, garbage_collector_1.concatGarbageCollectionStates)(rootGCState, gcState);
1111
- }
1112
- return { gcState: rootGCState, tombstones };
1113
- }
1114
1264
  function generateSortedGCState(gcState) {
1115
1265
  const sortableArray = Object.entries(gcState.gcNodes);
1116
1266
  sortableArray.sort(([a], [b]) => a.localeCompare(b));
@@ -1125,7 +1275,9 @@ function generateSortedGCState(gcState) {
1125
1275
  class TimerWithNoDefaultTimeout extends common_utils_1.Timer {
1126
1276
  constructor(callback) {
1127
1277
  // The default timeout/handlers will never be used since start/restart pass overrides below
1128
- super(0, () => { throw new Error("DefaultHandler should not be used"); });
1278
+ super(0, () => {
1279
+ throw new Error("DefaultHandler should not be used");
1280
+ });
1129
1281
  this.callback = callback;
1130
1282
  }
1131
1283
  start(timeoutMs) {