@fluidframework/container-runtime 0.58.2001 → 0.59.2000-61729

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 (124) hide show
  1. package/dist/blobManager.d.ts +15 -2
  2. package/dist/blobManager.d.ts.map +1 -1
  3. package/dist/blobManager.js +65 -9
  4. package/dist/blobManager.js.map +1 -1
  5. package/dist/connectionTelemetry.d.ts.map +1 -1
  6. package/dist/connectionTelemetry.js +63 -23
  7. package/dist/connectionTelemetry.js.map +1 -1
  8. package/dist/containerRuntime.d.ts +39 -7
  9. package/dist/containerRuntime.d.ts.map +1 -1
  10. package/dist/containerRuntime.js +161 -29
  11. package/dist/containerRuntime.js.map +1 -1
  12. package/dist/dataStore.js +8 -1
  13. package/dist/dataStore.js.map +1 -1
  14. package/dist/dataStoreContext.d.ts +9 -3
  15. package/dist/dataStoreContext.d.ts.map +1 -1
  16. package/dist/dataStoreContext.js +22 -6
  17. package/dist/dataStoreContext.js.map +1 -1
  18. package/dist/dataStores.d.ts +13 -5
  19. package/dist/dataStores.d.ts.map +1 -1
  20. package/dist/dataStores.js +39 -18
  21. package/dist/dataStores.js.map +1 -1
  22. package/dist/deltaScheduler.d.ts +4 -5
  23. package/dist/deltaScheduler.d.ts.map +1 -1
  24. package/dist/deltaScheduler.js +54 -35
  25. package/dist/deltaScheduler.js.map +1 -1
  26. package/dist/garbageCollection.d.ts +31 -27
  27. package/dist/garbageCollection.d.ts.map +1 -1
  28. package/dist/garbageCollection.js +76 -75
  29. package/dist/garbageCollection.js.map +1 -1
  30. package/dist/opTelemetry.d.ts +22 -0
  31. package/dist/opTelemetry.d.ts.map +1 -0
  32. package/dist/opTelemetry.js +59 -0
  33. package/dist/opTelemetry.js.map +1 -0
  34. package/dist/orderedClientElection.d.ts +57 -6
  35. package/dist/orderedClientElection.d.ts.map +1 -1
  36. package/dist/orderedClientElection.js +141 -26
  37. package/dist/orderedClientElection.js.map +1 -1
  38. package/dist/packageVersion.d.ts +1 -1
  39. package/dist/packageVersion.d.ts.map +1 -1
  40. package/dist/packageVersion.js +1 -1
  41. package/dist/packageVersion.js.map +1 -1
  42. package/dist/summarizerClientElection.d.ts +2 -0
  43. package/dist/summarizerClientElection.d.ts.map +1 -1
  44. package/dist/summarizerClientElection.js +15 -2
  45. package/dist/summarizerClientElection.js.map +1 -1
  46. package/dist/summarizerTypes.d.ts +9 -0
  47. package/dist/summarizerTypes.d.ts.map +1 -1
  48. package/dist/summarizerTypes.js.map +1 -1
  49. package/dist/summaryGenerator.d.ts.map +1 -1
  50. package/dist/summaryGenerator.js +3 -4
  51. package/dist/summaryGenerator.js.map +1 -1
  52. package/dist/summaryManager.d.ts.map +1 -1
  53. package/dist/summaryManager.js +14 -3
  54. package/dist/summaryManager.js.map +1 -1
  55. package/lib/blobManager.d.ts +15 -2
  56. package/lib/blobManager.d.ts.map +1 -1
  57. package/lib/blobManager.js +66 -10
  58. package/lib/blobManager.js.map +1 -1
  59. package/lib/connectionTelemetry.d.ts.map +1 -1
  60. package/lib/connectionTelemetry.js +63 -23
  61. package/lib/connectionTelemetry.js.map +1 -1
  62. package/lib/containerRuntime.d.ts +39 -7
  63. package/lib/containerRuntime.d.ts.map +1 -1
  64. package/lib/containerRuntime.js +163 -31
  65. package/lib/containerRuntime.js.map +1 -1
  66. package/lib/dataStore.js +8 -1
  67. package/lib/dataStore.js.map +1 -1
  68. package/lib/dataStoreContext.d.ts +9 -3
  69. package/lib/dataStoreContext.d.ts.map +1 -1
  70. package/lib/dataStoreContext.js +22 -6
  71. package/lib/dataStoreContext.js.map +1 -1
  72. package/lib/dataStores.d.ts +13 -5
  73. package/lib/dataStores.d.ts.map +1 -1
  74. package/lib/dataStores.js +39 -18
  75. package/lib/dataStores.js.map +1 -1
  76. package/lib/deltaScheduler.d.ts +4 -5
  77. package/lib/deltaScheduler.d.ts.map +1 -1
  78. package/lib/deltaScheduler.js +54 -35
  79. package/lib/deltaScheduler.js.map +1 -1
  80. package/lib/garbageCollection.d.ts +31 -27
  81. package/lib/garbageCollection.d.ts.map +1 -1
  82. package/lib/garbageCollection.js +75 -74
  83. package/lib/garbageCollection.js.map +1 -1
  84. package/lib/opTelemetry.d.ts +22 -0
  85. package/lib/opTelemetry.d.ts.map +1 -0
  86. package/lib/opTelemetry.js +55 -0
  87. package/lib/opTelemetry.js.map +1 -0
  88. package/lib/orderedClientElection.d.ts +57 -6
  89. package/lib/orderedClientElection.d.ts.map +1 -1
  90. package/lib/orderedClientElection.js +141 -26
  91. package/lib/orderedClientElection.js.map +1 -1
  92. package/lib/packageVersion.d.ts +1 -1
  93. package/lib/packageVersion.d.ts.map +1 -1
  94. package/lib/packageVersion.js +1 -1
  95. package/lib/packageVersion.js.map +1 -1
  96. package/lib/summarizerClientElection.d.ts +2 -0
  97. package/lib/summarizerClientElection.d.ts.map +1 -1
  98. package/lib/summarizerClientElection.js +15 -2
  99. package/lib/summarizerClientElection.js.map +1 -1
  100. package/lib/summarizerTypes.d.ts +9 -0
  101. package/lib/summarizerTypes.d.ts.map +1 -1
  102. package/lib/summarizerTypes.js.map +1 -1
  103. package/lib/summaryGenerator.d.ts.map +1 -1
  104. package/lib/summaryGenerator.js +3 -4
  105. package/lib/summaryGenerator.js.map +1 -1
  106. package/lib/summaryManager.d.ts.map +1 -1
  107. package/lib/summaryManager.js +14 -3
  108. package/lib/summaryManager.js.map +1 -1
  109. package/package.json +63 -19
  110. package/src/blobManager.ts +78 -11
  111. package/src/connectionTelemetry.ts +110 -19
  112. package/src/containerRuntime.ts +191 -36
  113. package/src/dataStore.ts +7 -1
  114. package/src/dataStoreContext.ts +22 -7
  115. package/src/dataStores.ts +40 -19
  116. package/src/deltaScheduler.ts +65 -39
  117. package/src/garbageCollection.ts +92 -78
  118. package/src/opTelemetry.ts +71 -0
  119. package/src/orderedClientElection.ts +155 -25
  120. package/src/packageVersion.ts +1 -1
  121. package/src/summarizerClientElection.ts +15 -2
  122. package/src/summarizerTypes.ts +9 -0
  123. package/src/summaryGenerator.ts +10 -8
  124. package/src/summaryManager.ts +15 -4
