@fluidframework/container-runtime 2.0.0-internal.3.0.0 → 2.0.0-internal.3.1.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 (327) hide show
  1. package/.eslintrc.js +19 -19
  2. package/.mocharc.js +2 -2
  3. package/api-extractor.json +2 -2
  4. package/dist/batchTracker.d.ts.map +1 -1
  5. package/dist/batchTracker.js +2 -1
  6. package/dist/batchTracker.js.map +1 -1
  7. package/dist/blobManager.d.ts +9 -2
  8. package/dist/blobManager.d.ts.map +1 -1
  9. package/dist/blobManager.js +80 -33
  10. package/dist/blobManager.js.map +1 -1
  11. package/dist/connectionTelemetry.d.ts.map +1 -1
  12. package/dist/connectionTelemetry.js +11 -9
  13. package/dist/connectionTelemetry.js.map +1 -1
  14. package/dist/containerHandleContext.d.ts.map +1 -1
  15. package/dist/containerHandleContext.js +3 -1
  16. package/dist/containerHandleContext.js.map +1 -1
  17. package/dist/containerRuntime.d.ts +10 -0
  18. package/dist/containerRuntime.d.ts.map +1 -1
  19. package/dist/containerRuntime.js +140 -72
  20. package/dist/containerRuntime.js.map +1 -1
  21. package/dist/dataStore.d.ts.map +1 -1
  22. package/dist/dataStore.js +11 -9
  23. package/dist/dataStore.js.map +1 -1
  24. package/dist/dataStoreContext.d.ts +18 -1
  25. package/dist/dataStoreContext.d.ts.map +1 -1
  26. package/dist/dataStoreContext.js +66 -15
  27. package/dist/dataStoreContext.js.map +1 -1
  28. package/dist/dataStoreContexts.d.ts.map +1 -1
  29. package/dist/dataStoreContexts.js +7 -3
  30. package/dist/dataStoreContexts.js.map +1 -1
  31. package/dist/dataStoreRegistry.d.ts.map +1 -1
  32. package/dist/dataStoreRegistry.js +3 -1
  33. package/dist/dataStoreRegistry.js.map +1 -1
  34. package/dist/dataStores.d.ts +26 -1
  35. package/dist/dataStores.d.ts.map +1 -1
  36. package/dist/dataStores.js +103 -18
  37. package/dist/dataStores.js.map +1 -1
  38. package/dist/deltaScheduler.d.ts.map +1 -1
  39. package/dist/deltaScheduler.js +8 -3
  40. package/dist/deltaScheduler.js.map +1 -1
  41. package/dist/garbageCollection.d.ts +34 -14
  42. package/dist/garbageCollection.d.ts.map +1 -1
  43. package/dist/garbageCollection.js +188 -93
  44. package/dist/garbageCollection.js.map +1 -1
  45. package/dist/garbageCollectionConstants.d.ts +3 -0
  46. package/dist/garbageCollectionConstants.d.ts.map +1 -1
  47. package/dist/garbageCollectionConstants.js +6 -1
  48. package/dist/garbageCollectionConstants.js.map +1 -1
  49. package/dist/garbageCollectionHelpers.d.ts +26 -0
  50. package/dist/garbageCollectionHelpers.d.ts.map +1 -0
  51. package/dist/garbageCollectionHelpers.js +45 -0
  52. package/dist/garbageCollectionHelpers.js.map +1 -0
  53. package/dist/gcSweepReadyUsageDetection.d.ts +5 -5
  54. package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
  55. package/dist/gcSweepReadyUsageDetection.js +14 -10
  56. package/dist/gcSweepReadyUsageDetection.js.map +1 -1
  57. package/dist/index.d.ts +2 -2
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js.map +1 -1
  60. package/dist/opLifecycle/batchManager.d.ts +5 -5
  61. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  62. package/dist/opLifecycle/batchManager.js +19 -12
  63. package/dist/opLifecycle/batchManager.js.map +1 -1
  64. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  65. package/dist/opLifecycle/definitions.js.map +1 -1
  66. package/dist/opLifecycle/index.d.ts.map +1 -1
  67. package/dist/opLifecycle/index.js.map +1 -1
  68. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  69. package/dist/opLifecycle/opCompressor.js.map +1 -1
  70. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  71. package/dist/opLifecycle/opDecompressor.js +5 -2
  72. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  73. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  74. package/dist/opLifecycle/opSplitter.js +4 -1
  75. package/dist/opLifecycle/opSplitter.js.map +1 -1
  76. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  77. package/dist/opLifecycle/outbox.js +19 -17
  78. package/dist/opLifecycle/outbox.js.map +1 -1
  79. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  80. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  81. package/dist/opProperties.d.ts.map +1 -1
  82. package/dist/opProperties.js +1 -3
  83. package/dist/opProperties.js.map +1 -1
  84. package/dist/orderedClientElection.d.ts.map +1 -1
  85. package/dist/orderedClientElection.js +10 -4
  86. package/dist/orderedClientElection.js.map +1 -1
  87. package/dist/packageVersion.d.ts +1 -1
  88. package/dist/packageVersion.js +1 -1
  89. package/dist/packageVersion.js.map +1 -1
  90. package/dist/pendingStateManager.d.ts +7 -0
  91. package/dist/pendingStateManager.d.ts.map +1 -1
  92. package/dist/pendingStateManager.js +7 -4
  93. package/dist/pendingStateManager.js.map +1 -1
  94. package/dist/runWhileConnectedCoordinator.d.ts.map +1 -1
  95. package/dist/runWhileConnectedCoordinator.js.map +1 -1
  96. package/dist/runningSummarizer.d.ts.map +1 -1
  97. package/dist/runningSummarizer.js +34 -21
  98. package/dist/runningSummarizer.js.map +1 -1
  99. package/dist/scheduleManager.d.ts.map +1 -1
  100. package/dist/scheduleManager.js +3 -2
  101. package/dist/scheduleManager.js.map +1 -1
  102. package/dist/serializedSnapshotStorage.d.ts +2 -2
  103. package/dist/serializedSnapshotStorage.d.ts.map +1 -1
  104. package/dist/serializedSnapshotStorage.js +5 -3
  105. package/dist/serializedSnapshotStorage.js.map +1 -1
  106. package/dist/summarizer.d.ts +2 -2
  107. package/dist/summarizer.d.ts.map +1 -1
  108. package/dist/summarizer.js +37 -17
  109. package/dist/summarizer.js.map +1 -1
  110. package/dist/summarizerClientElection.d.ts.map +1 -1
  111. package/dist/summarizerClientElection.js.map +1 -1
  112. package/dist/summarizerHandle.d.ts.map +1 -1
  113. package/dist/summarizerHandle.js.map +1 -1
  114. package/dist/summarizerHeuristics.d.ts.map +1 -1
  115. package/dist/summarizerHeuristics.js +6 -9
  116. package/dist/summarizerHeuristics.js.map +1 -1
  117. package/dist/summarizerTypes.d.ts +21 -21
  118. package/dist/summarizerTypes.d.ts.map +1 -1
  119. package/dist/summarizerTypes.js.map +1 -1
  120. package/dist/summaryCollection.d.ts.map +1 -1
  121. package/dist/summaryCollection.js +18 -8
  122. package/dist/summaryCollection.js.map +1 -1
  123. package/dist/summaryFormat.d.ts +22 -0
  124. package/dist/summaryFormat.d.ts.map +1 -1
  125. package/dist/summaryFormat.js +18 -10
  126. package/dist/summaryFormat.js.map +1 -1
  127. package/dist/summaryGenerator.d.ts.map +1 -1
  128. package/dist/summaryGenerator.js +34 -15
  129. package/dist/summaryGenerator.js.map +1 -1
  130. package/dist/summaryManager.d.ts.map +1 -1
  131. package/dist/summaryManager.js +21 -9
  132. package/dist/summaryManager.js.map +1 -1
  133. package/dist/throttler.d.ts +2 -2
  134. package/dist/throttler.d.ts.map +1 -1
  135. package/dist/throttler.js +4 -4
  136. package/dist/throttler.js.map +1 -1
  137. package/garbageCollection.md +15 -2
  138. package/lib/batchTracker.d.ts.map +1 -1
  139. package/lib/batchTracker.js +2 -1
  140. package/lib/batchTracker.js.map +1 -1
  141. package/lib/blobManager.d.ts +9 -2
  142. package/lib/blobManager.d.ts.map +1 -1
  143. package/lib/blobManager.js +82 -35
  144. package/lib/blobManager.js.map +1 -1
  145. package/lib/connectionTelemetry.d.ts.map +1 -1
  146. package/lib/connectionTelemetry.js +11 -9
  147. package/lib/connectionTelemetry.js.map +1 -1
  148. package/lib/containerHandleContext.d.ts.map +1 -1
  149. package/lib/containerHandleContext.js +3 -1
  150. package/lib/containerHandleContext.js.map +1 -1
  151. package/lib/containerRuntime.d.ts +10 -0
  152. package/lib/containerRuntime.d.ts.map +1 -1
  153. package/lib/containerRuntime.js +146 -78
  154. package/lib/containerRuntime.js.map +1 -1
  155. package/lib/dataStore.d.ts.map +1 -1
  156. package/lib/dataStore.js +11 -9
  157. package/lib/dataStore.js.map +1 -1
  158. package/lib/dataStoreContext.d.ts +18 -1
  159. package/lib/dataStoreContext.d.ts.map +1 -1
  160. package/lib/dataStoreContext.js +68 -17
  161. package/lib/dataStoreContext.js.map +1 -1
  162. package/lib/dataStoreContexts.d.ts.map +1 -1
  163. package/lib/dataStoreContexts.js +7 -3
  164. package/lib/dataStoreContexts.js.map +1 -1
  165. package/lib/dataStoreRegistry.d.ts.map +1 -1
  166. package/lib/dataStoreRegistry.js +3 -1
  167. package/lib/dataStoreRegistry.js.map +1 -1
  168. package/lib/dataStores.d.ts +26 -1
  169. package/lib/dataStores.d.ts.map +1 -1
  170. package/lib/dataStores.js +109 -24
  171. package/lib/dataStores.js.map +1 -1
  172. package/lib/deltaScheduler.d.ts.map +1 -1
  173. package/lib/deltaScheduler.js +9 -4
  174. package/lib/deltaScheduler.js.map +1 -1
  175. package/lib/garbageCollection.d.ts +34 -14
  176. package/lib/garbageCollection.d.ts.map +1 -1
  177. package/lib/garbageCollection.js +190 -95
  178. package/lib/garbageCollection.js.map +1 -1
  179. package/lib/garbageCollectionConstants.d.ts +3 -0
  180. package/lib/garbageCollectionConstants.d.ts.map +1 -1
  181. package/lib/garbageCollectionConstants.js +5 -0
  182. package/lib/garbageCollectionConstants.js.map +1 -1
  183. package/lib/garbageCollectionHelpers.d.ts +26 -0
  184. package/lib/garbageCollectionHelpers.d.ts.map +1 -0
  185. package/lib/garbageCollectionHelpers.js +40 -0
  186. package/lib/garbageCollectionHelpers.js.map +1 -0
  187. package/lib/gcSweepReadyUsageDetection.d.ts +5 -5
  188. package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
  189. package/lib/gcSweepReadyUsageDetection.js +14 -10
  190. package/lib/gcSweepReadyUsageDetection.js.map +1 -1
  191. package/lib/index.d.ts +2 -2
  192. package/lib/index.d.ts.map +1 -1
  193. package/lib/index.js +1 -1
  194. package/lib/index.js.map +1 -1
  195. package/lib/opLifecycle/batchManager.d.ts +5 -5
  196. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  197. package/lib/opLifecycle/batchManager.js +19 -12
  198. package/lib/opLifecycle/batchManager.js.map +1 -1
  199. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  200. package/lib/opLifecycle/definitions.js.map +1 -1
  201. package/lib/opLifecycle/index.d.ts.map +1 -1
  202. package/lib/opLifecycle/index.js.map +1 -1
  203. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  204. package/lib/opLifecycle/opCompressor.js.map +1 -1
  205. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  206. package/lib/opLifecycle/opDecompressor.js +5 -2
  207. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  208. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  209. package/lib/opLifecycle/opSplitter.js +5 -2
  210. package/lib/opLifecycle/opSplitter.js.map +1 -1
  211. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  212. package/lib/opLifecycle/outbox.js +19 -17
  213. package/lib/opLifecycle/outbox.js.map +1 -1
  214. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  215. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  216. package/lib/opProperties.d.ts.map +1 -1
  217. package/lib/opProperties.js +1 -3
  218. package/lib/opProperties.js.map +1 -1
  219. package/lib/orderedClientElection.d.ts.map +1 -1
  220. package/lib/orderedClientElection.js +10 -4
  221. package/lib/orderedClientElection.js.map +1 -1
  222. package/lib/packageVersion.d.ts +1 -1
  223. package/lib/packageVersion.js +1 -1
  224. package/lib/packageVersion.js.map +1 -1
  225. package/lib/pendingStateManager.d.ts +7 -0
  226. package/lib/pendingStateManager.d.ts.map +1 -1
  227. package/lib/pendingStateManager.js +7 -4
  228. package/lib/pendingStateManager.js.map +1 -1
  229. package/lib/runWhileConnectedCoordinator.d.ts.map +1 -1
  230. package/lib/runWhileConnectedCoordinator.js.map +1 -1
  231. package/lib/runningSummarizer.d.ts.map +1 -1
  232. package/lib/runningSummarizer.js +35 -22
  233. package/lib/runningSummarizer.js.map +1 -1
  234. package/lib/scheduleManager.d.ts.map +1 -1
  235. package/lib/scheduleManager.js +3 -2
  236. package/lib/scheduleManager.js.map +1 -1
  237. package/lib/serializedSnapshotStorage.d.ts +2 -2
  238. package/lib/serializedSnapshotStorage.d.ts.map +1 -1
  239. package/lib/serializedSnapshotStorage.js +5 -3
  240. package/lib/serializedSnapshotStorage.js.map +1 -1
  241. package/lib/summarizer.d.ts +2 -2
  242. package/lib/summarizer.d.ts.map +1 -1
  243. package/lib/summarizer.js +37 -17
  244. package/lib/summarizer.js.map +1 -1
  245. package/lib/summarizerClientElection.d.ts.map +1 -1
  246. package/lib/summarizerClientElection.js.map +1 -1
  247. package/lib/summarizerHandle.d.ts.map +1 -1
  248. package/lib/summarizerHandle.js.map +1 -1
  249. package/lib/summarizerHeuristics.d.ts.map +1 -1
  250. package/lib/summarizerHeuristics.js +6 -9
  251. package/lib/summarizerHeuristics.js.map +1 -1
  252. package/lib/summarizerTypes.d.ts +21 -21
  253. package/lib/summarizerTypes.d.ts.map +1 -1
  254. package/lib/summarizerTypes.js.map +1 -1
  255. package/lib/summaryCollection.d.ts.map +1 -1
  256. package/lib/summaryCollection.js +18 -8
  257. package/lib/summaryCollection.js.map +1 -1
  258. package/lib/summaryFormat.d.ts +22 -0
  259. package/lib/summaryFormat.d.ts.map +1 -1
  260. package/lib/summaryFormat.js +20 -12
  261. package/lib/summaryFormat.js.map +1 -1
  262. package/lib/summaryGenerator.d.ts.map +1 -1
  263. package/lib/summaryGenerator.js +34 -15
  264. package/lib/summaryGenerator.js.map +1 -1
  265. package/lib/summaryManager.d.ts.map +1 -1
  266. package/lib/summaryManager.js +21 -9
  267. package/lib/summaryManager.js.map +1 -1
  268. package/lib/throttler.d.ts +2 -2
  269. package/lib/throttler.d.ts.map +1 -1
  270. package/lib/throttler.js +4 -4
  271. package/lib/throttler.js.map +1 -1
  272. package/package.json +121 -149
  273. package/prettier.config.cjs +1 -1
  274. package/src/batchTracker.ts +54 -49
  275. package/src/blobManager.ts +793 -672
  276. package/src/connectionTelemetry.ts +280 -249
  277. package/src/containerHandleContext.ts +27 -29
  278. package/src/containerRuntime.ts +3168 -2940
  279. package/src/dataStore.ts +172 -159
  280. package/src/dataStoreContext.ts +1098 -996
  281. package/src/dataStoreContexts.ts +178 -161
  282. package/src/dataStoreRegistry.ts +25 -20
  283. package/src/dataStores.ts +884 -728
  284. package/src/deltaScheduler.ts +158 -150
  285. package/src/garbageCollection.ts +1883 -1692
  286. package/src/garbageCollectionConstants.ts +6 -0
  287. package/src/garbageCollectionHelpers.ts +61 -0
  288. package/src/gcSweepReadyUsageDetection.ts +89 -83
  289. package/src/index.ts +67 -66
  290. package/src/opLifecycle/README.md +152 -0
  291. package/src/opLifecycle/batchManager.ts +145 -141
  292. package/src/opLifecycle/definitions.ts +29 -29
  293. package/src/opLifecycle/index.ts +5 -5
  294. package/src/opLifecycle/opCompressor.ts +54 -53
  295. package/src/opLifecycle/opDecompressor.ts +100 -81
  296. package/src/opLifecycle/opSplitter.ts +214 -188
  297. package/src/opLifecycle/outbox.ts +204 -194
  298. package/src/opLifecycle/remoteMessageProcessor.ts +62 -62
  299. package/src/opProperties.ts +11 -9
  300. package/src/orderedClientElection.ts +489 -457
  301. package/src/packageVersion.ts +1 -1
  302. package/src/pendingStateManager.ts +384 -338
  303. package/src/runWhileConnectedCoordinator.ts +78 -71
  304. package/src/runningSummarizer.ts +619 -581
  305. package/src/scheduleManager.ts +299 -269
  306. package/src/serializedSnapshotStorage.ts +126 -112
  307. package/src/summarizer.ts +417 -381
  308. package/src/summarizerClientElection.ts +107 -100
  309. package/src/summarizerHandle.ts +11 -9
  310. package/src/summarizerHeuristics.ts +183 -186
  311. package/src/summarizerTypes.ts +344 -330
  312. package/src/summaryCollection.ts +378 -349
  313. package/src/summaryFormat.ts +170 -126
  314. package/src/summaryGenerator.ts +465 -406
  315. package/src/summaryManager.ts +377 -348
  316. package/src/throttler.ts +131 -122
  317. package/tsconfig.esnext.json +6 -6
  318. package/tsconfig.json +9 -13
  319. package/dist/garbageCollectionTombstoneUtils.d.ts +0 -14
  320. package/dist/garbageCollectionTombstoneUtils.d.ts.map +0 -1
  321. package/dist/garbageCollectionTombstoneUtils.js +0 -23
  322. package/dist/garbageCollectionTombstoneUtils.js.map +0 -1
  323. package/lib/garbageCollectionTombstoneUtils.d.ts +0 -14
  324. package/lib/garbageCollectionTombstoneUtils.d.ts.map +0 -1
  325. package/lib/garbageCollectionTombstoneUtils.js +0 -19
  326. package/lib/garbageCollectionTombstoneUtils.js.map +0 -1
  327. package/src/garbageCollectionTombstoneUtils.ts +0 -28
