@fluidframework/container-runtime 2.0.0-internal.6.4.0 → 2.0.0-internal.7.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 (237) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/api-extractor.json +13 -1
  3. package/api-report/container-runtime.api.md +799 -0
  4. package/dist/blobManager.d.ts +1 -1
  5. package/dist/blobManager.d.ts.map +1 -1
  6. package/dist/blobManager.js +7 -7
  7. package/dist/blobManager.js.map +1 -1
  8. package/dist/connectionTelemetry.d.ts.map +1 -1
  9. package/dist/connectionTelemetry.js +75 -42
  10. package/dist/connectionTelemetry.js.map +1 -1
  11. package/dist/container-runtime-alpha.d.ts +1554 -0
  12. package/dist/container-runtime-beta.d.ts +1554 -0
  13. package/dist/container-runtime-public.d.ts +1554 -0
  14. package/dist/container-runtime.d.ts +1611 -0
  15. package/dist/containerHandleContext.js +3 -3
  16. package/dist/containerHandleContext.js.map +1 -1
  17. package/dist/containerRuntime.d.ts +28 -24
  18. package/dist/containerRuntime.d.ts.map +1 -1
  19. package/dist/containerRuntime.js +277 -256
  20. package/dist/containerRuntime.js.map +1 -1
  21. package/dist/dataStore.js +9 -9
  22. package/dist/dataStore.js.map +1 -1
  23. package/dist/dataStoreContext.d.ts +1 -3
  24. package/dist/dataStoreContext.d.ts.map +1 -1
  25. package/dist/dataStoreContext.js +54 -58
  26. package/dist/dataStoreContext.js.map +1 -1
  27. package/dist/dataStoreRegistry.js +3 -3
  28. package/dist/dataStoreRegistry.js.map +1 -1
  29. package/dist/deltaManagerProxyBase.js +4 -4
  30. package/dist/deltaManagerProxyBase.js.map +1 -1
  31. package/dist/deltaManagerSummarizerProxy.js +6 -6
  32. package/dist/deltaManagerSummarizerProxy.js.map +1 -1
  33. package/dist/deltaScheduler.js.map +1 -1
  34. package/dist/error.d.ts.map +1 -1
  35. package/dist/error.js.map +1 -1
  36. package/dist/gc/garbageCollection.js +13 -13
  37. package/dist/gc/garbageCollection.js.map +1 -1
  38. package/dist/gc/gcDefinitions.d.ts +4 -15
  39. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  40. package/dist/gc/gcDefinitions.js.map +1 -1
  41. package/dist/gc/gcTelemetry.d.ts +1 -1
  42. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  43. package/dist/gc/gcUnreferencedStateTracker.js +3 -3
  44. package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
  45. package/dist/id-compressor/appendOnlySortedMap.js.map +1 -1
  46. package/dist/id-compressor/idCompressor.js.map +1 -1
  47. package/dist/id-compressor/identifiers.d.ts +3 -3
  48. package/dist/id-compressor/identifiers.d.ts.map +1 -1
  49. package/dist/index.d.ts +1 -1
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +2 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/messageTypes.d.ts +17 -17
  54. package/dist/messageTypes.d.ts.map +1 -1
  55. package/dist/messageTypes.js +1 -1
  56. package/dist/messageTypes.js.map +1 -1
  57. package/dist/opLifecycle/batchManager.js +6 -6
  58. package/dist/opLifecycle/batchManager.js.map +1 -1
  59. package/dist/opLifecycle/definitions.d.ts +2 -2
  60. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  61. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  62. package/dist/opLifecycle/outbox.js +7 -2
  63. package/dist/opLifecycle/outbox.js.map +1 -1
  64. package/dist/packageVersion.d.ts +1 -1
  65. package/dist/packageVersion.js +1 -1
  66. package/dist/packageVersion.js.map +1 -1
  67. package/dist/pendingStateManager.d.ts +3 -19
  68. package/dist/pendingStateManager.d.ts.map +1 -1
  69. package/dist/pendingStateManager.js +23 -40
  70. package/dist/pendingStateManager.js.map +1 -1
  71. package/dist/scheduleManager.js +6 -2
  72. package/dist/scheduleManager.js.map +1 -1
  73. package/dist/summary/orderedClientElection.d.ts +3 -3
  74. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  75. package/dist/summary/orderedClientElection.js +54 -54
  76. package/dist/summary/orderedClientElection.js.map +1 -1
  77. package/dist/summary/runWhileConnectedCoordinator.js +6 -6
  78. package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
  79. package/dist/summary/runningSummarizer.js +37 -37
  80. package/dist/summary/runningSummarizer.js.map +1 -1
  81. package/dist/summary/summarizer.d.ts +1 -0
  82. package/dist/summary/summarizer.d.ts.map +1 -1
  83. package/dist/summary/summarizer.js +17 -8
  84. package/dist/summary/summarizer.js.map +1 -1
  85. package/dist/summary/summarizerClientElection.js +6 -6
  86. package/dist/summary/summarizerClientElection.js.map +1 -1
  87. package/dist/summary/summarizerHeuristics.js +9 -9
  88. package/dist/summary/summarizerHeuristics.js.map +1 -1
  89. package/dist/summary/summarizerNode/summarizerNode.js +7 -7
  90. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  91. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +1 -1
  92. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  93. package/dist/summary/summarizerNode/summarizerNodeUtils.js +3 -3
  94. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  95. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +2 -2
  96. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  97. package/dist/summary/summarizerTypes.d.ts +12 -12
  98. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  99. package/dist/summary/summaryCollection.d.ts +2 -2
  100. package/dist/summary/summaryCollection.d.ts.map +1 -1
  101. package/dist/summary/summaryCollection.js +22 -21
  102. package/dist/summary/summaryCollection.js.map +1 -1
  103. package/dist/summary/summaryFormat.d.ts +5 -5
  104. package/dist/summary/summaryFormat.d.ts.map +1 -1
  105. package/dist/summary/summaryGenerator.d.ts +3 -3
  106. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  107. package/dist/summary/summaryGenerator.js.map +1 -1
  108. package/dist/summary/summaryManager.d.ts +2 -2
  109. package/dist/summary/summaryManager.d.ts.map +1 -1
  110. package/dist/summary/summaryManager.js +10 -10
  111. package/dist/summary/summaryManager.js.map +1 -1
  112. package/dist/throttler.js +16 -16
  113. package/dist/throttler.js.map +1 -1
  114. package/dist/tsdoc-metadata.json +1 -1
  115. package/lib/blobManager.d.ts +1 -1
  116. package/lib/blobManager.d.ts.map +1 -1
  117. package/lib/blobManager.js +7 -7
  118. package/lib/blobManager.js.map +1 -1
  119. package/lib/connectionTelemetry.d.ts.map +1 -1
  120. package/lib/connectionTelemetry.js +76 -43
  121. package/lib/connectionTelemetry.js.map +1 -1
  122. package/lib/containerHandleContext.js +3 -3
  123. package/lib/containerHandleContext.js.map +1 -1
  124. package/lib/containerRuntime.d.ts +28 -24
  125. package/lib/containerRuntime.d.ts.map +1 -1
  126. package/lib/containerRuntime.js +268 -252
  127. package/lib/containerRuntime.js.map +1 -1
  128. package/lib/dataStore.js +9 -9
  129. package/lib/dataStore.js.map +1 -1
  130. package/lib/dataStoreContext.d.ts +1 -3
  131. package/lib/dataStoreContext.d.ts.map +1 -1
  132. package/lib/dataStoreContext.js +54 -58
  133. package/lib/dataStoreContext.js.map +1 -1
  134. package/lib/dataStoreRegistry.js +3 -3
  135. package/lib/dataStoreRegistry.js.map +1 -1
  136. package/lib/deltaManagerProxyBase.js +4 -4
  137. package/lib/deltaManagerProxyBase.js.map +1 -1
  138. package/lib/deltaManagerSummarizerProxy.js +6 -6
  139. package/lib/deltaManagerSummarizerProxy.js.map +1 -1
  140. package/lib/deltaScheduler.js.map +1 -1
  141. package/lib/error.d.ts.map +1 -1
  142. package/lib/error.js.map +1 -1
  143. package/lib/gc/garbageCollection.js +13 -13
  144. package/lib/gc/garbageCollection.js.map +1 -1
  145. package/lib/gc/gcDefinitions.d.ts +4 -15
  146. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  147. package/lib/gc/gcDefinitions.js.map +1 -1
  148. package/lib/gc/gcTelemetry.d.ts +1 -1
  149. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  150. package/lib/gc/gcUnreferencedStateTracker.js +3 -3
  151. package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
  152. package/lib/id-compressor/appendOnlySortedMap.js.map +1 -1
  153. package/lib/id-compressor/idCompressor.js.map +1 -1
  154. package/lib/id-compressor/identifiers.d.ts +3 -3
  155. package/lib/id-compressor/identifiers.d.ts.map +1 -1
  156. package/lib/index.d.ts +1 -1
  157. package/lib/index.d.ts.map +1 -1
  158. package/lib/index.js +1 -1
  159. package/lib/index.js.map +1 -1
  160. package/lib/messageTypes.d.ts +17 -17
  161. package/lib/messageTypes.d.ts.map +1 -1
  162. package/lib/opLifecycle/batchManager.js +6 -6
  163. package/lib/opLifecycle/batchManager.js.map +1 -1
  164. package/lib/opLifecycle/definitions.d.ts +2 -2
  165. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  166. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  167. package/lib/opLifecycle/outbox.js +7 -2
  168. package/lib/opLifecycle/outbox.js.map +1 -1
  169. package/lib/packageVersion.d.ts +1 -1
  170. package/lib/packageVersion.js +1 -1
  171. package/lib/packageVersion.js.map +1 -1
  172. package/lib/pendingStateManager.d.ts +3 -19
  173. package/lib/pendingStateManager.d.ts.map +1 -1
  174. package/lib/pendingStateManager.js +23 -40
  175. package/lib/pendingStateManager.js.map +1 -1
  176. package/lib/scheduleManager.js +6 -2
  177. package/lib/scheduleManager.js.map +1 -1
  178. package/lib/summary/orderedClientElection.d.ts +3 -3
  179. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  180. package/lib/summary/orderedClientElection.js +54 -54
  181. package/lib/summary/orderedClientElection.js.map +1 -1
  182. package/lib/summary/runWhileConnectedCoordinator.js +6 -6
  183. package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
  184. package/lib/summary/runningSummarizer.js +37 -37
  185. package/lib/summary/runningSummarizer.js.map +1 -1
  186. package/lib/summary/summarizer.d.ts +1 -0
  187. package/lib/summary/summarizer.d.ts.map +1 -1
  188. package/lib/summary/summarizer.js +18 -9
  189. package/lib/summary/summarizer.js.map +1 -1
  190. package/lib/summary/summarizerClientElection.js +6 -6
  191. package/lib/summary/summarizerClientElection.js.map +1 -1
  192. package/lib/summary/summarizerHeuristics.js +9 -9
  193. package/lib/summary/summarizerHeuristics.js.map +1 -1
  194. package/lib/summary/summarizerNode/summarizerNode.js +7 -7
  195. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  196. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +1 -1
  197. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  198. package/lib/summary/summarizerNode/summarizerNodeUtils.js +3 -3
  199. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  200. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +2 -2
  201. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  202. package/lib/summary/summarizerTypes.d.ts +12 -12
  203. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  204. package/lib/summary/summaryCollection.d.ts +2 -2
  205. package/lib/summary/summaryCollection.d.ts.map +1 -1
  206. package/lib/summary/summaryCollection.js +22 -21
  207. package/lib/summary/summaryCollection.js.map +1 -1
  208. package/lib/summary/summaryFormat.d.ts +5 -5
  209. package/lib/summary/summaryFormat.d.ts.map +1 -1
  210. package/lib/summary/summaryGenerator.d.ts +3 -3
  211. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  212. package/lib/summary/summaryGenerator.js.map +1 -1
  213. package/lib/summary/summaryManager.d.ts +2 -2
  214. package/lib/summary/summaryManager.d.ts.map +1 -1
  215. package/lib/summary/summaryManager.js +9 -9
  216. package/lib/summary/summaryManager.js.map +1 -1
  217. package/lib/throttler.js +16 -16
  218. package/lib/throttler.js.map +1 -1
  219. package/package.json +27 -27
  220. package/src/blobManager.ts +1 -1
  221. package/src/connectionTelemetry.ts +97 -52
  222. package/src/containerRuntime.ts +96 -73
  223. package/src/dataStore.ts +1 -1
  224. package/src/dataStoreContext.ts +1 -6
  225. package/src/error.ts +4 -1
  226. package/src/gc/gcDefinitions.ts +3 -15
  227. package/src/gc/gcEarlyAdoption.md +1 -1
  228. package/src/index.ts +1 -0
  229. package/src/opLifecycle/README.md +53 -28
  230. package/src/opLifecycle/outbox.ts +3 -0
  231. package/src/packageVersion.ts +1 -1
  232. package/src/pendingStateManager.ts +8 -46
  233. package/src/scheduleManager.ts +2 -0
  234. package/src/summary/summarizer.ts +20 -7
  235. package/src/summary/summaryCollection.ts +1 -0
  236. package/src/summary/summaryGenerator.ts +3 -3
  237. package/src/summary/summaryManager.ts +2 -2