package/src/dataStores.ts CHANGED
@@ -18,7 +18,6 @@ import {
18
18
  CreateSummarizerNodeSource,
19
19
  IAttachMessage,
20
20
  IEnvelope,
21
- IFluidDataStoreChannel,
22
21
  IFluidDataStoreContextDetached,
23
22
  IGarbageCollectionData,
24
23
  IGarbageCollectionDetailsBase,
@@ -157,7 +156,7 @@ export class DataStores implements IDisposable {
157
156
  key,
158
157
  { type: CreateSummarizerNodeSource.FromSummary },
159
158
  ),
160
- bindChannelFn: (cr: IFluidDataStoreChannel) => this.bindFluidDataStore(cr),
159
+ makeLocallyVisibleFn: () => this.makeDataStoreLocallyVisible(key),
161
160
  snapshotTree,
162
161
  isRootDataStore: undefined,
163
162
  writeGCDataAtRoot: this.writeGCDataAtRoot,
@@ -291,14 +290,20 @@ export class DataStores implements IDisposable {
291
290
  return this.aliasMap.get(id) !== undefined || this.contexts.get(id) !== undefined;
292
291
  }
293
292
 
294
- public bindFluidDataStore(fluidDataStoreRuntime: IFluidDataStoreChannel): void {
295
- const id = fluidDataStoreRuntime.id;
293
+ /**
294
+ * Make the data stores locally visible in the container graph by moving the data store context from unbound to
295
+ * bound list. This data store can now be reached from the root.
296
+ * @param id - The id of the data store context to make visible.
297
+ */
298
+ private makeDataStoreLocallyVisible(id: string): void {
296
299
  const localContext = this.contexts.getUnbound(id);
297
300
  assert(!!localContext, 0x15f /* "Could not find unbound context to bind" */);
298
301
 
299
- // If the container is detached, we don't need to send OP or add to pending attach because
300
- // we will summarize it while uploading the create new summary and make it known to other
301
- // clients.
302
+ /**
303
+ * If the container is not detached, it is globally visible to all clients. This data store should also be
304
+ * globally visible. Move it to attaching state and send an "attach" op for it.
305
+ * If the container is detached, this data store will be part of the summary that makes the container attached.
306
+ */
302
307
  if (this.runtime.attachState !== AttachState.Detached) {
303
308
  localContext.emit("attaching");
304
309
  const message = localContext.generateAttachMessage();
@@ -308,7 +313,7 @@ export class DataStores implements IDisposable {
308
313
  this.attachOpFiredForDataStore.add(id);
309
314
  }
310
315
 
311
- this.contexts.bind(fluidDataStoreRuntime.id);
316
+ this.contexts.bind(id);
312
317
  }
313
318
 
314
319
  public createDetachedDataStoreCore(
@@ -326,7 +331,7 @@ export class DataStores implements IDisposable {
326
331
  id,
327
332
  { type: CreateSummarizerNodeSource.Local },
328
333
  ),
329
- bindChannelFn: (cr: IFluidDataStoreChannel) => this.bindFluidDataStore(cr),
334
+ makeLocallyVisibleFn: () => this.makeDataStoreLocallyVisible(id),
330
335
  snapshotTree: undefined,
331
336
  isRootDataStore: isRoot,
332
337
  writeGCDataAtRoot: this.writeGCDataAtRoot,
@@ -347,7 +352,7 @@ export class DataStores implements IDisposable {
347
352
  id,
348
353
  { type: CreateSummarizerNodeSource.Local },
349
354
  ),
350
- bindChannelFn: (cr: IFluidDataStoreChannel) => this.bindFluidDataStore(cr),
355
+ makeLocallyVisibleFn: () => this.makeDataStoreLocallyVisible(id),
351
356
  snapshotTree: undefined,
352
357
  isRootDataStore: isRoot,
353
358
  writeGCDataAtRoot: this.writeGCDataAtRoot,
@@ -587,7 +592,14 @@ export class DataStores implements IDisposable {
587
592
  */
588
593
  public deleteUnusedRoutes(unusedRoutes: string[]) {
589
594
  for (const route of unusedRoutes) {
590
- const dataStoreId = route.split("/")[1];
595
+ const pathParts = route.split("/");
596
+ // Delete data store only if its route (/datastoreId) is in unusedRoutes. We don't want to delete a data
597
+ // store based on its DDS being unused.
598
+ if (pathParts.length > 2) {
599
+ continue;
600
+ }
601
+ const dataStoreId = pathParts[1];
602
+ assert(this.contexts.has(dataStoreId), 0x2d7 /* `${dataStoreId} is not a data store` */);
591
603
  // Delete the contexts of unused data stores.
592
604
  this.contexts.delete(dataStoreId);
593
605
  // Delete the summarizer node of the unused data stores.
@@ -611,15 +623,24 @@ export class DataStores implements IDisposable {
611
623
  }
612
624
 
613
625
  /**
614
- * Returns the package path of the node with the given path. This is used by GC to log when an inactive / deleted
615
- * node is used.
626
+ * Called during GC to retrieve the package path of a data store node with the given path.
627
+ */
628
+ public getDataStorePackagePath(nodePath: string): readonly string[] | undefined {
629
+ // If the node belongs to a data store, return its package path if the data store is loaded. For DDSs, we return
630
+ // the package path of the data store that contains it.
631
+ const context = this.contexts.get(nodePath.split("/")[1]);
632
+ return context?.isLoaded ? context.packagePath : undefined;
633
+ }
634
+
635
+ /**
636
+ * Called by GC to know if a node is a data store or not. Data store ids are of the format "/dataStoreId".
616
637
  */
617
- public getNodePackagePath(nodePath: string): readonly string[] | undefined {
618
- // Currently, only return the data store package path for the node since GC is only interested in data stores.
619
- const dataStoreId = nodePath.split("/")[1];
620
- const context = this.contexts.get(dataStoreId);
621
- assert(context !== undefined, 0x2b9 /* "Data store with given id does not exist" */);
622
- return context.isLoaded ? context.packagePath : undefined;
638
+ public isDataStoreNode(nodePath: string): boolean {
639
+ const pathParts = nodePath.split("/");
640
+ if (pathParts.length === 2 && this.contexts.has(pathParts[1])) {
641
+ return true;
642
+ }
643
+ return false;
623
644
  }
624
645
  }
625
646
 
@@ -11,6 +11,9 @@ import {
11
11
  ISequencedDocumentMessage,
12
12
  } from "@fluidframework/protocol-definitions";
13
13
 
14
+ import {
15
+ TelemetryLogger,
16
+ } from "@fluidframework/telemetry-utils";
14
17
  /**
15
18
  * DeltaScheduler is responsible for the scheduling of inbound delta queue in cases where there
16
19
  * is more than one op a particular run of the queue. It does not schedule if there is just one
@@ -25,18 +28,13 @@ import {
25
28
  export class DeltaScheduler {
26
29
  private readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>;
27
30
  // The time for processing ops in a single turn.
28
- public static readonly processingTime = 20;
31
+ public static readonly processingTime = 50;
29
32
 
30
33
  // The increase in time for processing ops after each turn.
31
34
  private readonly processingTimeIncrement = 10;
32
35
 
33
36
  private processingStartTime: number | undefined;
34
- private totalProcessingTime: number = DeltaScheduler.processingTime;
35
-
36
- // This keeps track of whether the delta scheduler is scheduling a particular run of the
37
- // the inbound delta queue. Basically, every time the delta queue starts processing with
38
- // more than one op, this will be set to true until the run completes.
39
- private isScheduling: boolean = false;
37
+ private currentAllowedProcessingTimeForTurn: number = DeltaScheduler.processingTime;
40
38
 
41
39
  // This keeps track of the number of times inbound queue has been scheduled. After a particular
42
40
  // count, we log telemetry for the number of ops processed, the time and number of turns it took
@@ -44,9 +42,13 @@ export class DeltaScheduler {
44
42
  private schedulingCount: number = 0;
45
43
 
46
44
  private schedulingLog: {
47
- numberOfOps: number;
45
+ opsRemainingToProcess: number;
48
46
  totalProcessingTime: number;
49
47
  numberOfTurns: number;
48
+ numberOfBatchesProcessed: number;
49
+ lastSequenceNumber: number;
50
+ firstSequenceNumber: number;
51
+ startTime: number;
50
52
  } | undefined;
51
53
 
52
54
  constructor(
@@ -57,50 +59,72 @@ export class DeltaScheduler {
57
59
  this.deltaManager.inbound.on("idle", () => { this.inboundQueueIdle(); });
58
60
  }
59
61
 
60
- public batchBegin() {
62
+ public batchBegin(message: ISequencedDocumentMessage) {
61
63
  if (!this.processingStartTime) {
62
64
  this.processingStartTime = performance.now();
63
65
  }
66
+ if (this.schedulingLog === undefined && this.schedulingCount % 500 === 0) {
67
+ // Every 500th time we are scheduling the inbound queue, we log telemetry for the
68
+ // number of ops processed, the time and number of turns it took to process the ops.
69
+ this.schedulingLog = {
70
+ opsRemainingToProcess: 0,
71
+ numberOfTurns: 1,
72
+ totalProcessingTime: 0,
73
+ numberOfBatchesProcessed: 0,
74
+ firstSequenceNumber: message.sequenceNumber,
75
+ lastSequenceNumber: message.sequenceNumber,
76
+ startTime: performance.now(),
77
+ };
78
+ }
64
79
  }
65
80
 
66
- public batchEnd() {
67
- if (this.shouldRunScheduler()) {
68
- if (!this.isScheduling) {
69
- this.isScheduling = true;
70
- // Every 2000th time we are scheduling the inbound queue, we log telemetry for the
71
- // number of ops processed, the time and number of turns it took to process the ops.
72
- if (this.schedulingCount % 2000 === 0) {
73
- this.schedulingLog = {
74
- numberOfOps: this.deltaManager.inbound.length,
75
- numberOfTurns: 1,
76
- totalProcessingTime: 0,
77
- };
78
- }
79
- }
81
+ public batchEnd(message: ISequencedDocumentMessage) {
82
+ if (this.schedulingLog) {
83
+ this.schedulingLog.numberOfBatchesProcessed++;
84
+ this.schedulingLog.lastSequenceNumber = message.sequenceNumber;
85
+ this.schedulingLog.opsRemainingToProcess = this.deltaManager.inbound.length;
86
+ }
80
87
 
88
+ if (this.shouldRunScheduler()) {
89
+ const currentTime = performance.now();
81
90
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
82
- const elapsedTime = performance.now() - this.processingStartTime!;
83
- if (elapsedTime > this.totalProcessingTime) {
91
+ const elapsedTime = currentTime - this.processingStartTime!;
92
+ if (elapsedTime > this.currentAllowedProcessingTimeForTurn) {
84
93
  // We have processed ops for more than the total processing time. So, pause the
85
94
  // queue, yield the thread and schedule a resume.
86
95
 
87
96
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
88
97
  this.deltaManager.inbound.pause();
89
- setTimeout(() => {
90
- this.deltaManager.inbound.resume();
91
- });
92
98
 
93
- this.processingStartTime = undefined;
94
- // Increase the total processing time. Keep doing this after each turn until all the ops have
99
+ // Increase the total processing time. Keep doing this after each turn until all the ops have
95
100
  // been processed. This way we keep the responsiveness at the beginning while also making sure
96
101
  // that all the ops process fairly quickly.
97
- this.totalProcessingTime += this.processingTimeIncrement;
102
+ this.currentAllowedProcessingTimeForTurn += this.processingTimeIncrement;
98
103
 
99
104
  // If we are logging the telemetry this time, update the telemetry log object.
100
105
  if (this.schedulingLog) {
101
106
  this.schedulingLog.numberOfTurns++;
102
107
  this.schedulingLog.totalProcessingTime += elapsedTime;
103
108
  }
109
+
110
+ setTimeout(() => {
111
+ if (this.schedulingLog) {
112
+ this.logger.sendTelemetryEvent({
113
+ eventName: "InboundOpsPartialProcessingTime",
114
+ duration: TelemetryLogger.formatTick(elapsedTime),
115
+ opsProcessed: this.schedulingLog.lastSequenceNumber -
116
+ this.schedulingLog.firstSequenceNumber + 1,
117
+ opsRemainingToProcess: this.deltaManager.inbound.length,
118
+ processingTime: TelemetryLogger.formatTick(this.schedulingLog.totalProcessingTime),
119
+ numberOfTurns: this.schedulingLog.numberOfTurns,
120
+ batchesProcessed: this.schedulingLog.numberOfBatchesProcessed,
121
+ timeToResume: TelemetryLogger.formatTick(performance.now() - currentTime),
122
+ });
123
+ }
124
+ this.deltaManager.inbound.resume();
125
+ });
126
+
127
+ this.processingStartTime = undefined;
104
128
  }
105
129
  }
106
130
  }
@@ -109,14 +133,19 @@ export class DeltaScheduler {
109
133
  if (this.schedulingLog) {
110
134
  // Add the time taken for processing the final ops to the total processing time in the
111
135
  // telemetry log object.
136
+ const currentTime = performance.now();
112
137
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
113
- this.schedulingLog.totalProcessingTime += performance.now() - this.processingStartTime!;
138
+ this.schedulingLog.totalProcessingTime += currentTime - this.processingStartTime!;
114
139
 
115
140
  this.logger.sendTelemetryEvent({
116
141
  eventName: "InboundOpsProcessingTime",
117
- numberOfOps: this.schedulingLog.numberOfOps,
142
+ opsRemainingToProcess: this.schedulingLog.opsRemainingToProcess,
118
143
  numberOfTurns: this.schedulingLog.numberOfTurns,
119
- processingTime: this.schedulingLog.totalProcessingTime,
144
+ processingTime: TelemetryLogger.formatTick(this.schedulingLog.totalProcessingTime),
145
+ opsProcessed: this.schedulingLog.lastSequenceNumber - this.schedulingLog.firstSequenceNumber + 1,
146
+ batchesProcessed: this.schedulingLog.numberOfBatchesProcessed,
147
+ duration: TelemetryLogger.formatTick(currentTime - this.schedulingLog.startTime),
148
+ schedulingCount: this.schedulingCount,
120
149
  });
121
150
 
122
151
  this.schedulingLog = undefined;
@@ -124,14 +153,11 @@ export class DeltaScheduler {
124
153
 
125
154
  // If we scheduled this batch of the inbound queue, increment the counter that tracks the
126
155
  // number of times we have done this.
127
- if (this.isScheduling) {
128
- this.isScheduling = false;
129
- this.schedulingCount++;
130
- }
156
+ this.schedulingCount++;
131
157
 
132
158
  // Reset the processing times.
133
159
  this.processingStartTime = undefined;
134
- this.totalProcessingTime = DeltaScheduler.processingTime;
160
+ this.currentAllowedProcessingTimeForTurn = DeltaScheduler.processingTime;
135
161
  }
136
162
 
137
163
  /**