@@ -5,51 +5,53 @@
5
5
 
6
6
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
7
  import {
8
- assert,
9
- Deferred,
10
- IPromiseTimer,
11
- IPromiseTimerResult,
12
- Timer,
8
+ assert,
9
+ Deferred,
10
+ IPromiseTimer,
11
+ IPromiseTimerResult,
12
+ Timer,
13
13
  } from "@fluidframework/common-utils";
14
14
  import { MessageType } from "@fluidframework/protocol-definitions";
15
15
  import { PerformanceEvent, LoggingError, ChildLogger } from "@fluidframework/telemetry-utils";
16
16
  import { getRetryDelaySecondsFromError } from "@fluidframework/driver-utils";
17
17
  import { DriverErrorType } from "@fluidframework/driver-definitions";
18
18
  import {
19
- IAckSummaryResult,
20
- INackSummaryResult,
21
- ISummarizeOptions,
22
- IBroadcastSummaryResult,
23
- ISummarizeResults,
24
- ISummarizeHeuristicData,
25
- ISubmitSummaryOptions,
26
- SubmitSummaryResult,
27
- SummarizeResultPart,
28
- ISummaryCancellationToken,
29
- ISummarizeTelemetryProperties,
30
- SummaryGeneratorTelemetry,
19
+ IAckSummaryResult,
20
+ INackSummaryResult,
21
+ ISummarizeOptions,
22
+ IBroadcastSummaryResult,
23
+ ISummarizeResults,
24
+ ISummarizeHeuristicData,
25
+ ISubmitSummaryOptions,
26
+ SubmitSummaryResult,
27
+ SummarizeResultPart,
28
+ ISummaryCancellationToken,
29
+ ISummarizeTelemetryProperties,
30
+ SummaryGeneratorTelemetry,
31
31
  } from "./summarizerTypes";
