@fluidframework/container-runtime 0.58.2001 → 0.59.1000-61898
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/dist/blobManager.d.ts +15 -2
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +65 -9
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +63 -23
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +39 -7
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +161 -29
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.js +8 -1
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +9 -3
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +22 -6
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +13 -5
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +39 -18
- package/dist/dataStores.js.map +1 -1
- package/dist/deltaScheduler.d.ts +4 -5
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js +54 -35
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/garbageCollection.d.ts +31 -27
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +76 -75
- package/dist/garbageCollection.js.map +1 -1
- package/dist/opTelemetry.d.ts +22 -0
- package/dist/opTelemetry.d.ts.map +1 -0
- package/dist/opTelemetry.js +59 -0
- package/dist/opTelemetry.js.map +1 -0
- package/dist/orderedClientElection.d.ts +57 -6
- package/dist/orderedClientElection.d.ts.map +1 -1
- package/dist/orderedClientElection.js +140 -25
- package/dist/orderedClientElection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/summarizerClientElection.d.ts +2 -0
- package/dist/summarizerClientElection.d.ts.map +1 -1
- package/dist/summarizerClientElection.js +7 -2
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summarizerTypes.d.ts +9 -0
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +1 -1
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +14 -3
- package/dist/summaryManager.js.map +1 -1
- package/lib/blobManager.d.ts +15 -2
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +66 -10
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +63 -23
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +39 -7
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +163 -31
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.js +8 -1
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +9 -3
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +22 -6
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +13 -5
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +39 -18
- package/lib/dataStores.js.map +1 -1
- package/lib/deltaScheduler.d.ts +4 -5
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +54 -35
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/garbageCollection.d.ts +31 -27
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +75 -74
- package/lib/garbageCollection.js.map +1 -1
- package/lib/opTelemetry.d.ts +22 -0
- package/lib/opTelemetry.d.ts.map +1 -0
- package/lib/opTelemetry.js +55 -0
- package/lib/opTelemetry.js.map +1 -0
- package/lib/orderedClientElection.d.ts +57 -6
- package/lib/orderedClientElection.d.ts.map +1 -1
- package/lib/orderedClientElection.js +140 -25
- package/lib/orderedClientElection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/summarizerClientElection.d.ts +2 -0
- package/lib/summarizerClientElection.d.ts.map +1 -1
- package/lib/summarizerClientElection.js +7 -2
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summarizerTypes.d.ts +9 -0
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +1 -1
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +14 -3
- package/lib/summaryManager.js.map +1 -1
- package/package.json +63 -19
- package/src/blobManager.ts +78 -11
- package/src/connectionTelemetry.ts +110 -19
- package/src/containerRuntime.ts +191 -36
- package/src/dataStore.ts +7 -1
- package/src/dataStoreContext.ts +22 -7
- package/src/dataStores.ts +40 -19
- package/src/deltaScheduler.ts +65 -39
- package/src/garbageCollection.ts +92 -78
- package/src/opTelemetry.ts +71 -0
- package/src/orderedClientElection.ts +154 -25
- package/src/packageVersion.ts +1 -1
- package/src/summarizerClientElection.ts +7 -2
- package/src/summarizerTypes.ts +9 -0
- package/src/summaryGenerator.ts +9 -1
- package/src/summaryManager.ts +15 -4
package/src/blobManager.ts
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
import { IFluidHandle, IFluidHandleContext } from "@fluidframework/core-interfaces";
|
|
7
7
|
import { IDocumentStorageService } from "@fluidframework/driver-definitions";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { generateHandleContextPath } from "@fluidframework/runtime-utils";
|
|
8
|
+
import { ISnapshotTree } from "@fluidframework/protocol-definitions";
|
|
9
|
+
import { generateHandleContextPath, SummaryTreeBuilder } from "@fluidframework/runtime-utils";
|
|
11
10
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
12
11
|
import { assert, Deferred } from "@fluidframework/common-utils";
|
|
13
12
|
import { IContainerRuntime } from "@fluidframework/container-runtime-definitions";
|
|
14
13
|
import { AttachState } from "@fluidframework/container-definitions";
|
|
14
|
+
import { IGarbageCollectionData, ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* This class represents blob (long string)
|
|
@@ -147,9 +147,12 @@ export class BlobManager {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
public processBlobAttachOp(blobId: string, local: boolean) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
if (local) {
|
|
151
|
+
const pendingBlobP = this.pendingBlobIds.get(blobId);
|
|
152
|
+
assert(pendingBlobP !== undefined, 0x1f8 /* "local BlobAttach op with no pending blob" */);
|
|
153
|
+
pendingBlobP.resolve();
|
|
154
|
+
this.pendingBlobIds.delete(blobId);
|
|
155
|
+
}
|
|
153
156
|
this.blobIds.add(blobId);
|
|
154
157
|
}
|
|
155
158
|
|
|
@@ -202,19 +205,83 @@ export class BlobManager {
|
|
|
202
205
|
});
|
|
203
206
|
}
|
|
204
207
|
|
|
205
|
-
|
|
208
|
+
/**
|
|
209
|
+
* Generates data used for garbage collection. Each blob uploaded represents a node in the GC graph as it can be
|
|
210
|
+
* individually referenced by storing its handle in a referenced DDS. Returns the list of blob ids as GC nodes.
|
|
211
|
+
* @param fullGC - true to bypass optimizations and force full generation of GC data. BlobManager doesn't care
|
|
212
|
+
* about this for now because the data is a simple list of blob ids.
|
|
213
|
+
*/
|
|
214
|
+
public getGCData(fullGC: boolean = false): IGarbageCollectionData {
|
|
215
|
+
const getGCNodePath = (blobId: string) => { return `/${BlobManager.basePath}/${blobId}`; };
|
|
216
|
+
const gcData: IGarbageCollectionData = { gcNodes: {} };
|
|
217
|
+
/**
|
|
218
|
+
* The node path is of the format `/_blobs/blobId`. This path must match the path of the blob handle returned
|
|
219
|
+
* by the createBlob API because blobs are marked referenced by storing these handles in a referenced DDS.
|
|
220
|
+
*/
|
|
221
|
+
this.blobIds.forEach((blobId: string) => {
|
|
222
|
+
gcData.gcNodes[getGCNodePath(blobId)] = [];
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* For all blobs in the redirect table, the handle returned on creation is based off of the localId. So, these
|
|
227
|
+
* nodes can be referenced by storing the localId handle. When that happens, the corresponding storageId node
|
|
228
|
+
* must also be marked referenced. So, we add a route from the localId node to the storageId node.
|
|
229
|
+
* Note that because of de-duping, there can be multiple localIds that all redirect to the same storageId or
|
|
230
|
+
* a blob may be referenced via its storageId handle.
|
|
231
|
+
*/
|
|
232
|
+
if (this.redirectTable !== undefined) {
|
|
233
|
+
for (const [localId, storageId] of this.redirectTable) {
|
|
234
|
+
// Add node for the localId and add a route to the storageId node. The storageId node will have been
|
|
235
|
+
// added above when adding nodes for this.blobIds.
|
|
236
|
+
gcData.gcNodes[getGCNodePath(localId)] = [getGCNodePath(storageId)];
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return gcData;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* When running GC in test mode, this is called to delete blobs that are unused.
|
|
245
|
+
* @param unusedRoutes - These are the blob node ids that are unused and should be deleted.
|
|
246
|
+
*/
|
|
247
|
+
public deleteUnusedRoutes(unusedRoutes: string[]): void {
|
|
248
|
+
// The routes or blob node paths are in the same format as returned in getGCData - `/_blobs/blobId`.
|
|
249
|
+
for (const route of unusedRoutes) {
|
|
250
|
+
const pathParts = route.split("/");
|
|
251
|
+
assert(
|
|
252
|
+
pathParts.length === 3 && pathParts[1] === BlobManager.basePath,
|
|
253
|
+
0x2d5 /* "Invalid blob node id in unused routes." */,
|
|
254
|
+
);
|
|
255
|
+
const blobId = pathParts[2];
|
|
256
|
+
|
|
257
|
+
// The unused blobId could be a localId. If so, remove it from the redirect table and continue. The
|
|
258
|
+
// corresponding storageId may still be used either directly or via other localIds.
|
|
259
|
+
if (this.redirectTable?.has(blobId)) {
|
|
260
|
+
this.redirectTable.delete(blobId);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
this.blobIds.delete(blobId);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
public summarize(): ISummaryTreeWithStats {
|
|
206
268
|
// If we have a redirect table it means the container is about to transition to "Attaching" state, so we need
|
|
207
269
|
// to return an actual snapshot containing all the real storage IDs we know about.
|
|
208
270
|
const attachingOrAttached = !!this.redirectTable || this.runtime.attachState !== AttachState.Detached;
|
|
209
271
|
const blobIds = attachingOrAttached ? this.blobIds : this.detachedBlobIds;
|
|
210
|
-
const
|
|
272
|
+
const builder = new SummaryTreeBuilder();
|
|
273
|
+
blobIds.forEach((blobId) => {
|
|
274
|
+
builder.addAttachment(blobId);
|
|
275
|
+
});
|
|
276
|
+
|
|
211
277
|
if (this.redirectTable && this.redirectTable.size > 0) {
|
|
212
|
-
|
|
278
|
+
builder.addBlob(
|
|
213
279
|
BlobManager.redirectTableBlobName,
|
|
214
|
-
JSON.stringify(Array.from(this.redirectTable.entries()))
|
|
280
|
+
JSON.stringify(Array.from(this.redirectTable.entries())),
|
|
215
281
|
);
|
|
216
282
|
}
|
|
217
|
-
|
|
283
|
+
|
|
284
|
+
return builder.getSummaryTree();
|
|
218
285
|
}
|
|
219
286
|
|
|
220
287
|
public setRedirectTable(table: Map<string, string>) {
|
|
@@ -9,6 +9,7 @@ import { IDeltaManager } from "@fluidframework/container-definitions";
|
|
|
9
9
|
import {
|
|
10
10
|
IDocumentMessage,
|
|
11
11
|
ISequencedDocumentMessage,
|
|
12
|
+
MessageType,
|
|
12
13
|
} from "@fluidframework/protocol-definitions";
|
|
13
14
|
import { assert, performance } from "@fluidframework/common-utils";
|
|
14
15
|
|
|
@@ -17,17 +18,51 @@ import { assert, performance } from "@fluidframework/common-utils";
|
|
|
17
18
|
*/
|
|
18
19
|
export const latencyThreshold = 5000;
|
|
19
20
|
|
|
21
|
+
// Phases in OpPerfTelemetry:
|
|
22
|
+
// 1. Op sits in a buffer in DeltaManager (DM) queue, then in outbound queue for some time.
|
|
23
|
+
// - Note: We do not differentiate these two today in telemetry, but first one is due to batches,
|
|
24
|
+
// second one might happen due to outbound queue being paused.
|
|
25
|
+
// 2. Op is sent to service and back.
|
|
26
|
+
// 3. Op sits in inbound queue.
|
|
27
|
+
// 4. Op is processed.
|
|
28
|
+
interface IOpPerfTelemetryProperties {
|
|
29
|
+
/** Measure time between (1) and (2) - Measure time outbound op is sitting in queue due to active batch */
|
|
30
|
+
durationOutboundQueue: number;
|
|
31
|
+
/** Measure time between (2) and (3) - Track how long op is sitting in inbound queue until it is processed */
|
|
32
|
+
durationInboundQueue: number;
|
|
33
|
+
/** Measure time between (3) and (4) - Time between DM's inbound "push" event until DM's "op" event */
|
|
34
|
+
durationInboundToProcessing: number;
|
|
35
|
+
/** Length of the DeltaManager's inbound queue at the time of the DM's inbound "push" event (3) */
|
|
36
|
+
lenghtInboundQueue: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Timings collected at various moments during the op processing.
|
|
41
|
+
*/
|
|
42
|
+
interface IOpPerfTimings {
|
|
43
|
+
/** Starting time for (1) */
|
|
44
|
+
opStartTimeForLatencyStatistics: number;
|
|
45
|
+
/** Starting time for (2) */
|
|
46
|
+
opStartTimeSittingInboundQueue: number;
|
|
47
|
+
/** Starting time for (3) */
|
|
48
|
+
opStartTimeInboundPushEvent: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
20
51
|
class OpPerfTelemetry {
|
|
21
52
|
private pongCount: number = 0;
|
|
22
53
|
private pingLatency: number | undefined;
|
|
23
54
|
|
|
24
55
|
// Collab window tracking. This is timestamp of %1000 message.
|
|
25
|
-
private
|
|
26
|
-
|
|
27
|
-
// To track round trip time for every %
|
|
28
|
-
private opSendTimeForLatencyStatistics: number | undefined;
|
|
56
|
+
private sequenceNumberForMsnTracking: number | undefined;
|
|
57
|
+
private msnTrackingTimestamp: number = 0;
|
|
58
|
+
// To track round trip time for every %500 client message.
|
|
29
59
|
private clientSequenceNumberForLatencyStatistics: number | undefined;
|
|
30
60
|
|
|
61
|
+
private opProcessingTimes: Partial<IOpPerfTimings> = {};
|
|
62
|
+
|
|
63
|
+
// Performance Data to be reported for ops round trips and processing.
|
|
64
|
+
private opPerfData: Partial<IOpPerfTelemetryProperties> = {};
|
|
65
|
+
|
|
31
66
|
private firstConnection = true;
|
|
32
67
|
private connectionOpSeqNumber: number | undefined;
|
|
33
68
|
private readonly bootTime = performance.now();
|
|
@@ -61,12 +96,49 @@ class OpPerfTelemetry {
|
|
|
61
96
|
}
|
|
62
97
|
});
|
|
63
98
|
this.deltaManager.on("disconnect", () => {
|
|
64
|
-
this.
|
|
99
|
+
this.sequenceNumberForMsnTracking = undefined;
|
|
65
100
|
this.clientSequenceNumberForLatencyStatistics = undefined;
|
|
101
|
+
this.opProcessingTimes = {};
|
|
102
|
+
this.opPerfData = {};
|
|
66
103
|
this.connectionOpSeqNumber = undefined;
|
|
67
104
|
this.firstConnection = false;
|
|
68
105
|
});
|
|
69
106
|
|
|
107
|
+
this.deltaManager.outbound.on("push", (messages) => {
|
|
108
|
+
for (const msg of messages) {
|
|
109
|
+
if (msg.type === MessageType.Operation &&
|
|
110
|
+
this.clientSequenceNumberForLatencyStatistics === msg.clientSequenceNumber) {
|
|
111
|
+
assert(this.opProcessingTimes.opStartTimeSittingInboundQueue === undefined,
|
|
112
|
+
0x2c8 /* "opStartTimeSittingInboundQueue should be undefined" */);
|
|
113
|
+
assert(this.opPerfData.durationInboundQueue === undefined,
|
|
114
|
+
0x2c9 /* "durationInboundQueue should be undefined" */);
|
|
115
|
+
this.opProcessingTimes.opStartTimeSittingInboundQueue = Date.now();
|
|
116
|
+
|
|
117
|
+
assert(this.opPerfData.durationOutboundQueue === undefined,
|
|
118
|
+
0x2ca /* "durationOutboundQueue should be undefined" */);
|
|
119
|
+
|
|
120
|
+
assert(this.opProcessingTimes.opStartTimeForLatencyStatistics !== undefined,
|
|
121
|
+
0x2cb /* "opStartTimeForLatencyStatistics should be undefined" */);
|
|
122
|
+
|
|
123
|
+
this.opPerfData.durationOutboundQueue = this.opProcessingTimes.opStartTimeSittingInboundQueue
|
|
124
|
+
- this.opProcessingTimes.opStartTimeForLatencyStatistics;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
this.deltaManager.inbound.on("push", (message: ISequencedDocumentMessage) => {
|
|
130
|
+
if (this.clientId === message.clientId &&
|
|
131
|
+
message.type === MessageType.Operation &&
|
|
132
|
+
this.clientSequenceNumberForLatencyStatistics === message.clientSequenceNumber &&
|
|
133
|
+
this.opProcessingTimes.opStartTimeSittingInboundQueue !== undefined) {
|
|
134
|
+
this.opProcessingTimes.opStartTimeInboundPushEvent = Date.now();
|
|
135
|
+
this.opPerfData.durationInboundQueue = this.opProcessingTimes.opStartTimeInboundPushEvent
|
|
136
|
+
- this.opProcessingTimes.opStartTimeSittingInboundQueue;
|
|
137
|
+
this.opProcessingTimes.opStartTimeSittingInboundQueue = undefined;
|
|
138
|
+
this.opPerfData.lenghtInboundQueue = this.deltaManager.inbound.length;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
70
142
|
this.deltaManager.inbound.on("idle", (count: number, duration: number) => {
|
|
71
143
|
// Do not want to log zero for sure.
|
|
72
144
|
// We are more interested in aggregates, so logging only if we are processing some number of ops
|
|
@@ -113,8 +185,13 @@ class OpPerfTelemetry {
|
|
|
113
185
|
|
|
114
186
|
private beforeOpSubmit(message: IDocumentMessage) {
|
|
115
187
|
// start with first client op and measure latency every 500 client ops
|
|
116
|
-
if (this.clientSequenceNumberForLatencyStatistics === undefined &&
|
|
117
|
-
|
|
188
|
+
if (this.clientSequenceNumberForLatencyStatistics === undefined &&
|
|
189
|
+
message.clientSequenceNumber % 500 === 1) {
|
|
190
|
+
assert(this.opProcessingTimes.opStartTimeSittingInboundQueue === undefined,
|
|
191
|
+
0x2cc /* "OpTimeSittingInboundQueue should be undefined" */);
|
|
192
|
+
assert(this.opPerfData.durationInboundQueue === undefined,
|
|
193
|
+
0x2cd /* "durationInboundQueue should be undefined" */);
|
|
194
|
+
this.opProcessingTimes.opStartTimeForLatencyStatistics = Date.now();
|
|
118
195
|
this.clientSequenceNumberForLatencyStatistics = message.clientSequenceNumber;
|
|
119
196
|
}
|
|
120
197
|
}
|
|
@@ -127,24 +204,35 @@ class OpPerfTelemetry {
|
|
|
127
204
|
}
|
|
128
205
|
|
|
129
206
|
// Record collab window max size after every 1000th op.
|
|
130
|
-
if (sequenceNumber % 1000 === 0) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
207
|
+
if (this.sequenceNumberForMsnTracking === undefined && sequenceNumber % 1000 === 0) {
|
|
208
|
+
this.sequenceNumberForMsnTracking = sequenceNumber;
|
|
209
|
+
this.msnTrackingTimestamp = message.timestamp;
|
|
210
|
+
}
|
|
211
|
+
if (this.sequenceNumberForMsnTracking !== undefined &&
|
|
212
|
+
message.minimumSequenceNumber >= this.sequenceNumberForMsnTracking) {
|
|
213
|
+
assert(this.msnTrackingTimestamp !== undefined,
|
|
214
|
+
0x2ce /* "msnTrackingTimestamp should not be undefined" */);
|
|
215
|
+
this.logger.sendPerformanceEvent({
|
|
216
|
+
eventName: "MsnStatistics",
|
|
217
|
+
sequenceNumber,
|
|
218
|
+
msnDistance: sequenceNumber - this.sequenceNumberForMsnTracking,
|
|
219
|
+
duration: message.timestamp - this.msnTrackingTimestamp,
|
|
220
|
+
});
|
|
221
|
+
this.sequenceNumberForMsnTracking = undefined;
|
|
140
222
|
}
|
|
141
223
|
|
|
142
224
|
if (this.clientId === message.clientId &&
|
|
143
225
|
this.clientSequenceNumberForLatencyStatistics === message.clientSequenceNumber) {
|
|
144
|
-
assert(this.
|
|
226
|
+
assert(this.opProcessingTimes.opStartTimeForLatencyStatistics !== undefined,
|
|
145
227
|
0x120 /* "Undefined latency statistics (op send time)" */);
|
|
228
|
+
const currentTime = Date.now();
|
|
229
|
+
|
|
230
|
+
if (this.opProcessingTimes.opStartTimeInboundPushEvent !== undefined) {
|
|
231
|
+
this.opPerfData.durationInboundToProcessing = currentTime
|
|
232
|
+
- this.opProcessingTimes.opStartTimeInboundPushEvent;
|
|
233
|
+
}
|
|
146
234
|
|
|
147
|
-
const duration =
|
|
235
|
+
const duration = currentTime - this.opProcessingTimes.opStartTimeForLatencyStatistics;
|
|
148
236
|
|
|
149
237
|
// One of the core expectations for Fluid service is to be fast.
|
|
150
238
|
// When it's not the case, we want to learn about it and be able to investigate, so
|
|
@@ -162,8 +250,11 @@ class OpPerfTelemetry {
|
|
|
162
250
|
duration,
|
|
163
251
|
category,
|
|
164
252
|
pingLatency: this.pingLatency,
|
|
253
|
+
msnDistance: this.deltaManager.lastSequenceNumber - this.deltaManager.minimumSequenceNumber,
|
|
254
|
+
...this.opPerfData,
|
|
165
255
|
});
|
|
166
256
|
this.clientSequenceNumberForLatencyStatistics = undefined;
|
|
257
|
+
this.opPerfData = {};
|
|
167
258
|
}
|
|
168
259
|
}
|
|
169
260
|
}
|