@fluidframework/runtime-utils 2.0.0-internal.3.0.2 → 2.0.0-internal.3.2.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.
- package/.eslintrc.js +5 -7
- package/.mocharc.js +2 -2
- package/api-extractor.json +2 -2
- package/dist/dataStoreHandleContextUtils.d.ts.map +1 -1
- package/dist/dataStoreHandleContextUtils.js +3 -1
- package/dist/dataStoreHandleContextUtils.js.map +1 -1
- package/dist/dataStoreHelpers.d.ts.map +1 -1
- package/dist/dataStoreHelpers.js +23 -7
- package/dist/dataStoreHelpers.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/objectstoragepartition.d.ts.map +1 -1
- package/dist/objectstoragepartition.js.map +1 -1
- package/dist/objectstorageutils.d.ts.map +1 -1
- package/dist/objectstorageutils.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/requestParser.d.ts.map +1 -1
- package/dist/requestParser.js.map +1 -1
- package/dist/runtimeFactoryHelper.d.ts.map +1 -1
- package/dist/runtimeFactoryHelper.js +6 -2
- package/dist/runtimeFactoryHelper.js.map +1 -1
- package/dist/summarizerNode/summarizerNode.d.ts +25 -10
- package/dist/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summarizerNode/summarizerNode.js +117 -52
- package/dist/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summarizerNode/summarizerNodeUtils.d.ts +12 -14
- package/dist/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summarizerNode/summarizerNodeWithGc.d.ts +8 -114
- package/dist/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summarizerNode/summarizerNodeWithGc.js +45 -19
- package/dist/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summaryUtils.d.ts +2 -2
- package/dist/summaryUtils.d.ts.map +1 -1
- package/dist/summaryUtils.js +5 -4
- package/dist/summaryUtils.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js.map +1 -1
- package/lib/dataStoreHandleContextUtils.d.ts.map +1 -1
- package/lib/dataStoreHandleContextUtils.js +3 -1
- package/lib/dataStoreHandleContextUtils.js.map +1 -1
- package/lib/dataStoreHelpers.d.ts.map +1 -1
- package/lib/dataStoreHelpers.js +23 -7
- package/lib/dataStoreHelpers.js.map +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/objectstoragepartition.d.ts.map +1 -1
- package/lib/objectstoragepartition.js.map +1 -1
- package/lib/objectstorageutils.d.ts.map +1 -1
- package/lib/objectstorageutils.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/requestParser.d.ts.map +1 -1
- package/lib/requestParser.js.map +1 -1
- package/lib/runtimeFactoryHelper.d.ts.map +1 -1
- package/lib/runtimeFactoryHelper.js +6 -2
- package/lib/runtimeFactoryHelper.js.map +1 -1
- package/lib/summarizerNode/summarizerNode.d.ts +25 -10
- package/lib/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summarizerNode/summarizerNode.js +117 -52
- package/lib/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summarizerNode/summarizerNodeUtils.d.ts +12 -14
- package/lib/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summarizerNode/summarizerNodeWithGc.d.ts +8 -114
- package/lib/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summarizerNode/summarizerNodeWithGc.js +46 -19
- package/lib/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summaryUtils.d.ts +2 -2
- package/lib/summaryUtils.d.ts.map +1 -1
- package/lib/summaryUtils.js +5 -4
- package/lib/summaryUtils.js.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js.map +1 -1
- package/package.json +45 -48
- package/prettier.config.cjs +1 -1
- package/src/dataStoreHandleContextUtils.ts +23 -16
- package/src/dataStoreHelpers.ts +104 -86
- package/src/index.ts +3 -2
- package/src/objectstoragepartition.ts +13 -13
- package/src/objectstorageutils.ts +31 -28
- package/src/packageVersion.ts +1 -1
- package/src/requestParser.ts +80 -82
- package/src/runtimeFactoryHelper.ts +24 -17
- package/src/summarizerNode/summarizerNode.ts +702 -575
- package/src/summarizerNode/summarizerNodeUtils.ts +157 -159
- package/src/summarizerNode/summarizerNodeWithGc.ts +532 -473
- package/src/summaryUtils.ts +313 -305
- package/src/utils.ts +6 -6
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +8 -12
|
@@ -4,37 +4,47 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
ISummarizerNode,
|
|
8
|
+
ISummarizerNodeConfig,
|
|
9
|
+
ISummarizeResult,
|
|
10
|
+
ISummaryTreeWithStats,
|
|
11
|
+
CreateChildSummarizerNodeParam,
|
|
12
|
+
CreateSummarizerNodeSource,
|
|
13
|
+
SummarizeInternalFn,
|
|
14
|
+
ITelemetryContext,
|
|
15
15
|
} from "@fluidframework/runtime-definitions";
|
|
16
16
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
ISequencedDocumentMessage,
|
|
18
|
+
SummaryType,
|
|
19
|
+
ISnapshotTree,
|
|
20
|
+
SummaryObject,
|
|
21
21
|
} from "@fluidframework/protocol-definitions";
|
|
22
|
-
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
22
|
+
import { ITelemetryErrorEvent, ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
23
23
|
import { assert, unreachableCase } from "@fluidframework/common-utils";
|
|
24
|
+
import {
|
|
25
|
+
ChildLogger,
|
|
26
|
+
LoggingError,
|
|
27
|
+
PerformanceEvent,
|
|
28
|
+
TelemetryDataTag,
|
|
29
|
+
} from "@fluidframework/telemetry-utils";
|
|
24
30
|
import { mergeStats, convertToSummaryTree, calculateStats } from "../summaryUtils";
|
|
25
31
|
import { ReadAndParseBlob } from "../utils";
|
|
26
32
|
import {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
EscapedPath,
|
|
34
|
+
ICreateChildDetails,
|
|
35
|
+
IFetchSnapshotResult,
|
|
36
|
+
IInitialSummary,
|
|
37
|
+
ISummarizerNodeRootContract,
|
|
38
|
+
parseSummaryForSubtrees,
|
|
39
|
+
parseSummaryTreeForSubtrees,
|
|
40
|
+
RefreshSummaryResult,
|
|
41
|
+
SummaryNode,
|
|
36
42
|
} from "./summarizerNodeUtils";
|
|
37
43
|
|
|
44
|
+
/**
|
|
45
|
+
* @deprecated Internal implementation detail and will no longer be exported in an
|
|
46
|
+
* upcoming release.
|
|
47
|
+
*/
|
|
38
48
|
export interface IRootSummarizerNode extends ISummarizerNode, ISummarizerNodeRootContract {}
|
|
39
49
|
|
|
40
50
|
/**
|
|
@@ -51,547 +61,655 @@ export interface IRootSummarizerNode extends ISummarizerNode, ISummarizerNodeRoo
|
|
|
51
61
|
* latest successful summary.
|
|
52
62
|
*/
|
|
53
63
|
export class SummarizerNode implements IRootSummarizerNode {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
64
|
+
/**
|
|
65
|
+
* The reference sequence number of the most recent acked summary.
|
|
66
|
+
* Returns 0 if there is not yet an acked summary.
|
|
67
|
+
*/
|
|
68
|
+
public get referenceSequenceNumber() {
|
|
69
|
+
return this._latestSummary?.referenceSequenceNumber ?? 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
protected readonly children = new Map<string, SummarizerNode>();
|
|
73
|
+
protected readonly pendingSummaries = new Map<string, SummaryNode>();
|
|
74
|
+
protected wipReferenceSequenceNumber: number | undefined;
|
|
75
|
+
private wipLocalPaths: { localPath: EscapedPath; additionalPath?: EscapedPath } | undefined;
|
|
76
|
+
private wipSkipRecursion = false;
|
|
77
|
+
|
|
78
|
+
protected readonly logger: ITelemetryLogger;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Do not call constructor directly.
|
|
82
|
+
* Use createRootSummarizerNode to create root node, or createChild to create child nodes.
|
|
83
|
+
*/
|
|
84
|
+
public constructor(
|
|
85
|
+
baseLogger: ITelemetryLogger,
|
|
86
|
+
private readonly summarizeInternalFn: SummarizeInternalFn,
|
|
87
|
+
config: ISummarizerNodeConfig,
|
|
88
|
+
private _changeSequenceNumber: number,
|
|
89
|
+
/** Undefined means created without summary */
|
|
90
|
+
private _latestSummary?: SummaryNode,
|
|
91
|
+
private readonly initialSummary?: IInitialSummary,
|
|
92
|
+
protected wipSummaryLogger?: ITelemetryLogger,
|
|
93
|
+
/** A unique id of this node to be logged when sending telemetry. */
|
|
94
|
+
protected telemetryNodeId?: string,
|
|
95
|
+
) {
|
|
96
|
+
this.canReuseHandle = config.canReuseHandle ?? true;
|
|
97
|
+
// All logs posted by the summarizer node should include the telemetryNodeId.
|
|
98
|
+
this.logger = ChildLogger.create(baseLogger, undefined /* namespace */, {
|
|
99
|
+
all: {
|
|
100
|
+
id: {
|
|
101
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
102
|
+
value: this.telemetryNodeId,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public startSummary(referenceSequenceNumber: number, summaryLogger: ITelemetryLogger) {
|
|
109
|
+
assert(
|
|
110
|
+
this.wipSummaryLogger === undefined,
|
|
111
|
+
0x19f /* "wipSummaryLogger should not be set yet in startSummary" */,
|
|
112
|
+
);
|
|
113
|
+
assert(
|
|
114
|
+
this.wipReferenceSequenceNumber === undefined,
|
|
115
|
+
0x1a0 /* "Already tracking a summary" */,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
this.wipSummaryLogger = summaryLogger;
|
|
119
|
+
|
|
120
|
+
for (const child of this.children.values()) {
|
|
121
|
+
child.startSummary(referenceSequenceNumber, this.wipSummaryLogger);
|
|
122
|
+
}
|
|
123
|
+
this.wipReferenceSequenceNumber = referenceSequenceNumber;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public async summarize(
|
|
127
|
+
fullTree: boolean,
|
|
128
|
+
trackState: boolean = true,
|
|
129
|
+
telemetryContext?: ITelemetryContext,
|
|
130
|
+
): Promise<ISummarizeResult> {
|
|
131
|
+
assert(
|
|
132
|
+
this.isSummaryInProgress(),
|
|
133
|
+
0x1a1 /* "summarize should not be called when not tracking the summary" */,
|
|
134
|
+
);
|
|
135
|
+
assert(
|
|
136
|
+
this.wipSummaryLogger !== undefined,
|
|
137
|
+
0x1a2 /* "wipSummaryLogger should have been set in startSummary or ctor" */,
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// Try to reuse the tree if unchanged
|
|
141
|
+
if (this.canReuseHandle && !fullTree && !this.hasChanged()) {
|
|
142
|
+
const latestSummary = this._latestSummary;
|
|
143
|
+
if (latestSummary !== undefined) {
|
|
144
|
+
this.wipLocalPaths = {
|
|
145
|
+
localPath: latestSummary.localPath,
|
|
146
|
+
additionalPath: latestSummary.additionalPath,
|
|
147
|
+
};
|
|
148
|
+
this.wipSkipRecursion = true;
|
|
149
|
+
const stats = mergeStats();
|
|
150
|
+
stats.handleNodeCount++;
|
|
151
|
+
return {
|
|
152
|
+
summary: {
|
|
153
|
+
type: SummaryType.Handle,
|
|
154
|
+
handle: latestSummary.fullPath.path,
|
|
155
|
+
handleType: SummaryType.Tree,
|
|
156
|
+
},
|
|
157
|
+
stats,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const result = await this.summarizeInternalFn(fullTree, true, telemetryContext);
|
|
163
|
+
this.wipLocalPaths = { localPath: EscapedPath.create(result.id) };
|
|
164
|
+
if (result.pathPartsForChildren !== undefined) {
|
|
165
|
+
this.wipLocalPaths.additionalPath = EscapedPath.createAndConcat(
|
|
166
|
+
result.pathPartsForChildren,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
return { summary: result.summary, stats: result.stats };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Complete the WIP summary for the given proposalHandle
|
|
174
|
+
*/
|
|
175
|
+
public completeSummary(proposalHandle: string) {
|
|
176
|
+
this.completeSummaryCore(proposalHandle, undefined, false);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Recursive implementation for completeSummary, with additional internal-only parameters
|
|
181
|
+
*/
|
|
182
|
+
protected completeSummaryCore(
|
|
183
|
+
proposalHandle: string,
|
|
184
|
+
parentPath: EscapedPath | undefined,
|
|
185
|
+
parentSkipRecursion: boolean,
|
|
186
|
+
) {
|
|
187
|
+
assert(
|
|
188
|
+
this.wipSummaryLogger !== undefined,
|
|
189
|
+
0x1a3 /* "wipSummaryLogger should have been set in startSummary or ctor" */,
|
|
190
|
+
);
|
|
191
|
+
assert(this.wipReferenceSequenceNumber !== undefined, 0x1a4 /* "Not tracking a summary" */);
|
|
192
|
+
let localPathsToUse = this.wipLocalPaths;
|
|
193
|
+
|
|
194
|
+
if (parentSkipRecursion) {
|
|
195
|
+
const latestSummary = this._latestSummary;
|
|
196
|
+
if (latestSummary !== undefined) {
|
|
197
|
+
// This case the parent node created a failure summary or was reused.
|
|
198
|
+
// This node and all children should only try to reference their path
|
|
199
|
+
// by its last known good state in the actual summary tree.
|
|
200
|
+
// If parent fails or is reused, the child summarize is not called so
|
|
201
|
+
// it did not get a chance to change its paths.
|
|
202
|
+
// In this case, essentially only propagate the new summary ref seq num.
|
|
203
|
+
localPathsToUse = {
|
|
204
|
+
localPath: latestSummary.localPath,
|
|
205
|
+
additionalPath: latestSummary.additionalPath,
|
|
206
|
+
};
|
|
207
|
+
} else {
|
|
208
|
+
// This case the child is added after the latest non-failure summary.
|
|
209
|
+
// This node and all children should consider themselves as still not
|
|
210
|
+
// having a successful summary yet.
|
|
211
|
+
// We cannot "reuse" this node if unchanged since that summary, because
|
|
212
|
+
// handles will be unable to point to that node. It never made it to the
|
|
213
|
+
// tree itself, and only exists as an attach op in the _outstandingOps.
|
|
214
|
+
this.clearSummary();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* The absence of wip local path indicates that summarize was not called for this node. This can happen if:
|
|
221
|
+
* 1. A child node was created after summarize was already called on the parent. For example, a data store
|
|
222
|
+
* is realized (loaded) after summarize was called on it creating summarizer nodes for its DDSes. In this case,
|
|
223
|
+
* parentSkipRecursion will be true and the if block above would handle it.
|
|
224
|
+
* 2. A new node was created but summarize was never called on it. This can mean that the summary that is
|
|
225
|
+
* generated may not have the data from this node. We should not continue, log and throw an error. This
|
|
226
|
+
* will help us identify these cases and take appropriate action.
|
|
227
|
+
*/
|
|
228
|
+
if (localPathsToUse === undefined) {
|
|
229
|
+
this.throwUnexpectedError({
|
|
230
|
+
eventName: "NodeNotSummarized",
|
|
231
|
+
proposalHandle,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const summary = new SummaryNode({
|
|
236
|
+
...localPathsToUse,
|
|
237
|
+
referenceSequenceNumber: this.wipReferenceSequenceNumber,
|
|
238
|
+
basePath: parentPath,
|
|
239
|
+
});
|
|
240
|
+
const fullPathForChildren = summary.fullPathForChildren;
|
|
241
|
+
for (const child of this.children.values()) {
|
|
242
|
+
child.completeSummaryCore(
|
|
243
|
+
proposalHandle,
|
|
244
|
+
fullPathForChildren,
|
|
245
|
+
this.wipSkipRecursion || parentSkipRecursion,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
// Note that this overwrites existing pending summary with
|
|
249
|
+
// the same proposalHandle. If proposalHandle is something like
|
|
250
|
+
// a hash or unique identifier, this should be fine. If storage
|
|
251
|
+
// can return the same proposalHandle for a different summary,
|
|
252
|
+
// this should still be okay, because we should be proposing the
|
|
253
|
+
// newer one later which would have to overwrite the previous one.
|
|
254
|
+
this.pendingSummaries.set(proposalHandle, summary);
|
|
255
|
+
this.clearSummary();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
public clearSummary() {
|
|
259
|
+
this.wipReferenceSequenceNumber = undefined;
|
|
260
|
+
this.wipLocalPaths = undefined;
|
|
261
|
+
this.wipSkipRecursion = false;
|
|
262
|
+
this.wipSummaryLogger = undefined;
|
|
263
|
+
for (const child of this.children.values()) {
|
|
264
|
+
child.clearSummary();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Refreshes the latest summary tracked by this node. If we have a pending summary for the given proposal handle,
|
|
270
|
+
* it becomes the latest summary. If the current summary is already ahead (e.g., loaded from a service summary),
|
|
271
|
+
* we skip the update. Otherwise, we fetch the latest snapshot and update latest summary based off of that.
|
|
272
|
+
*
|
|
273
|
+
* @returns A RefreshSummaryResult type which returns information based on the following three scenarios:
|
|
274
|
+
*
|
|
275
|
+
* 1. The latest summary was not updated.
|
|
276
|
+
*
|
|
277
|
+
* 2. The latest summary was updated and the summary corresponding to the params was being tracked.
|
|
278
|
+
*
|
|
279
|
+
* 3. The latest summary was updated but the summary corresponding to the params was not tracked. In this
|
|
280
|
+
* case, the latest summary is updated based on the downloaded snapshot which is also returned.
|
|
281
|
+
*/
|
|
282
|
+
public async refreshLatestSummary(
|
|
283
|
+
proposalHandle: string | undefined,
|
|
284
|
+
summaryRefSeq: number,
|
|
285
|
+
fetchLatestSnapshot: () => Promise<IFetchSnapshotResult>,
|
|
286
|
+
readAndParseBlob: ReadAndParseBlob,
|
|
287
|
+
correlatedSummaryLogger: ITelemetryLogger,
|
|
288
|
+
): Promise<RefreshSummaryResult> {
|
|
289
|
+
const eventProps: {
|
|
290
|
+
proposalHandle: string | undefined;
|
|
291
|
+
summaryRefSeq: number;
|
|
292
|
+
referenceSequenceNumber: number;
|
|
293
|
+
latestSummaryUpdated?: boolean;
|
|
294
|
+
wasSummaryTracked?: boolean;
|
|
295
|
+
} = {
|
|
296
|
+
proposalHandle,
|
|
297
|
+
summaryRefSeq,
|
|
298
|
+
referenceSequenceNumber: this.referenceSequenceNumber,
|
|
299
|
+
};
|
|
300
|
+
return PerformanceEvent.timedExecAsync(
|
|
301
|
+
this.logger,
|
|
302
|
+
{
|
|
303
|
+
eventName: "refreshLatestSummary",
|
|
304
|
+
...eventProps,
|
|
305
|
+
},
|
|
306
|
+
async (event) => {
|
|
307
|
+
// Refresh latest summary should not happen while a summary is in progress. If it does, it can result
|
|
308
|
+
// in inconsistent state, so, we should not continue;
|
|
309
|
+
if (this.isSummaryInProgress()) {
|
|
310
|
+
throw new LoggingError("UnexpectedRefreshDuringSummarize", {
|
|
311
|
+
inProgressSummaryRefSeq: this.wipReferenceSequenceNumber,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (proposalHandle !== undefined) {
|
|
316
|
+
const maybeSummaryNode = this.pendingSummaries.get(proposalHandle);
|
|
317
|
+
|
|
318
|
+
if (maybeSummaryNode !== undefined) {
|
|
319
|
+
this.refreshLatestSummaryFromPending(
|
|
320
|
+
proposalHandle,
|
|
321
|
+
maybeSummaryNode.referenceSequenceNumber,
|
|
322
|
+
);
|
|
323
|
+
eventProps.wasSummaryTracked = true;
|
|
324
|
+
eventProps.latestSummaryUpdated = true;
|
|
325
|
+
event.end(eventProps);
|
|
326
|
+
return {
|
|
327
|
+
latestSummaryUpdated: true,
|
|
328
|
+
wasSummaryTracked: true,
|
|
329
|
+
summaryRefSeq,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const props = {
|
|
334
|
+
summaryRefSeq,
|
|
335
|
+
pendingSize: this.pendingSummaries.size ?? undefined,
|
|
336
|
+
};
|
|
337
|
+
this.logger.sendTelemetryEvent({
|
|
338
|
+
eventName: "PendingSummaryNotFound",
|
|
339
|
+
proposalHandle,
|
|
340
|
+
referenceSequenceNumber: this.referenceSequenceNumber,
|
|
341
|
+
details: JSON.stringify(props),
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// If the summary for which refresh is called is older than the latest tracked summary, ignore it.
|
|
346
|
+
if (this.referenceSequenceNumber >= summaryRefSeq) {
|
|
347
|
+
eventProps.latestSummaryUpdated = false;
|
|
348
|
+
event.end(eventProps);
|
|
349
|
+
return { latestSummaryUpdated: false };
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Fetch the latest snapshot and refresh state from it. Note that we need to use the reference sequence number
|
|
353
|
+
// of the fetched snapshot and not the "summaryRefSeq" that was passed in.
|
|
354
|
+
const { snapshotTree, snapshotRefSeq: fetchedSnapshotRefSeq } =
|
|
355
|
+
await fetchLatestSnapshot();
|
|
356
|
+
|
|
357
|
+
// Possible re-entrancy. We may have updated latest summary state while fetching the snapshot. If the fetched
|
|
358
|
+
// snapshot is older than the latest tracked summary, ignore it.
|
|
359
|
+
if (this.referenceSequenceNumber >= fetchedSnapshotRefSeq) {
|
|
360
|
+
eventProps.latestSummaryUpdated = false;
|
|
361
|
+
event.end(eventProps);
|
|
362
|
+
return { latestSummaryUpdated: false };
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
await this.refreshLatestSummaryFromSnapshot(
|
|
366
|
+
fetchedSnapshotRefSeq,
|
|
367
|
+
snapshotTree,
|
|
368
|
+
undefined,
|
|
369
|
+
EscapedPath.create(""),
|
|
370
|
+
correlatedSummaryLogger,
|
|
371
|
+
readAndParseBlob,
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
eventProps.latestSummaryUpdated = true;
|
|
375
|
+
eventProps.wasSummaryTracked = false;
|
|
376
|
+
eventProps.summaryRefSeq = fetchedSnapshotRefSeq;
|
|
377
|
+
event.end(eventProps);
|
|
378
|
+
return {
|
|
379
|
+
latestSummaryUpdated: true,
|
|
380
|
+
wasSummaryTracked: false,
|
|
381
|
+
snapshotTree,
|
|
382
|
+
summaryRefSeq: fetchedSnapshotRefSeq,
|
|
383
|
+
};
|
|
384
|
+
},
|
|
385
|
+
{ start: true, end: true, cancel: "error" },
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Called when we get an ack from the server for a summary we've just sent. Updates the reference state of this node
|
|
390
|
+
* from the state in the pending summary queue.
|
|
391
|
+
* @param proposalHandle - Handle for the current proposal.
|
|
392
|
+
* @param referenceSequenceNumber - reference sequence number of sent summary.
|
|
393
|
+
*/
|
|
394
|
+
protected refreshLatestSummaryFromPending(
|
|
395
|
+
proposalHandle: string,
|
|
396
|
+
referenceSequenceNumber: number,
|
|
397
|
+
): void {
|
|
398
|
+
const summaryNode = this.pendingSummaries.get(proposalHandle);
|
|
399
|
+
if (summaryNode === undefined) {
|
|
400
|
+
// This should only happen if parent skipped recursion AND no prior summary existed.
|
|
401
|
+
assert(
|
|
402
|
+
this._latestSummary === undefined,
|
|
403
|
+
0x1a6 /* "Not found pending summary, but this node has previously completed a summary" */,
|
|
404
|
+
);
|
|
405
|
+
return;
|
|
406
|
+
} else {
|
|
407
|
+
assert(
|
|
408
|
+
referenceSequenceNumber === summaryNode.referenceSequenceNumber,
|
|
409
|
+
0x1a7 /* Pending summary reference sequence number should be consistent */,
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
// Clear earlier pending summaries
|
|
413
|
+
this.pendingSummaries.delete(proposalHandle);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
this.refreshLatestSummaryCore(referenceSequenceNumber);
|
|
417
|
+
|
|
418
|
+
this._latestSummary = summaryNode;
|
|
419
|
+
// Propagate update to all child nodes
|
|
420
|
+
for (const child of this.children.values()) {
|
|
421
|
+
child.refreshLatestSummaryFromPending(proposalHandle, referenceSequenceNumber);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
protected async refreshLatestSummaryFromSnapshot(
|
|
426
|
+
referenceSequenceNumber: number,
|
|
427
|
+
snapshotTree: ISnapshotTree,
|
|
428
|
+
basePath: EscapedPath | undefined,
|
|
429
|
+
localPath: EscapedPath,
|
|
430
|
+
correlatedSummaryLogger: ITelemetryLogger,
|
|
431
|
+
readAndParseBlob: ReadAndParseBlob,
|
|
432
|
+
): Promise<void> {
|
|
433
|
+
// Possible re-entrancy. If we have already seen a summary later than this one, ignore it.
|
|
434
|
+
if (this.referenceSequenceNumber >= referenceSequenceNumber) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
this.refreshLatestSummaryCore(referenceSequenceNumber);
|
|
439
|
+
|
|
440
|
+
this._latestSummary = new SummaryNode({
|
|
441
|
+
referenceSequenceNumber,
|
|
442
|
+
basePath,
|
|
443
|
+
localPath,
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
const pathParts: string[] = [];
|
|
447
|
+
const { childrenTree, childrenPathPart } = parseSummaryForSubtrees(snapshotTree);
|
|
448
|
+
if (childrenPathPart !== undefined) {
|
|
449
|
+
pathParts.push(childrenPathPart);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (pathParts.length > 0) {
|
|
453
|
+
this._latestSummary.additionalPath = EscapedPath.createAndConcat(pathParts);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Propagate update to all child nodes
|
|
457
|
+
const pathForChildren = this._latestSummary.fullPathForChildren;
|
|
458
|
+
await Promise.all(
|
|
459
|
+
Array.from(this.children)
|
|
460
|
+
.filter(([id]) => {
|
|
461
|
+
// Assuming subtrees missing from snapshot are newer than the snapshot,
|
|
462
|
+
// but might be nice to assert this using earliest seq for node.
|
|
463
|
+
return childrenTree.trees[id] !== undefined;
|
|
464
|
+
})
|
|
465
|
+
.map(async ([id, child]) => {
|
|
466
|
+
return child.refreshLatestSummaryFromSnapshot(
|
|
467
|
+
referenceSequenceNumber,
|
|
468
|
+
childrenTree.trees[id],
|
|
469
|
+
pathForChildren,
|
|
470
|
+
EscapedPath.create(id),
|
|
471
|
+
correlatedSummaryLogger,
|
|
472
|
+
readAndParseBlob,
|
|
473
|
+
);
|
|
474
|
+
}),
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
private refreshLatestSummaryCore(referenceSequenceNumber: number): void {
|
|
479
|
+
for (const [key, value] of this.pendingSummaries) {
|
|
480
|
+
if (value.referenceSequenceNumber < referenceSequenceNumber) {
|
|
481
|
+
this.pendingSummaries.delete(key);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
public updateBaseSummaryState(snapshot: ISnapshotTree) {
|
|
487
|
+
// Check base summary to see if it has any additional path parts
|
|
488
|
+
// separating child SummarizerNodes. Checks for .channels subtrees.
|
|
489
|
+
const { childrenPathPart } = parseSummaryForSubtrees(snapshot);
|
|
490
|
+
if (childrenPathPart !== undefined && this._latestSummary !== undefined) {
|
|
491
|
+
this._latestSummary.additionalPath = EscapedPath.create(childrenPathPart);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
public recordChange(op: ISequencedDocumentMessage): void {
|
|
496
|
+
this.invalidate(op.sequenceNumber);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
public invalidate(sequenceNumber: number): void {
|
|
500
|
+
if (sequenceNumber > this._changeSequenceNumber) {
|
|
501
|
+
this._changeSequenceNumber = sequenceNumber;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* True if a change has been recorded with sequence number exceeding
|
|
507
|
+
* the latest successfully acked summary reference sequence number.
|
|
508
|
+
* False implies that the previous summary can be reused.
|
|
509
|
+
*/
|
|
510
|
+
protected hasChanged(): boolean {
|
|
511
|
+
return this._changeSequenceNumber > this.referenceSequenceNumber;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
public get latestSummary(): Readonly<SummaryNode> | undefined {
|
|
515
|
+
return this._latestSummary;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
protected readonly canReuseHandle: boolean;
|
|
519
|
+
|
|
520
|
+
public createChild(
|
|
521
|
+
/** Summarize function */
|
|
522
|
+
summarizeInternalFn: SummarizeInternalFn,
|
|
523
|
+
/** Initial id or path part of this node */
|
|
524
|
+
id: string,
|
|
525
|
+
/**
|
|
526
|
+
* Information needed to create the node.
|
|
527
|
+
* If it is from a base summary, it will assert that a summary has been seen.
|
|
528
|
+
* Attach information if it is created from an attach op.
|
|
529
|
+
*/
|
|
530
|
+
createParam: CreateChildSummarizerNodeParam,
|
|
531
|
+
config: ISummarizerNodeConfig = {},
|
|
532
|
+
): ISummarizerNode {
|
|
533
|
+
assert(!this.children.has(id), 0x1ab /* "Create SummarizerNode child already exists" */);
|
|
534
|
+
|
|
535
|
+
const createDetails: ICreateChildDetails = this.getCreateDetailsForChild(id, createParam);
|
|
536
|
+
const child = new SummarizerNode(
|
|
537
|
+
this.logger,
|
|
538
|
+
summarizeInternalFn,
|
|
539
|
+
config,
|
|
540
|
+
createDetails.changeSequenceNumber,
|
|
541
|
+
createDetails.latestSummary,
|
|
542
|
+
createDetails.initialSummary,
|
|
543
|
+
this.wipSummaryLogger,
|
|
544
|
+
createDetails.telemetryNodeId,
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
// There may be additional state that has to be updated in this child. For example, if a summary is being
|
|
548
|
+
// tracked, the child's summary tracking state needs to be updated too. Same goes for pendingSummaries we might
|
|
549
|
+
// have outstanding on the parent in case we realize nodes in between Summary Op and Summary Ack.
|
|
550
|
+
this.maybeUpdateChildState(child);
|
|
551
|
+
|
|
552
|
+
this.children.set(id, child);
|
|
553
|
+
return child;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
public getChild(id: string): ISummarizerNode | undefined {
|
|
557
|
+
return this.children.get(id);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Returns the details needed to create a child node.
|
|
562
|
+
* @param id - Initial id or path part of the child node.
|
|
563
|
+
* @param createParam - Information needed to create the node.
|
|
564
|
+
* @returns the details needed to create the child node.
|
|
565
|
+
*/
|
|
566
|
+
protected getCreateDetailsForChild(
|
|
567
|
+
id: string,
|
|
568
|
+
createParam: CreateChildSummarizerNodeParam,
|
|
569
|
+
): ICreateChildDetails {
|
|
570
|
+
let initialSummary: IInitialSummary | undefined;
|
|
571
|
+
let latestSummary: SummaryNode | undefined;
|
|
572
|
+
let changeSequenceNumber: number;
|
|
573
|
+
|
|
574
|
+
const parentLatestSummary = this._latestSummary;
|
|
575
|
+
switch (createParam.type) {
|
|
576
|
+
case CreateSummarizerNodeSource.FromAttach: {
|
|
577
|
+
if (
|
|
578
|
+
parentLatestSummary !== undefined &&
|
|
579
|
+
createParam.sequenceNumber <= parentLatestSummary.referenceSequenceNumber
|
|
580
|
+
) {
|
|
581
|
+
// Prioritize latest summary if it was after this node was attached.
|
|
582
|
+
latestSummary = parentLatestSummary.createForChild(id);
|
|
583
|
+
} else {
|
|
584
|
+
const summary = convertToSummaryTree(
|
|
585
|
+
createParam.snapshot,
|
|
586
|
+
) as ISummaryTreeWithStats;
|
|
587
|
+
initialSummary = {
|
|
588
|
+
sequenceNumber: createParam.sequenceNumber,
|
|
589
|
+
id,
|
|
590
|
+
summary,
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
changeSequenceNumber = createParam.sequenceNumber;
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
case CreateSummarizerNodeSource.FromSummary: {
|
|
597
|
+
if (this.initialSummary === undefined) {
|
|
598
|
+
assert(
|
|
599
|
+
!!parentLatestSummary,
|
|
600
|
+
0x1ac /* "Cannot create child from summary if parent does not have latest summary" */,
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
// fallthrough to local
|
|
604
|
+
}
|
|
605
|
+
case CreateSummarizerNodeSource.Local: {
|
|
606
|
+
const parentInitialSummary = this.initialSummary;
|
|
607
|
+
if (parentInitialSummary !== undefined) {
|
|
608
|
+
let childSummary: SummaryObject | undefined;
|
|
609
|
+
if (parentInitialSummary.summary !== undefined) {
|
|
610
|
+
const { childrenTree } = parseSummaryTreeForSubtrees(
|
|
611
|
+
parentInitialSummary.summary.summary,
|
|
612
|
+
);
|
|
613
|
+
assert(
|
|
614
|
+
childrenTree.type === SummaryType.Tree,
|
|
615
|
+
0x1d6 /* "Parent summary object is not a tree" */,
|
|
616
|
+
);
|
|
617
|
+
childSummary = childrenTree.tree[id];
|
|
618
|
+
}
|
|
619
|
+
if (createParam.type === CreateSummarizerNodeSource.FromSummary) {
|
|
620
|
+
// Locally created would not have differential subtree.
|
|
621
|
+
assert(!!childSummary, 0x1ad /* "Missing child summary tree" */);
|
|
622
|
+
}
|
|
623
|
+
let childSummaryWithStats: ISummaryTreeWithStats | undefined;
|
|
624
|
+
if (childSummary !== undefined) {
|
|
625
|
+
assert(
|
|
626
|
+
childSummary.type === SummaryType.Tree,
|
|
627
|
+
0x1ae /* "Child summary object is not a tree" */,
|
|
628
|
+
);
|
|
629
|
+
childSummaryWithStats = {
|
|
630
|
+
summary: childSummary,
|
|
631
|
+
stats: calculateStats(childSummary),
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
initialSummary = {
|
|
635
|
+
sequenceNumber: parentInitialSummary.sequenceNumber,
|
|
636
|
+
id,
|
|
637
|
+
summary: childSummaryWithStats,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
latestSummary = parentLatestSummary?.createForChild(id);
|
|
641
|
+
changeSequenceNumber = parentLatestSummary?.referenceSequenceNumber ?? -1;
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
default: {
|
|
645
|
+
const type = (createParam as unknown as CreateChildSummarizerNodeParam).type;
|
|
646
|
+
unreachableCase(createParam, `Unexpected CreateSummarizerNodeSource: ${type}`);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const childtelemetryNodeId = `${this.telemetryNodeId ?? ""}/${id}`;
|
|
651
|
+
|
|
652
|
+
return {
|
|
653
|
+
initialSummary,
|
|
654
|
+
latestSummary,
|
|
655
|
+
changeSequenceNumber,
|
|
656
|
+
telemetryNodeId: childtelemetryNodeId,
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Updates the state of the child if required. For example, if a summary is currently being tracked, the child's
|
|
662
|
+
* summary tracking state needs to be updated too.
|
|
663
|
+
* Also, in case a child node gets realized in between Summary Op and Summary Ack, let's initialize the child's
|
|
664
|
+
* pending summary as well.
|
|
665
|
+
* @param child - The child node whose state is to be updated.
|
|
666
|
+
*/
|
|
667
|
+
protected maybeUpdateChildState(child: SummarizerNode) {
|
|
668
|
+
// If a summary is in progress, this child was created after the summary started. So, we need to update the
|
|
669
|
+
// child's summary state as well.
|
|
670
|
+
if (this.isSummaryInProgress()) {
|
|
671
|
+
child.wipReferenceSequenceNumber = this.wipReferenceSequenceNumber;
|
|
672
|
+
}
|
|
673
|
+
// In case we have pending summaries on the parent, let's initialize it on the child.
|
|
674
|
+
if (child._latestSummary !== undefined) {
|
|
675
|
+
for (const [key, value] of this.pendingSummaries.entries()) {
|
|
676
|
+
const newLatestSummaryNode = new SummaryNode({
|
|
677
|
+
referenceSequenceNumber: value.referenceSequenceNumber,
|
|
678
|
+
basePath: child._latestSummary.basePath,
|
|
679
|
+
localPath: child._latestSummary.localPath,
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
child.addPendingSummary(key, newLatestSummaryNode);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
protected addPendingSummary(key: string, summary: SummaryNode) {
|
|
688
|
+
this.pendingSummaries.set(key, summary);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Tells whether summary tracking is in progress. True if "startSummary" API is called before summarize.
|
|
693
|
+
*/
|
|
694
|
+
public isSummaryInProgress(): boolean {
|
|
695
|
+
return this.wipReferenceSequenceNumber !== undefined;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Creates and throws an error due to unexpected conditions.
|
|
700
|
+
*/
|
|
701
|
+
protected throwUnexpectedError(eventProps: ITelemetryErrorEvent): never {
|
|
702
|
+
const error = new LoggingError(eventProps.eventName, {
|
|
703
|
+
...eventProps,
|
|
704
|
+
referenceSequenceNumber: this.wipReferenceSequenceNumber,
|
|
705
|
+
id: {
|
|
706
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
707
|
+
value: this.telemetryNodeId,
|
|
708
|
+
},
|
|
709
|
+
});
|
|
710
|
+
this.logger.sendErrorEvent(eventProps, error);
|
|
711
|
+
throw error;
|
|
712
|
+
}
|
|
595
713
|
}
|
|
596
714
|
|
|
597
715
|
/**
|
|
@@ -602,17 +720,26 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
602
720
|
* @param referenceSequenceNumber - Reference sequence number of last acked summary,
|
|
603
721
|
* or undefined if not loaded from summary
|
|
604
722
|
* @param config - Configure behavior of summarizer node
|
|
723
|
+
*
|
|
724
|
+
* @deprecated Internal implementation detail and will no longer be exported in an
|
|
725
|
+
* upcoming release.
|
|
605
726
|
*/
|
|
606
727
|
export const createRootSummarizerNode = (
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
): IRootSummarizerNode =>
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
728
|
+
logger: ITelemetryLogger,
|
|
729
|
+
summarizeInternalFn: SummarizeInternalFn,
|
|
730
|
+
changeSequenceNumber: number,
|
|
731
|
+
referenceSequenceNumber: number | undefined,
|
|
732
|
+
config: ISummarizerNodeConfig = {},
|
|
733
|
+
): IRootSummarizerNode =>
|
|
734
|
+
new SummarizerNode(
|
|
735
|
+
logger,
|
|
736
|
+
summarizeInternalFn,
|
|
737
|
+
config,
|
|
738
|
+
changeSequenceNumber,
|
|
739
|
+
referenceSequenceNumber === undefined
|
|
740
|
+
? undefined
|
|
741
|
+
: SummaryNode.createForRoot(referenceSequenceNumber),
|
|
742
|
+
undefined /* initialSummary */,
|
|
743
|
+
undefined /* wipSummaryLogger */,
|
|
744
|
+
"" /* telemetryNodeId */,
|
|
745
|
+
);
|