32
32
  import { IClientSummaryWatcher } from "./summaryCollection";
33
33
 
34
34
  export type raceTimerResult<T> =
35
- { result: "done"; value: T; } |
36
- { result: IPromiseTimerResult["timerResult"]; } |
37
- { result: "cancelled"; };
35
+ | { result: "done"; value: T }
36
+ | { result: IPromiseTimerResult["timerResult"] }
37
+ | { result: "cancelled" };
38
38
 
39
39
  /** Helper function to wait for a promise or PromiseTimer to elapse. */
40
40
  export async function raceTimer<T>(
41
- promise: Promise<T>,
42
- timer: Promise<IPromiseTimerResult>,
43
- cancellationToken?: ISummaryCancellationToken,
41
+ promise: Promise<T>,
42
+ timer: Promise<IPromiseTimerResult>,
43
+ cancellationToken?: ISummaryCancellationToken,
44
44
  ): Promise<raceTimerResult<T>> {
45
- const promises: Promise<raceTimerResult<T>>[] = [
46
- promise.then((value) => ({ result: "done", value } as const)),
47
- timer.then(({ timerResult: result }) => ({ result } as const)),
48
- ];
49
- if (cancellationToken !== undefined) {
50
- promises.push(cancellationToken.waitCancelled.then(() => ({ result: "cancelled" } as const)));
51
- }
52
- return Promise.race(promises);
45
+ const promises: Promise<raceTimerResult<T>>[] = [
46
+ promise.then((value) => ({ result: "done", value } as const)),
47
+ timer.then(({ timerResult: result }) => ({ result } as const)),
48
+ ];
49
+ if (cancellationToken !== undefined) {
50
+ promises.push(
51
+ cancellationToken.waitCancelled.then(() => ({ result: "cancelled" } as const)),
52
+ );
53
+ }
54
+ return Promise.race(promises);
53
55
  }