@@ -4,8 +4,11 @@
4
4
  */
5
5
 
6
6
  import {
7
+ IEventSampler,
8
+ ISampledTelemetryLogger,
7
9
  ITelemetryLoggerExt,
8
10
  createChildLogger,
11
+ createSampledLogger,
9
12
  formatTick,
10
13
  } from "@fluidframework/telemetry-utils";
11
14
  import { IDeltaManager } from "@fluidframework/container-definitions";
@@ -53,7 +56,6 @@ interface IOpPerfTimings {
53
56
  }
54
57
 
55
58
  class OpPerfTelemetry {
56
- private pongCount: number = 0;
57
59
  private pingLatency: number | undefined;
58
60
 
59
61
  // Collab window tracking. This is timestamp of %1000 message.
@@ -61,11 +63,14 @@ class OpPerfTelemetry {
61
63
  private msnTrackingTimestamp: number = 0;
62
64
  // To track round trip time for every %500 client message.
63
65
  private clientSequenceNumberForLatencyStatistics: number | undefined;
64
-
65
- private opProcessingTimes: Partial<IOpPerfTimings> = {};
66
-
67
66
  // Performance Data to be reported for ops round trips and processing.
68
- private opPerfData: Partial<IOpPerfTelemetryProperties> = {};
67
+ private readonly latencyStatistics = new Map<
68
+ number,
69
+ {
70
+ opProcessingTimes: Partial<IOpPerfTimings>;
71
+ opPerfData: Partial<IOpPerfTelemetryProperties>;
72
+ }
73
+ >();
69
74
 
70
75
  private firstConnection = true;
71
76
  private connectionOpSeqNumber: number | undefined;
@@ -75,6 +80,12 @@ class OpPerfTelemetry {
75
80
 
76
81
  private readonly logger: ITelemetryLoggerExt;
77
82
 
83
+ private static readonly OP_LATENCY_SAMPLE_RATE = 500;
84
+ private readonly opLatencyLogger: ISampledTelemetryLogger;
85
+
86
+ private static readonly DELTA_LATENCY_SAMPLE_RATE = 100;
87
+ private readonly deltaLatencyLogger: ISampledTelemetryLogger;
88
+
78
89
  public constructor(
79
90
  private clientId: string | undefined,
80
91
  private readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,
@@ -82,6 +93,28 @@ class OpPerfTelemetry {
82
93
  ) {
83
94
  this.logger = createChildLogger({ logger, namespace: "OpPerf" });
84
95
 
96
+ const deltaLatencyEventSampler: IEventSampler = (() => {
97
+ let eventCount = -1;
98
+ return {
99
+ sample: () => {
100
+ eventCount++;
101
+ const shouldSample =
102
+ eventCount % OpPerfTelemetry.DELTA_LATENCY_SAMPLE_RATE === 0;
103
+ if (shouldSample) {
104
+ eventCount = 0;
105
+ }
106
+ return shouldSample;
107
+ },
108
+ };
109
+ })();
110
+
111
+ this.deltaLatencyLogger = createSampledLogger(logger, deltaLatencyEventSampler);
112
+
113
+ // The SampledLogger here is used get access to the isSamplingDisabled property dervied from
114
+ // telemetry config properties. The actual sampling logic for op messages happens outside this SampledLogger
115
+ // due to complexity of the different asynchronus scenarios of the op message lifecycle.
116
+ this.opLatencyLogger = createSampledLogger(logger);
117
+
85
118
  this.deltaManager.on("pong", (latency) => this.recordPingTime(latency));
86
119
  this.deltaManager.on("submitOp", (message) => this.beforeOpSubmit(message));
87
120
 
@@ -103,42 +136,47 @@ class OpPerfTelemetry {
103
136
  this.deltaManager.on("disconnect", () => {
104
137
  this.sequenceNumberForMsnTracking = undefined;
105
138
  this.clientSequenceNumberForLatencyStatistics = undefined;
106
- this.opProcessingTimes = {};
107
- this.opPerfData = {};
108
139
  this.connectionOpSeqNumber = undefined;
109
140
  this.firstConnection = false;
110
- this.pongCount = 0;
141
+ this.latencyStatistics.clear();
111
142
  });
112
143
 
113
144
  this.deltaManager.outbound.on("push", (messages) => {
114
145
  for (const msg of messages) {
115
146
  if (
116
147
  msg.type === MessageType.Operation &&
117
- this.clientSequenceNumberForLatencyStatistics === msg.clientSequenceNumber
148
+ (this.opLatencyLogger.isSamplingDisabled ||
149
+ this.clientSequenceNumberForLatencyStatistics === msg.clientSequenceNumber)
118
150
  ) {
151
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
152
+ const latencyStats = this.latencyStatistics.get(msg.clientSequenceNumber)!;
153
+ assert(
154
+ latencyStats !== undefined,
155
+ 0x7c2 /* Latency stats for op should exist */,
156
+ );
119
157
  assert(
120
- this.opProcessingTimes.outboundPushEventTime === undefined,
158
+ latencyStats.opProcessingTimes.outboundPushEventTime === undefined,
121
159
  0x2c8 /* "outboundPushEventTime should be undefined" */,
122
160
  );
123
161
  assert(
124
- this.opPerfData.durationNetwork === undefined,
162
+ latencyStats.opPerfData.durationNetwork === undefined,
125
163
  0x2c9 /* "durationNetwork should be undefined" */,
126
164
  );
127
- this.opProcessingTimes.outboundPushEventTime = Date.now();
165
+ latencyStats.opProcessingTimes.outboundPushEventTime = Date.now();
128
166
 
129
167
  assert(
130
- this.opPerfData.durationOutboundBatching === undefined,
168
+ latencyStats.opPerfData.durationOutboundBatching === undefined,
131
169
  0x2ca /* "durationOutboundBatching should be undefined" */,
132
170
  );
133
171
 
134
172
  assert(
135
- this.opProcessingTimes.submitOpEventTime !== undefined,
173
+ latencyStats.opProcessingTimes.submitOpEventTime !== undefined,
136
174
  0x2cb /* "submitOpEventTime should be undefined" */,
137
175
  );
138
176
 
139
- this.opPerfData.durationOutboundBatching =
140
- this.opProcessingTimes.outboundPushEventTime -
141
- this.opProcessingTimes.submitOpEventTime;
177
+ latencyStats.opPerfData.durationOutboundBatching =
178
+ latencyStats.opProcessingTimes.outboundPushEventTime -
179
+ latencyStats.opProcessingTimes.submitOpEventTime;
142
180
  }
143
181
  }
144
182
  });
@@ -147,15 +185,20 @@ class OpPerfTelemetry {
147
185
  if (
148
186
  this.clientId === message.clientId &&
149
187
  message.type === MessageType.Operation &&
150
- this.clientSequenceNumberForLatencyStatistics === message.clientSequenceNumber &&
151
- this.opProcessingTimes.outboundPushEventTime !== undefined
188
+ (this.opLatencyLogger.isSamplingDisabled ||
189
+ this.clientSequenceNumberForLatencyStatistics === message.clientSequenceNumber)
152
190
  ) {
153
- this.opProcessingTimes.inboundPushEventTime = Date.now();
154
- this.opPerfData.durationNetwork =
155
- this.opProcessingTimes.inboundPushEventTime -
156
- this.opProcessingTimes.outboundPushEventTime;
157
- this.opProcessingTimes.outboundPushEventTime = undefined;
158
- this.opPerfData.lengthInboundQueue = this.deltaManager.inbound.length;
191
+ // We do an explicit check for undefined right after this
192
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
193
+ const latencyStats = this.latencyStatistics.get(message.clientSequenceNumber)!;
194
+ assert(latencyStats !== undefined, 0x7c3 /* Latency stats for op should exist */);
195
+ if (latencyStats.opProcessingTimes.outboundPushEventTime !== undefined) {
196
+ latencyStats.opProcessingTimes.inboundPushEventTime = Date.now();
197
+ latencyStats.opPerfData.durationNetwork =
198
+ latencyStats.opProcessingTimes.inboundPushEventTime -
199
+ latencyStats.opProcessingTimes.outboundPushEventTime;
200
+ latencyStats.opPerfData.lengthInboundQueue = this.deltaManager.inbound.length;
201
+ }
159
202
  }
160
203
  });
161
204
 
@@ -201,32 +244,33 @@ class OpPerfTelemetry {
201
244
  });
202
245
  }
203
246
 
204
- // logging one in every 100 pongs, including the first time, if it is a "write" client.
205
- if (this.pongCount % 100 === 0 && this.deltaManager.active) {
206
- this.logger.sendPerformanceEvent({
247
+ // logging one in every DELTA_LATENCY_SAMPLE_RATE pongs, including the first time, if it is a "write" client.
248
+ if (this.deltaManager.active) {
249
+ this.deltaLatencyLogger.sendPerformanceEvent({
207
250
  eventName: "DeltaLatency",
208
251
  duration: latency,
209
252
  });
210
253
  }
211
- this.pongCount++;
212
254
  }
213
255
 
214
256
  private beforeOpSubmit(message: IDocumentMessage) {
215
257
  // start with first client op and measure latency every 500 client ops
216
258
  if (
217
- this.clientSequenceNumberForLatencyStatistics === undefined &&
218
- message.clientSequenceNumber % 500 === 1
259
+ this.opLatencyLogger.isSamplingDisabled ||
260
+ (this.clientSequenceNumberForLatencyStatistics === undefined &&
261
+ message.clientSequenceNumber % OpPerfTelemetry.OP_LATENCY_SAMPLE_RATE === 1)
219
262
  ) {
220
263
  assert(
221
- this.opProcessingTimes.outboundPushEventTime === undefined,
222
- 0x2cc /* "OpTimeSittingInboundQueue should be undefined" */,
223
- );
224
- assert(
225
- this.opPerfData.durationNetwork === undefined,
226
- 0x2cd /* "durationNetwork should be undefined" */,
264
+ this.latencyStatistics.get(message.clientSequenceNumber) === undefined,
265
+ 0x7c4 /* Existing op perf data for client sequence number */,
227
266
  );
228
- this.opProcessingTimes.submitOpEventTime = Date.now();
229
267
  this.clientSequenceNumberForLatencyStatistics = message.clientSequenceNumber;
268
+ this.latencyStatistics.set(message.clientSequenceNumber, {
269
+ opProcessingTimes: {
270
+ submitOpEventTime: Date.now(),
271
+ },
272
+ opPerfData: {},
273
+ });
230
274
  }
231
275
  }
232
276
 
@@ -261,20 +305,23 @@ class OpPerfTelemetry {
261
305
 
262
306
  if (
263
307
  this.clientId === message.clientId &&
264
- this.clientSequenceNumberForLatencyStatistics === message.clientSequenceNumber
308
+ (this.opLatencyLogger.isSamplingDisabled ||
309
+ this.clientSequenceNumberForLatencyStatistics === message.clientSequenceNumber)
265
310
  ) {
311
+ // We do an explicit check for undefined right after this
312
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
313
+ const latencyData = this.latencyStatistics.get(message.clientSequenceNumber)!;
314
+ assert(latencyData !== undefined, 0x7c5 /* Undefined latency statistics for op */);
266
315
  assert(
267
- this.opProcessingTimes.submitOpEventTime !== undefined,
268
- 0x120 /* "Undefined latency statistics (op send time)" */,
316
+ latencyData.opProcessingTimes.submitOpEventTime !== undefined,
317
+ 0x120 /* "Undefined latency statistics for op (op send time)" */,
269
318
  );
270
319
  const currentTime = Date.now();
271
-
272
- if (this.opProcessingTimes.inboundPushEventTime !== undefined) {
273
- this.opPerfData.durationInboundToProcessing =
274
- currentTime - this.opProcessingTimes.inboundPushEventTime;
320
+ if (latencyData.opProcessingTimes.inboundPushEventTime !== undefined) {
321
+ latencyData.opPerfData.durationInboundToProcessing =
322
+ currentTime - latencyData.opProcessingTimes.inboundPushEventTime;
275
323
  }
276
-
277
- const duration = currentTime - this.opProcessingTimes.submitOpEventTime;
324
+ const duration = currentTime - latencyData.opProcessingTimes.submitOpEventTime;
278
325
 
279
326
  // One of the core expectations for Fluid service is to be fast.
280
327
  // When it's not the case, we want to learn about it and be able to investigate, so
@@ -284,8 +331,7 @@ class OpPerfTelemetry {
284
331
  // The threshold could be adjusted, but ideally it stays workload-agnostic, as service
285
332
  // performance impacts all workloads relying on service.
286
333
  const category = duration > latencyThreshold ? "error" : "performance";
287
-
288
- this.logger.sendPerformanceEvent({
334
+ this.opLatencyLogger.sendPerformanceEvent({
289
335
  eventName: "OpRoundtripTime",
290
336
  sequenceNumber,
291
337
  referenceSequenceNumber: message.referenceSequenceNumber,
@@ -294,11 +340,10 @@ class OpPerfTelemetry {
294
340
  pingLatency: this.pingLatency,
295
341
  msnDistance:
296
342
  this.deltaManager.lastSequenceNumber - this.deltaManager.minimumSequenceNumber,
297
- ...this.opPerfData,
343
+ ...latencyData.opPerfData,
298
344
  });
299
345
  this.clientSequenceNumberForLatencyStatistics = undefined;
300
- this.opPerfData = {};
301
- this.opProcessingTimes = {};
346
+ this.latencyStatistics.delete(message.clientSequenceNumber);
302
347
  }
303
348
  }
304
349
  }
@@ -8,9 +8,11 @@ import {
8
8
  FluidObject,
9
9
  IFluidHandle,
10
10
  IFluidHandleContext,
11
+ // eslint-disable-next-line import/no-deprecated
11
12
  IFluidRouter,
12
13
  IRequest,
13
14
  IResponse,
15
+ IProvideFluidHandleContext,
14
16
  } from "@fluidframework/core-interfaces";
15
17
  import {
16
18
  IAudience,
@@ -21,6 +23,7 @@ import {
21
23
  ICriticalContainerError,
22
24
  AttachState,
23
25
  ILoaderOptions,
26
+ ILoader,
24
27
  LoaderHeader,
25
28
  } from "@fluidframework/container-definitions";
26
29
  import {
@@ -97,11 +100,11 @@ import {
97
100
  create404Response,
98
101
  exceptionToResponse,
99
102
  GCDataBuilder,
100
- requestFluidObject,
101
103
  seqFromTree,
102
104
  calculateStats,
103
105
  TelemetryContext,
104
106
  ReadAndParseBlob,
107
+ responseToException,
105
108
  } from "@fluidframework/runtime-utils";
106
109
  import { v4 as uuid } from "uuid";
107
110
  import { ContainerFluidHandleContext } from "./containerHandleContext";
@@ -141,7 +144,6 @@ import {
141
144
  IConnectableRuntime,
142
145
  IGeneratedSummaryStats,
143
146
  ISubmitSummaryOptions,
144
- ISummarizer,
145
147
  ISummarizerInternalsProvider,
146
148
  ISummarizerRuntime,
147
149
  IRefreshSummaryAckOptions,
@@ -154,6 +156,7 @@ import {
154
156
  EnqueueSummarizeResult,
155
157
  ISummarizerEvents,
156
158
  IBaseSummarizeResult,
159
+ ISummarizer,
157
160
  } from "./summary";
158
161
  import { formExponentialFn, Throttler } from "./throttler";
159
162
  import {
@@ -352,16 +355,17 @@ export interface ISummaryRuntimeOptions {
352
355
 
353
356
  /**
354
357
  * Options for op compression.
355
- * @experimental - Not ready for use
356
358
  */
357
359
  export interface ICompressionRuntimeOptions {
358
360
  /**
359
- * The minimum size the batch's payload must exceed before the batch's contents will be compressed.
361
+ * The value the batch's content size must exceed for the batch to be compressed.
362
+ * By default the value is 600 * 1024 = 614400 bytes. If the value is set to `Infinity`, compression will be disabled.
360
363
  */
361
364
  readonly minimumBatchSizeInBytes: number;
362
365
 
363
366
  /**
364
367
  * The compression algorithm that will be used to compress the op.
368
+ * By default the value is `lz4` which is the only compression algorithm currently supported.
365
369
  */
366
370
  readonly compressionAlgorithm: CompressionAlgorithms;
367
371
  }
@@ -389,8 +393,7 @@ export interface IContainerRuntimeOptions {
389
393
  */
390
394
  readonly flushMode?: FlushMode;
391
395
  /**
392
- * Enables the runtime to compress ops. Compression is disabled when undefined.
393
- * @experimental Not ready for use.
396
+ * Enables the runtime to compress ops. See {@link ICompressionRuntimeOptions}.
394
397
  */
395
398
  readonly compressionOptions?: ICompressionRuntimeOptions;
396
399
  /**
@@ -407,12 +410,15 @@ export interface IContainerRuntimeOptions {
407
410
  /**
408
411
  * If the op payload needs to be chunked in order to work around the maximum size of the batch, this value represents
409
412
  * how large the individual chunks will be. This is only supported when compression is enabled. If after compression, the
410
- * batch size exceeds this value, it will be chunked into smaller ops of this size.
413
+ * batch content size exceeds this value, it will be chunked into smaller ops of this exact size.
411
414
  *
412
- * If unspecified, if a batch exceeds `maxBatchSizeInBytes` after compression, the container will close with an instance
413
- * of `GenericError` with the `BatchTooLarge` message.
415
+ * This value is a trade-off between having many small chunks vs fewer larger chunks and by default, the runtime is configured to use
416
+ * 200 * 1024 = 204800 bytes. This default value ensures that no compressed payload's content is able to exceed {@link IContainerRuntimeOptions.maxBatchSizeInBytes}
417
+ * regardless of the overhead of an individual op.
414
418
  *
415
- * @experimental Not ready for use.
419
+ * Any value of `chunkSizeInBytes` exceeding {@link IContainerRuntimeOptions.maxBatchSizeInBytes} will disable this feature, therefore if a compressed batch's content
420
+ * size exceeds {@link IContainerRuntimeOptions.maxBatchSizeInBytes} after compression, the container will close with an instance of `GenericError` with
421
+ * the `BatchTooLarge` message.
416
422
  */
417
423
  readonly chunkSizeInBytes?: number;
418
424
 
@@ -626,13 +632,65 @@ type MessageWithContext =
626
632
  local: boolean;
627
633
  };
628
634
 
635
+ const summarizerRequestUrl = "_summarizer";
636
+
637
+ /**
638
+ * Create and retrieve the summmarizer
639
+ */
640
+ async function createSummarizer(loader: ILoader, url: string): Promise<ISummarizer> {
641
+ const request: IRequest = {
642
+ headers: {
643
+ [LoaderHeader.cache]: false,
644
+ [LoaderHeader.clientDetails]: {
645
+ capabilities: { interactive: false },
646
+ type: summarizerClientType,
647
+ },
648
+ [DriverHeader.summarizingClient]: true,
649
+ [LoaderHeader.reconnect]: false,
650
+ },
651
+ url,
652
+ };
653
+
654
+ const resolvedContainer = await loader.resolve(request);
655
+ let fluidObject: FluidObject<ISummarizer> | undefined;
656
+
657
+ // Older containers may not have the "getEntryPoint" API
658
+ // ! This check will need to stay until LTS of loader moves past 2.0.0-internal.7.0.0
659
+ if (resolvedContainer.getEntryPoint !== undefined) {
660
+ fluidObject = await resolvedContainer.getEntryPoint();
661
+ } else {
662
+ const response = await resolvedContainer.request({ url: `/${summarizerRequestUrl}` });
663
+ if (response.status !== 200 || response.mimeType !== "fluid/object") {
664
+ throw responseToException(response, request);
665
+ }
666
+ fluidObject = response.value;
667
+ }
668
+
669
+ if (fluidObject?.ISummarizer === undefined) {
670
+ throw new UsageError("Fluid object does not implement ISummarizer");
671
+ }
672
+ return fluidObject.ISummarizer;
673
+ }
674
+
675
+ /**
676
+ * This function is not supported publicly and exists for e2e testing
677
+ */
678
+ export async function TEST_requestSummarizer(loader: ILoader, url: string): Promise<ISummarizer> {
679
+ return createSummarizer(loader, url);
680
+ }
681
+
629
682
  /**
630
683
  * Represents the runtime of the container. Contains helper functions/state of the container.
631
684
  * It will define the store level mappings.
632
685
  */
633
686
  export class ContainerRuntime
634
687
  extends TypedEventEmitter<IContainerRuntimeEvents & ISummarizerEvents>
635
- implements IContainerRuntime, IRuntime, ISummarizerRuntime, ISummarizerInternalsProvider
688
+ implements
689
+ IContainerRuntime,
690
+ IRuntime,
691
+ ISummarizerRuntime,
692
+ ISummarizerInternalsProvider,
693
+ IProvideFluidHandleContext
636
694
  {
637
695
  /**
638
696
  * @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
@@ -669,10 +727,15 @@ export class ContainerRuntime
669
727
  context,
670
728
  registryEntries,
671
729
  existing: existingFlag,
672
- requestHandler,
673
730
  runtimeOptions,
674
731
  containerScope,
675
732
  containerRuntimeCtor,
733
+ requestHandler,
734
+ provideEntryPoint: () => {
735
+ throw new UsageError(
736
+ "ContainerRuntime.load is deprecated and should no longer be used",
737
+ );
738
+ },
676
739
  });
677
740
  }
678
741
 
@@ -688,7 +751,7 @@ export class ContainerRuntime
688
751
  * - containerScope - runtime services provided with context
689
752
  * - containerRuntimeCtor - Constructor to use to create the ContainerRuntime instance.
690
753
  * This allows mixin classes to leverage this method to define their own async initializer.
691
- * - initializeEntryPoint - Promise that resolves to an object which will act as entryPoint for the Container.
754
+ * - provideEntryPoint - Promise that resolves to an object which will act as entryPoint for the Container.
692
755
  * This object should provide all the functionality that the Container is expected to provide to the loader layer.
693
756
  */
694
757
  public static async loadRuntime(params: {
@@ -698,30 +761,21 @@ export class ContainerRuntime
698
761
  runtimeOptions?: IContainerRuntimeOptions;
699
762
  containerScope?: FluidObject;
700
763
  containerRuntimeCtor?: typeof ContainerRuntime;
764
+ /** @deprecated Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md */
701
765
  requestHandler?: (request: IRequest, runtime: IContainerRuntime) => Promise<IResponse>;
702
- initializeEntryPoint?: (containerRuntime: IContainerRuntime) => Promise<FluidObject>;
766
+ provideEntryPoint: (containerRuntime: IContainerRuntime) => Promise<FluidObject>;
703
767
  }): Promise<ContainerRuntime> {
704
768
  const {
705
769
  context,
706
770
  registryEntries,
707
771
  existing,
708
772
  requestHandler,
773
+ provideEntryPoint,
709
774
  runtimeOptions = {},
710
775
  containerScope = {},
711
776
  containerRuntimeCtor = ContainerRuntime,
712
777
  } = params;
713
778
 
714
- const initializeEntryPoint =
715
- params.initializeEntryPoint ??
716
- (async (containerRuntime: IContainerRuntime) => ({
717
- get IFluidRouter() {
718
- return this;
719
- },
720
- async request(req) {
721
- return containerRuntime.request(req);
722
- },
723
- }));
724
-
725
779
  // If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
726
780
  // back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
727
781
  const backCompatContext: IContainerContext | OldContainerContextWithLogger = context;
@@ -850,9 +904,9 @@ export class ContainerRuntime
850
904
  blobManagerSnapshot,
851
905
  context.storage,
852
906
  idCompressor,
907
+ provideEntryPoint,
853
908
  requestHandler,
854
909
  undefined, // summaryConfiguration
855
- initializeEntryPoint,
856
910
  );
857
911
 
858
912
  // Apply stashed ops with a reference sequence number equal to the sequence number of the snapshot,
@@ -878,17 +932,6 @@ export class ContainerRuntime
878
932
  return this._storage;
879
933
  }
880
934
 
881
- /** @deprecated - The functionality is no longer exposed publicly */
882
- public get reSubmitFn() {
883
- return (
884
- type: ContainerMessageType,
885
- contents: any,
886
- localOpMetadata: unknown,
887
- opMetadata: Record<string, unknown> | undefined,
888
- ) => this.reSubmitCore({ type, contents }, localOpMetadata, opMetadata);
889
- // Note: compatDetails is not included in this deprecated API
890
- }
891
-
892
935
  private readonly submitFn: (
893
936
  type: MessageType,
894
937
  contents: any,
@@ -1140,6 +1183,7 @@ export class ContainerRuntime
1140
1183
  blobManagerSnapshot: IBlobManagerLoadInfo,
1141
1184
  private readonly _storage: IDocumentStorageService,
1142
1185
  idCompressor: (IIdCompressor & IIdCompressorCore) | undefined,
1186
+ provideEntryPoint: (containerRuntime: IContainerRuntime) => Promise<FluidObject>,
1143
1187
  private readonly requestHandler?: (
1144
1188
  request: IRequest,
1145
1189
  runtime: IContainerRuntime,
@@ -1150,7 +1194,6 @@ export class ContainerRuntime
1150
1194
  // the runtime configuration overrides
1151
1195
  ...runtimeOptions.summaryOptions?.summaryConfigOverrides,
1152
1196
  },
1153
- initializeEntryPoint?: (containerRuntime: IContainerRuntime) => Promise<FluidObject>,
1154
1197
  ) {
1155
1198
  super();
1156
1199
 
@@ -1584,7 +1627,7 @@ export class ContainerRuntime
1584
1627
  this, // IConnectedState
1585
1628
  this.summaryCollection,
1586
1629
  this.logger,
1587
- this.formRequestSummarizerFn(loader),
1630
+ this.formCreateSummarizerFn(loader),
1588
1631
  new Throttler(
1589
1632
  60 * 1000, // 60 sec delay window
1590
1633
  30 * 1000, // 30 sec max delay
@@ -1642,7 +1685,7 @@ export class ContainerRuntime
1642
1685
  );
1643
1686
  return this._summarizer;
1644
1687
  }
1645
- return initializeEntryPoint?.(this);
1688
+ return provideEntryPoint(this);
1646
1689
  });
1647
1690
  }
1648
1691
 
@@ -1690,7 +1733,7 @@ export class ContainerRuntime
1690
1733
  const parser = RequestParser.create(request);
1691
1734
  const id = parser.pathParts[0];
1692
1735
 
1693
- if (id === "_summarizer" && parser.pathParts.length === 1) {
1736
+ if (id === summarizerRequestUrl && parser.pathParts.length === 1) {
1694
1737
  if (this._summarizer !== undefined) {
1695
1738
  return {
1696
1739
  status: 200,
@@ -1701,6 +1744,7 @@ export class ContainerRuntime
1701
1744
  return create404Response(request);
1702
1745
  }
1703
1746
  if (this.requestHandler !== undefined) {
1747
+ // eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
1704
1748
  return this.requestHandler(parser, this);
1705
1749
  }
1706
1750
 
@@ -1720,6 +1764,7 @@ export class ContainerRuntime
1720
1764
  const id = requestParser.pathParts[0];
1721
1765
 
1722
1766
  if (id === "_channels") {
1767
+ // eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
1723
1768
  return this.resolveHandle(requestParser.createSubRequest(1));
1724
1769
  }
1725
1770
 
@@ -1741,6 +1786,7 @@ export class ContainerRuntime
1741
1786
  subRequest.url.startsWith("/"),
1742
1787
  0x126 /* "Expected createSubRequest url to include a leading slash" */,
1743
1788
  );
1789
+ // eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
1744
1790
  return dataStore.request(subRequest);
1745
1791
  }
1746
1792
 
@@ -1753,10 +1799,10 @@ export class ContainerRuntime
1753
1799
  /**
1754
1800
  * {@inheritDoc @fluidframework/container-definitions#IRuntime.getEntryPoint}
1755
1801
  */
1756
- public async getEntryPoint?(): Promise<FluidObject | undefined> {
1802
+ public async getEntryPoint(): Promise<FluidObject> {
1757
1803
  return this.entryPoint;
1758
1804
  }
1759
- private readonly entryPoint: LazyPromise<FluidObject | undefined>;
1805
+ private readonly entryPoint: LazyPromise<FluidObject>;
1760
1806
 
1761
1807
  private internalId(maybeAlias: string): string {
1762
1808
  return this.dataStores.aliases.get(maybeAlias) ?? maybeAlias;
@@ -2338,6 +2384,7 @@ export class ContainerRuntime
2338
2384
  * @param wait - True if you want to wait for it.
2339
2385
  * @deprecated - Use getAliasedDataStoreEntryPoint instead to get an aliased data store's entry point.
2340
2386
  */
2387
+ // eslint-disable-next-line import/no-deprecated
2341
2388
  public async getRootDataStore(id: string, wait = true): Promise<IFluidRouter> {
2342
2389
  return this.getRootDataStoreChannel(id, wait);
2343
2390
  }
@@ -2526,7 +2573,7 @@ export class ContainerRuntime
2526
2573
  return false;
2527
2574
  }
2528
2575
  } else if (type === ContainerMessageType.FluidDataStoreOp) {
2529
- const envelope = contents as IEnvelope;
2576
+ const envelope = contents;
2530
2577
  if (envelope.address === agentSchedulerId) {
2531
2578
  return false;
2532
2579
  }
@@ -3664,7 +3711,7 @@ export class ContainerRuntime
3664
3711
  case ContainerMessageType.FluidDataStoreOp:
3665
3712
  // For operations, call rollbackDataStoreOp which will find the right store
3666
3713
  // and trigger rollback on it.
3667
- this.dataStores.rollbackDataStoreOp(contents as IEnvelope, localOpMetadata);
3714
+ this.dataStores.rollbackDataStoreOp(contents, localOpMetadata);
3668
3715
  break;
3669
3716
  default:
3670
3717
  // Don't check message.compatDetails because this is for rolling back a local op so the type will be known
@@ -3911,35 +3958,11 @@ export class ContainerRuntime
3911
3958
  }
3912
3959
 
3913
3960
  /**
3914
- * * Forms a function that will request a Summarizer.
3915
- * @param loaderRouter - the loader acting as an IFluidRouter
3916
- * */
3917
- private formRequestSummarizerFn(loaderRouter: IFluidRouter) {
3961
+ * Forms a function that will create and retrieve a Summarizer.
3962
+ */
3963
+ private formCreateSummarizerFn(loader: ILoader) {
3918
3964
  return async () => {
3919
- const request: IRequest = {
3920
- headers: {
3921
- [LoaderHeader.cache]: false,
3922
- [LoaderHeader.clientDetails]: {
3923
- capabilities: { interactive: false },
3924
- type: summarizerClientType,
3925
- },
3926
- [DriverHeader.summarizingClient]: true,
3927
- [LoaderHeader.reconnect]: false,
3928
- },
3929
- url: "/_summarizer",
3930
- };
3931
-
3932
- const fluidObject = await requestFluidObject<FluidObject<ISummarizer>>(
3933
- loaderRouter,
3934
- request,
3935
- );
3936
- const summarizer = fluidObject.ISummarizer;
3937
-
3938
- if (!summarizer) {
3939
- throw new UsageError("Fluid object does not implement ISummarizer");
3940
- }
3941
-
3942
- return summarizer;
3965
+ return createSummarizer(loader, `/${summarizerRequestUrl}`);
3943
3966
  };
3944
3967
  }
3945
3968
 
package/src/dataStore.ts CHANGED
@@ -170,7 +170,7 @@ class DataStore implements IDataStore {
170
170
  /**
171
171
  * {@inheritDoc @fluidframework/runtime-definitions#IDataStore.entryPoint}
172
172
  */
173
- get entryPoint(): IFluidHandle<FluidObject> | undefined {
173
+ get entryPoint(): IFluidHandle<FluidObject> {
174
174
  return this.fluidDataStoreChannel.entryPoint;
175
175
  }
176
176