54
56
 
55
57
  // Send some telemetry if generate summary takes too long
@@ -57,390 +59,447 @@ const maxSummarizeTimeoutTime = 20000; // 20 sec
57
59
  const maxSummarizeTimeoutCount = 5; // Double and resend 5 times
58
60
 
59
61
  export type SummarizeReason =
60
- /**
61
- * Attempt to summarize after idle timeout has elapsed.
62
- * Idle timer restarts whenever an op is received. So this
63
- * triggers only after some amount of time has passed with
64
- * no ops being received.
65
- */
66
- | "idle"
67
- /**
68
- * Attempt to summarize after a maximum time since last
69
- * successful summary has passed. This measures time since
70
- * last summary ack op was processed.
71
- */
72
- | "maxTime"
73
- /**
74
- * Attempt to summarize after a maximum number of ops have
75
- * passed since the last successful summary. This compares
76
- * op sequence numbers with the reference sequence number
77
- * of the summarize op corresponding to the last summary
78
- * ack op.
79
- */
80
- | "maxOps"
81
- /**
82
- * Special case to attempt to summarize one last time before the
83
- * summarizer client closes itself. This is to prevent cases where
84
- * the summarizer client never gets a chance to summarize, because
85
- * there are too many outstanding ops and/or parent client cannot
86
- * stay connected long enough for summarizer client to catch up.
87
- */
88
- | "lastSummary"
89
- /** On-demand summary requested with specified reason. */
90
- | `onDemand;${string}`
91
- /** Enqueue summarize attempt with specified reason. */
92
- | `enqueue;${string}`;
62
+ /**
63
+ * Attempt to summarize after idle timeout has elapsed.
64
+ * Idle timer restarts whenever an op is received. So this
65
+ * triggers only after some amount of time has passed with
66
+ * no ops being received.
67
+ */
68
+ | "idle"
69
+ /**
70
+ * Attempt to summarize after a maximum time since last
71
+ * successful summary has passed. This measures time since
72
+ * last summary ack op was processed.
73
+ */
74
+ | "maxTime"
75
+ /**
76
+ * Attempt to summarize after a maximum number of ops have
77
+ * passed since the last successful summary. This compares
78
+ * op sequence numbers with the reference sequence number
79
+ * of the summarize op corresponding to the last summary
80
+ * ack op.
81
+ */
82
+ | "maxOps"
83
+ /**
84
+ * Special case to attempt to summarize one last time before the
85
+ * summarizer client closes itself. This is to prevent cases where
86
+ * the summarizer client never gets a chance to summarize, because
87
+ * there are too many outstanding ops and/or parent client cannot
88
+ * stay connected long enough for summarizer client to catch up.
89
+ */
90
+ | "lastSummary"
91
+ /** On-demand summary requested with specified reason. */
92
+ | `onDemand;${string}`
93
+ /** Enqueue summarize attempt with specified reason. */
94
+ | `enqueue;${string}`;
93
95
 
94
96
  const summarizeErrors = {
95
- /**
96
- * Error encountered while generating the summary tree, uploading
97
- * it to storage, or submitting the op. It could be a result of
98
- * the client becoming disconnected while generating or an actual error.
99
- */
100
- submitSummaryFailure: "Error while generating, uploading, or submitting summary",
101
- /**
102
- * The summaryAckWaitTimeout time has elapsed before receiving the summarize op
103
- * sent by this summarize attempt. It is expected to be broadcast quickly.
104
- */
105
- summaryOpWaitTimeout: "Timeout while waiting for summarize op broadcast",
106
- /**
107
- * The summaryAckWaitTimeout time has elapsed before receiving either a
108
- * summaryAck or summaryNack op from the server in response to this
109
- * summarize attempt. It is expected that the server should respond.
110
- */
111
- summaryAckWaitTimeout: "Timeout while waiting for summaryAck/summaryNack op",
112
- /**
113
- * The server responded with a summaryNack op, thus rejecting this
114
- * summarize attempt.
115
- */
116
- summaryNack: "Server rejected summary via summaryNack op",
117
-
118
- disconnect: "Summary cancelled due to summarizer or main client disconnect",
97
+ /**
98
+ * Error encountered while generating the summary tree, uploading
99
+ * it to storage, or submitting the op. It could be a result of
100
+ * the client becoming disconnected while generating or an actual error.
101
+ */
102
+ submitSummaryFailure: "Error while generating, uploading, or submitting summary",
103
+ /**
104
+ * The summaryAckWaitTimeout time has elapsed before receiving the summarize op
105
+ * sent by this summarize attempt. It is expected to be broadcast quickly.
106
+ */
107
+ summaryOpWaitTimeout: "Timeout while waiting for summarize op broadcast",
108
+ /**
109
+ * The summaryAckWaitTimeout time has elapsed before receiving either a
110
+ * summaryAck or summaryNack op from the server in response to this
111
+ * summarize attempt. It is expected that the server should respond.
112
+ */
113
+ summaryAckWaitTimeout: "Timeout while waiting for summaryAck/summaryNack op",
114
+ /**
115
+ * The server responded with a summaryNack op, thus rejecting this
116
+ * summarize attempt.
117
+ */
118
+ summaryNack: "Server rejected summary via summaryNack op",
119
+
120
+ disconnect: "Summary cancelled due to summarizer or main client disconnect",
119
121
  } as const;
120
122
 
121
123
  export class SummarizeResultBuilder {
122
- public readonly summarySubmitted = new Deferred<SummarizeResultPart<SubmitSummaryResult>>();
123
- public readonly summaryOpBroadcasted = new Deferred<SummarizeResultPart<IBroadcastSummaryResult>>();
124
- public readonly receivedSummaryAckOrNack =
125
- new Deferred<SummarizeResultPart<IAckSummaryResult, INackSummaryResult>>();
126
-
127
- public fail(message: string, error: any, nackSummaryResult?: INackSummaryResult, retryAfterSeconds?: number) {
128
- assert(!this.receivedSummaryAckOrNack.isCompleted,
129
- 0x25e /* "no reason to call fail if all promises have been completed" */);
130
-
131
- const result: SummarizeResultPart<undefined> =
132
- { success: false, message, data: undefined, error, retryAfterSeconds } as const;
133
- this.summarySubmitted.resolve(result);
134
- this.summaryOpBroadcasted.resolve(result);
135
- this.receivedSummaryAckOrNack.resolve({ ...result, data: nackSummaryResult });
136
- }
137
- public build(): ISummarizeResults {
138
- return {
139
- summarySubmitted: this.summarySubmitted.promise,
140
- summaryOpBroadcasted: this.summaryOpBroadcasted.promise,
141
- receivedSummaryAckOrNack: this.receivedSummaryAckOrNack.promise,
142
- } as const;
143
- }
124
+ public readonly summarySubmitted = new Deferred<SummarizeResultPart<SubmitSummaryResult>>();
125
+ public readonly summaryOpBroadcasted = new Deferred<
126
+ SummarizeResultPart<IBroadcastSummaryResult>
127
+ >();
128
+ public readonly receivedSummaryAckOrNack = new Deferred<
129
+ SummarizeResultPart<IAckSummaryResult, INackSummaryResult>
130
+ >();
131
+
132
+ public fail(
133
+ message: string,
134
+ error: any,
135
+ nackSummaryResult?: INackSummaryResult,
136
+ retryAfterSeconds?: number,
137
+ ) {
138
+ assert(
139
+ !this.receivedSummaryAckOrNack.isCompleted,
140
+ 0x25e /* "no reason to call fail if all promises have been completed" */,
141
+ );
142
+
143
+ const result: SummarizeResultPart<undefined> = {
144
+ success: false,
145
+ message,
146
+ data: undefined,
147
+ error,
148
+ retryAfterSeconds,
149
+ } as const;
150
+ this.summarySubmitted.resolve(result);
151
+ this.summaryOpBroadcasted.resolve(result);
152
+ this.receivedSummaryAckOrNack.resolve({ ...result, data: nackSummaryResult });
153
+ }
154
+ public build(): ISummarizeResults {
155
+ return {
156
+ summarySubmitted: this.summarySubmitted.promise,
157
+ summaryOpBroadcasted: this.summaryOpBroadcasted.promise,
158
+ receivedSummaryAckOrNack: this.receivedSummaryAckOrNack.promise,
159
+ } as const;
160
+ }
144
161
  }
145
162
 
146
163
  /**
147
164
  * This class generates and tracks a summary attempt.
148
165
  */
149
166
  export class SummaryGenerator {
150
- private readonly summarizeTimer: Timer;
151
- constructor(
152
- private readonly pendingAckTimer: IPromiseTimer,
153
- private readonly heuristicData: ISummarizeHeuristicData,
154
- private readonly submitSummaryCallback: (options: ISubmitSummaryOptions) => Promise<SubmitSummaryResult>,
155
- private readonly raiseSummarizingError: (errorMessage: string) => void,
156
- private readonly successfulSummaryCallback: () => void,
157
- private readonly summaryWatcher: Pick<IClientSummaryWatcher, "watchSummary">,
158
- private readonly logger: ITelemetryLogger,
159
- ) {
160
- this.summarizeTimer = new Timer(
161
- maxSummarizeTimeoutTime,
162
- () => this.summarizeTimerHandler(maxSummarizeTimeoutTime, 1),
163
- );
164
- }
165
-
166
- /**
167
- * Generates summary and listens for broadcast and ack/nack.
168
- * Returns true for ack, false for nack, and undefined for failure or timeout.
169
- * @param reason - reason for summarizing
170
- * @param options - refreshLatestAck to fetch summary ack info from server,
171
- * fullTree to generate tree without any summary handles even if unchanged
172
- */
173
- public summarize(
174
- summarizeProps: ISummarizeTelemetryProperties,
175
- options: ISummarizeOptions,
176
- cancellationToken: ISummaryCancellationToken,
177
- resultsBuilder = new SummarizeResultBuilder(),
178
- ): ISummarizeResults {
179
- this.summarizeCore(summarizeProps, options, resultsBuilder, cancellationToken)
180
- .catch((error) => {
181
- const message = "UnexpectedSummarizeError";
182
- this.logger.sendErrorEvent({ eventName: message, ...summarizeProps }, error);
183
- resultsBuilder.fail(message, error);
184
- });
185
-
186
- return resultsBuilder.build();
187
- }
188
-
189
- private async summarizeCore(
190
- summarizeProps: ISummarizeTelemetryProperties,
191
- options: ISummarizeOptions,
192
- resultsBuilder: SummarizeResultBuilder,
193
- cancellationToken: ISummaryCancellationToken,
194
- ): Promise<void> {
195
- const { refreshLatestAck, fullTree } = options;
196
- const logger = ChildLogger.create(this.logger, undefined, { all: summarizeProps });
197
-
198
- // Note: timeSinceLastAttempt and timeSinceLastSummary for the
199
- // first summary are basically the time since the summarizer was loaded.
200
- const timeSinceLastAttempt = Date.now() - this.heuristicData.lastAttempt.summaryTime;
201
- const timeSinceLastSummary = Date.now() - this.heuristicData.lastSuccessfulSummary.summaryTime;
202
- let summarizeTelemetryProps: SummaryGeneratorTelemetry = {
203
- fullTree,
204
- timeSinceLastAttempt,
205
- timeSinceLastSummary,
206
- };
207
-
208
- const summarizeEvent = PerformanceEvent.start(logger, {
209
- eventName: "Summarize",
210
- refreshLatestAck,
211
- ...summarizeTelemetryProps,
212
- });
213
-
214
- // Helper functions to report failures and return.
215
- const getFailMessage =
216
- (errorCode: keyof typeof summarizeErrors) => `${errorCode}: ${summarizeErrors[errorCode]}`;
217
- const fail = (
218
- errorCode: keyof typeof summarizeErrors,
219
- error?: any,
220
- properties?: SummaryGeneratorTelemetry,
221
- nackSummaryResult?: INackSummaryResult,
222
- ) => {
223
- this.raiseSummarizingError(summarizeErrors[errorCode]);
224
- // UploadSummary may fail with 429 and retryAfter - respect that
225
- // Summary Nack also can have retryAfter, it's parsed below and comes as a property.
226
- const retryAfterSeconds = getRetryDelaySecondsFromError(error);
227
-
228
- // Report any failure as an error unless it was due to cancellation (like "disconnected" error)
229
- // If failure happened on upload, we may not yet realized that socket disconnected, so check
230
- // offlineError too.
231
- const category = cancellationToken.cancelled || error?.errorType === DriverErrorType.offlineError ?
232
- "generic" : "error";
233
-
234
- const message = getFailMessage(errorCode);
235
- summarizeEvent.cancel({
236
- ...properties,
237
- reason: errorCode,
238
- category,
239
- retryAfterSeconds,
240
- }, error ?? message); // disconnect & summaryAckTimeout do not have proper error.
241
- resultsBuilder.fail(message, error, nackSummaryResult, retryAfterSeconds);
242
- };
243
-
244
- // Wait to generate and send summary
245
- this.summarizeTimer.start();
246
-
247
- // Use record type to prevent unexpected value types
248
- let summaryData: SubmitSummaryResult | undefined;
249
- try {
250
- summaryData = await this.submitSummaryCallback({
251
- fullTree,
252
- refreshLatestAck,
253
- summaryLogger: logger,
254
- cancellationToken,
255
- });
256
-
257
- // Cumulatively add telemetry properties based on how far generateSummary went.
258
- const referenceSequenceNumber = summaryData.referenceSequenceNumber;
259
- const opsSinceLastSummary =
260
- referenceSequenceNumber - this.heuristicData.lastSuccessfulSummary.refSequenceNumber;
261
- summarizeTelemetryProps = {
262
- ...summarizeTelemetryProps,
263
- referenceSequenceNumber,
264
- minimumSequenceNumber: summaryData.minimumSequenceNumber,
265
- opsSinceLastAttempt: referenceSequenceNumber - this.heuristicData.lastAttempt.refSequenceNumber,
266
- opsSinceLastSummary,
267
- };
268
- summarizeTelemetryProps = this.addSummaryDataToTelemetryProps(summaryData, summarizeTelemetryProps);
269
-
270
- if (summaryData.stage !== "submit") {
271
- return fail("submitSummaryFailure", summaryData.error, summarizeTelemetryProps);
272
- }
273
-
274
- /**
275
- * With incremental summaries, if the full tree was not summarized, only data stores that changed should
276
- * be summarized. A data store is considered changed if either or both of the following is true:
277
- * - It has received an op.
278
- * - Its reference state changed, i.e., it went from referenced to unreferenced or vice-versa.
279
- *
280
- * In the extreme case, every op can be for a different data store and each op can result in the reference
281
- * state change of multiple data stores. So, the total number of data stores that are summarized should not
282
- * exceed the number of ops since last summary + number of data store whose reference state changed.
283
- */
284
- if (!fullTree && !summaryData.forcedFullTree) {
285
- const { summarizedDataStoreCount, gcStateUpdatedDataStoreCount = 0 } = summaryData.summaryStats;
286
- if (summarizedDataStoreCount > gcStateUpdatedDataStoreCount + opsSinceLastSummary) {
287
- logger.sendErrorEvent({
288
- eventName: "IncrementalSummaryViolation",
289
- summarizedDataStoreCount,
290
- gcStateUpdatedDataStoreCount,
291
- opsSinceLastSummary,
292
- });
293
- }
294
- }
295
-
296
- // Log event here on summary success only, as Summarize_cancel duplicates failure logging.
297
- summarizeEvent.reportEvent("generate", { ...summarizeTelemetryProps });
298
- resultsBuilder.summarySubmitted.resolve({ success: true, data: summaryData });
299
- } catch (error) {
300
- return fail("submitSummaryFailure", error);
301
- } finally {
302
- this.heuristicData.recordAttempt(summaryData?.referenceSequenceNumber);
303
- this.summarizeTimer.clear();
304
- }
305
-
306
- try {
307
- const pendingTimeoutP = this.pendingAckTimer.start();
308
- const summary = this.summaryWatcher.watchSummary(summaryData.clientSequenceNumber);
309
-
310
- // Wait for broadcast
311
- const waitBroadcastResult = await raceTimer(summary.waitBroadcast(), pendingTimeoutP, cancellationToken);
312
- if (waitBroadcastResult.result === "cancelled") {
313
- return fail("disconnect");
314
- }
315
- if (waitBroadcastResult.result !== "done") {
316
- return fail("summaryOpWaitTimeout");
317
- }
318
- const summarizeOp = waitBroadcastResult.value;
319
-
320
- const broadcastDuration = Date.now() - this.heuristicData.lastAttempt.summaryTime;
321
- resultsBuilder.summaryOpBroadcasted.resolve({
322
- success: true,
323
- data: { summarizeOp, broadcastDuration },
324
- });
325
-
326
- this.heuristicData.lastAttempt.summarySequenceNumber = summarizeOp.sequenceNumber;
327
- logger.sendTelemetryEvent({
328
- eventName: "Summarize_Op",
329
- duration: broadcastDuration,
330
- referenceSequenceNumber: summarizeOp.referenceSequenceNumber,
331
- summarySequenceNumber: summarizeOp.sequenceNumber,
332
- handle: summarizeOp.contents.handle,
333
- });
334
-
335
- // Wait for ack/nack
336
- const waitAckNackResult = await raceTimer(summary.waitAckNack(), pendingTimeoutP, cancellationToken);
337
- if (waitAckNackResult.result === "cancelled") {
338
- return fail("disconnect");
339
- }
340
- if (waitAckNackResult.result !== "done") {
341
- return fail("summaryAckWaitTimeout");
342
- }
343
- const ackNackOp = waitAckNackResult.value;
344
- this.pendingAckTimer.clear();
345
-
346
- // Update for success/failure
347
- const ackNackDuration = Date.now() - this.heuristicData.lastAttempt.summaryTime;
348
-
349
- // adding new properties
350
- summarizeTelemetryProps = {
351
- ackWaitDuration: ackNackDuration,
352
- ackNackSequenceNumber: ackNackOp.sequenceNumber,
353
- summarySequenceNumber: ackNackOp.contents.summaryProposal.summarySequenceNumber,
354
- ...summarizeTelemetryProps,
355
- };
356
- if (ackNackOp.type === MessageType.SummaryAck) {
357
- this.heuristicData.markLastAttemptAsSuccessful();
358
- this.successfulSummaryCallback();
359
- summarizeEvent.end({
360
- ...summarizeTelemetryProps,
361
- handle: ackNackOp.contents.handle,
362
- });
363
- resultsBuilder.receivedSummaryAckOrNack.resolve({ success: true, data: {
364
- summaryAckOp: ackNackOp,
365
- ackNackDuration,
366
- } });
367
- } else {
368
- // Check for retryDelay in summaryNack response.
369
- assert(ackNackOp.type === MessageType.SummaryNack, 0x274 /* "type check" */);
370
- const summaryNack = ackNackOp.contents;
371
- const message = summaryNack?.message;
372
- const retryAfterSeconds = summaryNack?.retryAfter;
373
-
374
- // pre-0.58 error message prefix: summaryNack
375
- const error = new LoggingError(`Received summaryNack: ${message}`, { retryAfterSeconds });
376
-
377
- assert(getRetryDelaySecondsFromError(error) === retryAfterSeconds, 0x25f /* "retryAfterSeconds" */);
378
- // This will only set resultsBuilder.receivedSummaryAckOrNack, as other promises are already set.
379
- return fail(
380
- "summaryNack",
381
- error,
382
- { ...summarizeTelemetryProps, nackRetryAfter: retryAfterSeconds },
383
- { summaryNackOp: ackNackOp, ackNackDuration },
384
- );
385
- }
386
- } finally {
387
- this.pendingAckTimer.clear();
388
- }
389
- }
390
-
391
- private addSummaryDataToTelemetryProps(
392
- summaryData: SubmitSummaryResult,
393
- initialProps: SummaryGeneratorTelemetry,
394
- ): SummaryGeneratorTelemetry {
395
- switch (summaryData.stage) {
396
- case "base": return initialProps;
397
-
398
- case "generate": return {
399
- ...initialProps,
400
- ...summaryData.summaryStats,
401
- generateDuration: summaryData.generateDuration,
402
- };
403
-
404
- case "upload": return {
405
- ...initialProps,
406
- ...summaryData.summaryStats,
407
- generateDuration: summaryData.generateDuration,
408
- handle: summaryData.handle,
409
- uploadDuration: summaryData.uploadDuration,
410
- };
411
-
412
- case "submit": return {
413
- ...initialProps,
414
- ...summaryData.summaryStats,
415
- generateDuration: summaryData.generateDuration,
416
- handle: summaryData.handle,
417
- uploadDuration: summaryData.uploadDuration,
418
- clientSequenceNumber: summaryData.clientSequenceNumber,
419
- hasMissingOpData: this.heuristicData.hasMissingOpData,
420
- opsSizesSinceLastSummary: this.heuristicData.totalOpsSize,
421
- nonRuntimeOpsSinceLastSummary: this.heuristicData.numNonRuntimeOps,
422
- };
423
-
424
- default: assert(true, 0x397 /* Unexpected summary stage */);
425
- }
426
-
427
- return initialProps;
428
- }
429
-
430
- private summarizeTimerHandler(time: number, count: number) {
431
- this.logger.sendPerformanceEvent({
432
- eventName: "SummarizeTimeout",
433
- timeoutTime: time,
434
- timeoutCount: count,
435
- });
436
- if (count < maxSummarizeTimeoutCount) {
437
- // Double and start a new timer
438
- const nextTime = time * 2;
439
- this.summarizeTimer.start(nextTime, () => this.summarizeTimerHandler(nextTime, count + 1));
440
- }
441
- }
442
-
443
- public dispose() {
444
- this.summarizeTimer.clear();
445
- }
167
+ private readonly summarizeTimer: Timer;
168
+ constructor(
169
+ private readonly pendingAckTimer: IPromiseTimer,
170
+ private readonly heuristicData: ISummarizeHeuristicData,
171
+ private readonly submitSummaryCallback: (
172
+ options: ISubmitSummaryOptions,
173
+ ) => Promise<SubmitSummaryResult>,
174
+ private readonly raiseSummarizingError: (errorMessage: string) => void,
175
+ private readonly successfulSummaryCallback: () => void,
176
+ private readonly summaryWatcher: Pick<IClientSummaryWatcher, "watchSummary">,
177
+ private readonly logger: ITelemetryLogger,
178
+ ) {
179
+ this.summarizeTimer = new Timer(maxSummarizeTimeoutTime, () =>
180
+ this.summarizeTimerHandler(maxSummarizeTimeoutTime, 1),
181
+ );
182
+ }
183
+
184
+ /**
185
+ * Generates summary and listens for broadcast and ack/nack.
186
+ * Returns true for ack, false for nack, and undefined for failure or timeout.
187
+ * @param reason - reason for summarizing
188
+ * @param options - refreshLatestAck to fetch summary ack info from server,
189
+ * fullTree to generate tree without any summary handles even if unchanged
190
+ */
191
+ public summarize(
192
+ summarizeProps: ISummarizeTelemetryProperties,
193
+ options: ISummarizeOptions,
194
+ cancellationToken: ISummaryCancellationToken,
195
+ resultsBuilder = new SummarizeResultBuilder(),
196
+ ): ISummarizeResults {
197
+ this.logger.sendTelemetryEvent({
198
+ eventName: "Summarize_start",
199
+ ...summarizeProps,
200
+ });
201
+ this.summarizeCore(summarizeProps, options, resultsBuilder, cancellationToken).catch(
202
+ (error) => {
203
+ const message = "UnexpectedSummarizeError";
204
+ this.logger.sendErrorEvent({ eventName: message, ...summarizeProps }, error);
205
+ resultsBuilder.fail(message, error);
206
+ },
207
+ );
208
+
209
+ return resultsBuilder.build();
210
+ }
211
+
212
+ private async summarizeCore(
213
+ summarizeProps: ISummarizeTelemetryProperties,
214
+ options: ISummarizeOptions,
215
+ resultsBuilder: SummarizeResultBuilder,
216
+ cancellationToken: ISummaryCancellationToken,
217
+ ): Promise<void> {
218
+ const { refreshLatestAck, fullTree } = options;
219
+ const logger = ChildLogger.create(this.logger, undefined, { all: summarizeProps });
220
+
221
+ // Note: timeSinceLastAttempt and timeSinceLastSummary for the
222
+ // first summary are basically the time since the summarizer was loaded.
223
+ const timeSinceLastAttempt = Date.now() - this.heuristicData.lastAttempt.summaryTime;
224
+ const timeSinceLastSummary =
225
+ Date.now() - this.heuristicData.lastSuccessfulSummary.summaryTime;
226
+ let summarizeTelemetryProps: SummaryGeneratorTelemetry = {
227
+ fullTree,
228
+ timeSinceLastAttempt,
229
+ timeSinceLastSummary,
230
+ };
231
+
232
+ const summarizeEvent = PerformanceEvent.start(logger, {
233
+ eventName: "Summarize",
234
+ refreshLatestAck,
235
+ ...summarizeTelemetryProps,
236
+ });
237
+
238
+ // Helper functions to report failures and return.
239
+ const getFailMessage = (errorCode: keyof typeof summarizeErrors) =>
240
+ `${errorCode}: ${summarizeErrors[errorCode]}`;
241
+ const fail = (
242
+ errorCode: keyof typeof summarizeErrors,
243
+ error?: any,
244
+ properties?: SummaryGeneratorTelemetry,
245
+ nackSummaryResult?: INackSummaryResult,
246
+ ) => {
247
+ this.raiseSummarizingError(summarizeErrors[errorCode]);
248
+ // UploadSummary may fail with 429 and retryAfter - respect that
249
+ // Summary Nack also can have retryAfter, it's parsed below and comes as a property.
250
+ const retryAfterSeconds = getRetryDelaySecondsFromError(error);
251
+
252
+ // Report any failure as an error unless it was due to cancellation (like "disconnected" error)
253
+ // If failure happened on upload, we may not yet realized that socket disconnected, so check
254
+ // offlineError too.
255
+ const category =
256
+ cancellationToken.cancelled || error?.errorType === DriverErrorType.offlineError
257
+ ? "generic"
258
+ : "error";
259
+
260
+ const message = getFailMessage(errorCode);
261
+ summarizeEvent.cancel(
262
+ {
263
+ ...properties,
264
+ reason: errorCode,
265
+ category,
266
+ retryAfterSeconds,
267
+ },
268
+ error ?? message,
269
+ ); // disconnect & summaryAckTimeout do not have proper error.
270
+ resultsBuilder.fail(message, error, nackSummaryResult, retryAfterSeconds);
271
+ };
272
+
273
+ // Wait to generate and send summary
274
+ this.summarizeTimer.start();
275
+
276
+ // Use record type to prevent unexpected value types
277
+ let summaryData: SubmitSummaryResult | undefined;
278
+ try {
279
+ summaryData = await this.submitSummaryCallback({
280
+ fullTree,
281
+ refreshLatestAck,
282
+ summaryLogger: logger,
283
+ cancellationToken,
284
+ });
285
+
286
+ // Cumulatively add telemetry properties based on how far generateSummary went.
287
+ const referenceSequenceNumber = summaryData.referenceSequenceNumber;
288
+ const opsSinceLastSummary =
289
+ referenceSequenceNumber -
290
+ this.heuristicData.lastSuccessfulSummary.refSequenceNumber;
291
+ summarizeTelemetryProps = {
292
+ ...summarizeTelemetryProps,
293
+ referenceSequenceNumber,
294
+ minimumSequenceNumber: summaryData.minimumSequenceNumber,
295
+ opsSinceLastAttempt:
296
+ referenceSequenceNumber - this.heuristicData.lastAttempt.refSequenceNumber,
297
+ opsSinceLastSummary,
298
+ };
299
+ summarizeTelemetryProps = this.addSummaryDataToTelemetryProps(
300
+ summaryData,
301
+ summarizeTelemetryProps,
302
+ );
303
+
304
+ if (summaryData.stage !== "submit") {
305
+ return fail("submitSummaryFailure", summaryData.error, summarizeTelemetryProps);
306
+ }
307
+
308
+ /**
309
+ * With incremental summaries, if the full tree was not summarized, only data stores that changed should
310
+ * be summarized. A data store is considered changed if either or both of the following is true:
311
+ * - It has received an op.
312
+ * - Its reference state changed, i.e., it went from referenced to unreferenced or vice-versa.
313
+ *
314
+ * In the extreme case, every op can be for a different data store and each op can result in the reference
315
+ * state change of multiple data stores. So, the total number of data stores that are summarized should not
316
+ * exceed the number of ops since last summary + number of data store whose reference state changed.
317
+ */
318
+ if (!fullTree && !summaryData.forcedFullTree) {
319
+ const { summarizedDataStoreCount, gcStateUpdatedDataStoreCount = 0 } =
320
+ summaryData.summaryStats;
321
+ if (summarizedDataStoreCount > gcStateUpdatedDataStoreCount + opsSinceLastSummary) {
322
+ logger.sendErrorEvent({
323
+ eventName: "IncrementalSummaryViolation",
324
+ summarizedDataStoreCount,
325
+ gcStateUpdatedDataStoreCount,
326
+ opsSinceLastSummary,
327
+ });
328
+ }
329
+ }
330
+
331
+ // Log event here on summary success only, as Summarize_cancel duplicates failure logging.
332
+ summarizeEvent.reportEvent("generate", { ...summarizeTelemetryProps });
333
+ resultsBuilder.summarySubmitted.resolve({ success: true, data: summaryData });
334
+ } catch (error) {
335
+ return fail("submitSummaryFailure", error);
336
+ } finally {
337
+ this.heuristicData.recordAttempt(summaryData?.referenceSequenceNumber);
338
+ this.summarizeTimer.clear();
339
+ }
340
+
341
+ try {
342
+ const pendingTimeoutP = this.pendingAckTimer.start();
343
+ const summary = this.summaryWatcher.watchSummary(summaryData.clientSequenceNumber);
344
+
345
+ // Wait for broadcast
346
+ const waitBroadcastResult = await raceTimer(
347
+ summary.waitBroadcast(),
348
+ pendingTimeoutP,
349
+ cancellationToken,
350
+ );
351
+ if (waitBroadcastResult.result === "cancelled") {
352
+ return fail("disconnect");
353
+ }
354
+ if (waitBroadcastResult.result !== "done") {
355
+ return fail("summaryOpWaitTimeout");
356
+ }
357
+ const summarizeOp = waitBroadcastResult.value;
358
+
359
+ const broadcastDuration = Date.now() - this.heuristicData.lastAttempt.summaryTime;
360
+ resultsBuilder.summaryOpBroadcasted.resolve({
361
+ success: true,
362
+ data: { summarizeOp, broadcastDuration },
363
+ });
364
+
365
+ this.heuristicData.lastAttempt.summarySequenceNumber = summarizeOp.sequenceNumber;
366
+ logger.sendTelemetryEvent({
367
+ eventName: "Summarize_Op",
368
+ duration: broadcastDuration,
369
+ referenceSequenceNumber: summarizeOp.referenceSequenceNumber,
370
+ summarySequenceNumber: summarizeOp.sequenceNumber,
371
+ handle: summarizeOp.contents.handle,
372
+ });
373
+
374
+ // Wait for ack/nack
375
+ const waitAckNackResult = await raceTimer(
376
+ summary.waitAckNack(),
377
+ pendingTimeoutP,
378
+ cancellationToken,
379
+ );
380
+ if (waitAckNackResult.result === "cancelled") {
381
+ return fail("disconnect");
382
+ }
383
+ if (waitAckNackResult.result !== "done") {
384
+ return fail("summaryAckWaitTimeout");
385
+ }
386
+ const ackNackOp = waitAckNackResult.value;
387
+ this.pendingAckTimer.clear();
388
+
389
+ // Update for success/failure
390
+ const ackNackDuration = Date.now() - this.heuristicData.lastAttempt.summaryTime;
391
+
392
+ // adding new properties
393
+ summarizeTelemetryProps = {
394
+ ackWaitDuration: ackNackDuration,
395
+ ackNackSequenceNumber: ackNackOp.sequenceNumber,
396
+ summarySequenceNumber: ackNackOp.contents.summaryProposal.summarySequenceNumber,
397
+ ...summarizeTelemetryProps,
398
+ };
399
+ if (ackNackOp.type === MessageType.SummaryAck) {
400
+ this.heuristicData.markLastAttemptAsSuccessful();
401
+ this.successfulSummaryCallback();
402
+ summarizeEvent.end({
403
+ ...summarizeTelemetryProps,
404
+ handle: ackNackOp.contents.handle,
405
+ });
406
+ resultsBuilder.receivedSummaryAckOrNack.resolve({
407
+ success: true,
408
+ data: {
409
+ summaryAckOp: ackNackOp,
410
+ ackNackDuration,
411
+ },
412
+ });
413
+ } else {
414
+ // Check for retryDelay in summaryNack response.
415
+ assert(ackNackOp.type === MessageType.SummaryNack, 0x274 /* "type check" */);
416
+ const summaryNack = ackNackOp.contents;
417
+ const errorMessage = summaryNack?.message;
418
+ const retryAfterSeconds = summaryNack?.retryAfter;
419
+
420
+ // pre-0.58 error message prefix: summaryNack
421
+ const error = new LoggingError(`Received summaryNack`, {
422
+ retryAfterSeconds,
423
+ errorMessage,
424
+ });
425
+
426
+ assert(
427
+ getRetryDelaySecondsFromError(error) === retryAfterSeconds,
428
+ 0x25f /* "retryAfterSeconds" */,
429
+ );
430
+ // This will only set resultsBuilder.receivedSummaryAckOrNack, as other promises are already set.
431
+ return fail(
432
+ "summaryNack",
433
+ error,
434
+ { ...summarizeTelemetryProps, nackRetryAfter: retryAfterSeconds },
435
+ { summaryNackOp: ackNackOp, ackNackDuration },
436
+ );
437
+ }
438
+ } finally {
439
+ this.pendingAckTimer.clear();
440
+ }
441
+ }
442
+
443
+ private addSummaryDataToTelemetryProps(
444
+ summaryData: SubmitSummaryResult,
445
+ initialProps: SummaryGeneratorTelemetry,
446
+ ): SummaryGeneratorTelemetry {
447
+ switch (summaryData.stage) {
448
+ case "base":
449
+ return initialProps;
450
+
451
+ case "generate":
452
+ return {
453
+ ...initialProps,
454
+ ...summaryData.summaryStats,
455
+ generateDuration: summaryData.generateDuration,
456
+ };
457
+
458
+ case "upload":
459
+ return {
460
+ ...initialProps,
461
+ ...summaryData.summaryStats,
462
+ generateDuration: summaryData.generateDuration,
463
+ handle: summaryData.handle,
464
+ uploadDuration: summaryData.uploadDuration,
465
+ };
466
+
467
+ case "submit":
468
+ return {
469
+ ...initialProps,
470
+ ...summaryData.summaryStats,
471
+ generateDuration: summaryData.generateDuration,
472
+ handle: summaryData.handle,
473
+ uploadDuration: summaryData.uploadDuration,
474
+ clientSequenceNumber: summaryData.clientSequenceNumber,
475
+ hasMissingOpData: this.heuristicData.hasMissingOpData,
476
+ opsSizesSinceLastSummary: this.heuristicData.totalOpsSize,
477
+ nonRuntimeOpsSinceLastSummary: this.heuristicData.numNonRuntimeOps,
478
+ };
479
+
480
+ default:
481
+ assert(true, 0x397 /* Unexpected summary stage */);
482
+ }
483
+
484
+ return initialProps;
485
+ }
486
+
487
+ private summarizeTimerHandler(time: number, count: number) {
488
+ this.logger.sendPerformanceEvent({
489
+ eventName: "SummarizeTimeout",
490
+ timeoutTime: time,
491
+ timeoutCount: count,
492
+ });
493
+ if (count < maxSummarizeTimeoutCount) {
494
+ // Double and start a new timer
495
+ const nextTime = time * 2;
496
+ this.summarizeTimer.start(nextTime, () =>
497
+ this.summarizeTimerHandler(nextTime, count + 1),
498
+ );
499
+ }
500
+ }
501
+
502
+ public dispose() {
503
+ this.summarizeTimer.clear();
504
+ }
446
505
  }