@fluid-tools/fetch-tool 0.53.0-46105
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 +17 -0
- package/LICENSE +21 -0
- package/README.md +114 -0
- package/bin/fluid-fetch +2 -0
- package/dist/fluidAnalyzeMessages.d.ts +8 -0
- package/dist/fluidAnalyzeMessages.d.ts.map +1 -0
- package/dist/fluidAnalyzeMessages.js +598 -0
- package/dist/fluidAnalyzeMessages.js.map +1 -0
- package/dist/fluidFetch.d.ts +6 -0
- package/dist/fluidFetch.d.ts.map +1 -0
- package/dist/fluidFetch.js +119 -0
- package/dist/fluidFetch.js.map +1 -0
- package/dist/fluidFetchArgs.d.ts +35 -0
- package/dist/fluidFetchArgs.d.ts.map +1 -0
- package/dist/fluidFetchArgs.js +206 -0
- package/dist/fluidFetchArgs.js.map +1 -0
- package/dist/fluidFetchInit.d.ts +9 -0
- package/dist/fluidFetchInit.d.ts.map +1 -0
- package/dist/fluidFetchInit.js +161 -0
- package/dist/fluidFetchInit.js.map +1 -0
- package/dist/fluidFetchMessages.d.ts +7 -0
- package/dist/fluidFetchMessages.d.ts.map +1 -0
- package/dist/fluidFetchMessages.js +264 -0
- package/dist/fluidFetchMessages.js.map +1 -0
- package/dist/fluidFetchSharePoint.d.ts +10 -0
- package/dist/fluidFetchSharePoint.d.ts.map +1 -0
- package/dist/fluidFetchSharePoint.js +95 -0
- package/dist/fluidFetchSharePoint.js.map +1 -0
- package/dist/fluidFetchSnapshot.d.ts +7 -0
- package/dist/fluidFetchSnapshot.d.ts.map +1 -0
- package/dist/fluidFetchSnapshot.js +289 -0
- package/dist/fluidFetchSnapshot.js.map +1 -0
- package/package.json +65 -0
- package/src/fluidAnalyzeMessages.ts +687 -0
- package/src/fluidFetch.ts +123 -0
- package/src/fluidFetchArgs.ts +224 -0
- package/src/fluidFetchInit.ts +168 -0
- package/src/fluidFetchMessages.ts +280 -0
- package/src/fluidFetchSharePoint.ts +141 -0
- package/src/fluidFetchSnapshot.ts +383 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*!
|
|
3
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
7
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
8
|
+
var m = o[Symbol.asyncIterator], i;
|
|
9
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
10
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
11
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.printMessageStats = exports.formatNumber = void 0;
|
|
15
|
+
const common_utils_1 = require("@fluidframework/common-utils");
|
|
16
|
+
const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
|
|
17
|
+
const container_runtime_1 = require("@fluidframework/container-runtime");
|
|
18
|
+
const datastore_1 = require("@fluidframework/datastore");
|
|
19
|
+
const noClientName = "No Client";
|
|
20
|
+
const objectTypePrefix = "https://graph.microsoft.com/types/";
|
|
21
|
+
function incr(map, key, size) {
|
|
22
|
+
const value = map.get(key);
|
|
23
|
+
if (value === undefined) {
|
|
24
|
+
map.set(key, [1, size]);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
value[0]++;
|
|
28
|
+
value[1] += size;
|
|
29
|
+
map.set(key, value);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Helper class to track session statistics
|
|
34
|
+
*/
|
|
35
|
+
class ActiveSession {
|
|
36
|
+
constructor(email, startMessage) {
|
|
37
|
+
this.email = email;
|
|
38
|
+
this.startMessage = startMessage;
|
|
39
|
+
this.opCount = 0;
|
|
40
|
+
}
|
|
41
|
+
static create(email, message) {
|
|
42
|
+
return new ActiveSession(email, message);
|
|
43
|
+
}
|
|
44
|
+
reportOp(timestamp) {
|
|
45
|
+
this.opCount++;
|
|
46
|
+
}
|
|
47
|
+
leave(timestamp) {
|
|
48
|
+
return {
|
|
49
|
+
opCount: this.opCount,
|
|
50
|
+
email: this.email,
|
|
51
|
+
startSeq: this.startMessage.sequenceNumber,
|
|
52
|
+
duration: timestamp - this.startMessage.timestamp,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Format a number separating 3 digits by comma
|
|
57
|
+
// eslint-disable-next-line unicorn/no-unsafe-regex
|
|
58
|
+
const formatNumber = (num) => num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
59
|
+
exports.formatNumber = formatNumber;
|
|
60
|
+
function dumpStats(map, props) {
|
|
61
|
+
const fieldSizes = [10, 14];
|
|
62
|
+
const nameLength = 72;
|
|
63
|
+
const fieldsLength = fieldSizes[0] + fieldSizes[1] + 1;
|
|
64
|
+
let headers = props.headers;
|
|
65
|
+
let recordsToShow = props.lines ? props.lines : 10;
|
|
66
|
+
if (map.size !== recordsToShow && !props.removeTotals && recordsToShow > 1) {
|
|
67
|
+
recordsToShow--;
|
|
68
|
+
}
|
|
69
|
+
let sorted;
|
|
70
|
+
const sortIndex = props.orderByFirstColumn ? 0 : 1;
|
|
71
|
+
let add;
|
|
72
|
+
if (props.reverseSort) {
|
|
73
|
+
sorted = [...map.entries()].sort((a, b) => a[1][sortIndex] - b[1][sortIndex]);
|
|
74
|
+
add = "↑";
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
sorted = [...map.entries()].sort((a, b) => b[1][sortIndex] - a[1][sortIndex]);
|
|
78
|
+
add = "↓";
|
|
79
|
+
}
|
|
80
|
+
headers[sortIndex] = `${headers[sortIndex]} ${add}`;
|
|
81
|
+
if (props.reverseColumnsInUI) {
|
|
82
|
+
headers = [headers[1], headers[0]];
|
|
83
|
+
const sorted2 = [];
|
|
84
|
+
for (const [name, [count, size]] of sorted) {
|
|
85
|
+
sorted2.push([name, [size, count]]);
|
|
86
|
+
}
|
|
87
|
+
sorted = sorted2;
|
|
88
|
+
}
|
|
89
|
+
let totalCount = 0;
|
|
90
|
+
let sizeTotal = 0;
|
|
91
|
+
props.title = `${props.title} (${sorted.length})`;
|
|
92
|
+
const header0 = headers[0].padStart(fieldSizes[0]);
|
|
93
|
+
let overflow = header0.length - fieldSizes[0];
|
|
94
|
+
console.log(`\n\n${props.title.padEnd(nameLength)} │ ${header0} ${headers[1].padStart(fieldSizes[1] - overflow)}`);
|
|
95
|
+
console.log(`${"─".repeat(nameLength + 1)}┼${"─".repeat(fieldsLength + 1)}`);
|
|
96
|
+
let index = 0;
|
|
97
|
+
let allOtherCount = 0;
|
|
98
|
+
let allOtherSize = 0;
|
|
99
|
+
for (const [name, [count, size]] of sorted) {
|
|
100
|
+
index++;
|
|
101
|
+
totalCount += count;
|
|
102
|
+
sizeTotal += size;
|
|
103
|
+
if (index <= recordsToShow) {
|
|
104
|
+
const item = name.padEnd(nameLength);
|
|
105
|
+
overflow = item.length - nameLength;
|
|
106
|
+
const col1 = exports.formatNumber(count).padStart(fieldSizes[0] - overflow);
|
|
107
|
+
overflow += col1.length - fieldSizes[0];
|
|
108
|
+
const col2 = exports.formatNumber(size).padStart(fieldSizes[1] - overflow);
|
|
109
|
+
console.log(`${item} │ ${col1} ${col2}`);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
allOtherCount += count;
|
|
113
|
+
allOtherSize += size;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!props.removeTotals) {
|
|
117
|
+
if (allOtherCount || allOtherSize) {
|
|
118
|
+
// eslint-disable-next-line max-len
|
|
119
|
+
console.log(`${`All Others (${sorted.length - recordsToShow})`.padEnd(nameLength)} │ ${exports.formatNumber(allOtherCount).padStart(fieldSizes[0])} ${exports.formatNumber(allOtherSize).padStart(fieldSizes[1])}`);
|
|
120
|
+
}
|
|
121
|
+
console.log(`${"─".repeat(nameLength + 1)}┼${"─".repeat(fieldsLength + 1)}`);
|
|
122
|
+
// eslint-disable-next-line max-len
|
|
123
|
+
console.log(`${"Total".padEnd(nameLength)} │ ${exports.formatNumber(totalCount).padStart(fieldSizes[0])} ${exports.formatNumber(sizeTotal).padStart(fieldSizes[1])}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const getObjectId = (dataStoreId, id) => `[${dataStoreId}]/${id}`;
|
|
127
|
+
/**
|
|
128
|
+
* Analyzer for sessions
|
|
129
|
+
*/
|
|
130
|
+
class SessionAnalyzer {
|
|
131
|
+
constructor() {
|
|
132
|
+
this.sessionsInProgress = new Map();
|
|
133
|
+
this.sessions = new Map();
|
|
134
|
+
this.users = new Map();
|
|
135
|
+
this.first = true;
|
|
136
|
+
}
|
|
137
|
+
processOp(message, msgSize, skipMessage) {
|
|
138
|
+
if (this.first) {
|
|
139
|
+
this.first = false;
|
|
140
|
+
// Start of the road.
|
|
141
|
+
const noNameSession = ActiveSession.create(noClientName, message);
|
|
142
|
+
this.sessionsInProgress.set(noClientName, noNameSession);
|
|
143
|
+
}
|
|
144
|
+
const session = processQuorumMessages(message, skipMessage, this.sessionsInProgress, this.sessions, this.users);
|
|
145
|
+
if (!skipMessage && session) {
|
|
146
|
+
session.reportOp(message.timestamp);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
reportAnalyzes(lastOp) {
|
|
150
|
+
// Close any open sessions
|
|
151
|
+
reportOpenSessions(lastOp.timestamp, this.sessionsInProgress, this.sessions, this.users);
|
|
152
|
+
dumpStats(this.users, {
|
|
153
|
+
title: "Users",
|
|
154
|
+
headers: ["Sessions", "Op count"],
|
|
155
|
+
reverseColumnsInUI: true,
|
|
156
|
+
lines: 6,
|
|
157
|
+
});
|
|
158
|
+
dumpStats(this.sessions, {
|
|
159
|
+
title: "Sessions",
|
|
160
|
+
headers: ["Duration(s)", "Op count"],
|
|
161
|
+
reverseColumnsInUI: true,
|
|
162
|
+
lines: 6,
|
|
163
|
+
});
|
|
164
|
+
dumpStats(this.sessions, {
|
|
165
|
+
title: "Sessions",
|
|
166
|
+
headers: ["Duration(s)", "Op count"],
|
|
167
|
+
orderByFirstColumn: true,
|
|
168
|
+
reverseColumnsInUI: true,
|
|
169
|
+
removeTotals: true,
|
|
170
|
+
lines: 5,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Analyzer for data structures
|
|
176
|
+
*/
|
|
177
|
+
class DataStructureAnalyzer {
|
|
178
|
+
constructor() {
|
|
179
|
+
this.messageTypeStats = new Map();
|
|
180
|
+
this.dataType = new Map();
|
|
181
|
+
this.dataTypeStats = new Map();
|
|
182
|
+
this.objectStats = new Map();
|
|
183
|
+
}
|
|
184
|
+
processOp(message, msgSize, skipMessage) {
|
|
185
|
+
if (!skipMessage) {
|
|
186
|
+
processOp(message, this.dataType, this.objectStats, msgSize, this.dataTypeStats, this.messageTypeStats);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
reportAnalyzes(lastOp) {
|
|
190
|
+
dumpStats(this.messageTypeStats, {
|
|
191
|
+
title: "Message Type",
|
|
192
|
+
headers: ["Op count", "Bytes"],
|
|
193
|
+
lines: 20,
|
|
194
|
+
});
|
|
195
|
+
dumpStats(calcChannelStats(this.dataType, this.objectStats), {
|
|
196
|
+
title: "Channel name",
|
|
197
|
+
headers: ["Op count", "Bytes"],
|
|
198
|
+
lines: 7,
|
|
199
|
+
});
|
|
200
|
+
/*
|
|
201
|
+
dumpStats(this.dataTypeStats, {
|
|
202
|
+
title: "Channel type",
|
|
203
|
+
headers: ["Op count", "Bytes"],
|
|
204
|
+
});
|
|
205
|
+
*/
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Helper class to report if we filtered out any messages.
|
|
210
|
+
*/
|
|
211
|
+
class FilteredMessageAnalyzer {
|
|
212
|
+
constructor() {
|
|
213
|
+
this.sizeTotal = 0;
|
|
214
|
+
this.opsTotal = 0;
|
|
215
|
+
this.sizeFiltered = 0;
|
|
216
|
+
this.opsFiltered = 0;
|
|
217
|
+
this.filtered = false;
|
|
218
|
+
}
|
|
219
|
+
processOp(message, msgSize, skipMessage) {
|
|
220
|
+
this.sizeTotal += msgSize;
|
|
221
|
+
this.opsTotal++;
|
|
222
|
+
if (!skipMessage) {
|
|
223
|
+
this.sizeFiltered += msgSize;
|
|
224
|
+
this.opsFiltered++;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
this.filtered = true;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
reportAnalyzes(lastOp) {
|
|
231
|
+
if (this.filtered) {
|
|
232
|
+
// eslint-disable-next-line max-len
|
|
233
|
+
console.log(`\nData is filtered according to --filter:messageType argument(s):\nOp size: ${this.sizeFiltered} / ${this.sizeTotal}\nOp count ${this.opsFiltered} / ${this.opsTotal}`);
|
|
234
|
+
}
|
|
235
|
+
if (this.opsTotal === 0) {
|
|
236
|
+
console.error("No ops were found");
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Helper class to find places where we generated too many ops
|
|
242
|
+
*/
|
|
243
|
+
class MessageDensityAnalyzer {
|
|
244
|
+
constructor() {
|
|
245
|
+
this.opChunk = 1000;
|
|
246
|
+
this.opLimit = 1;
|
|
247
|
+
this.size = 0;
|
|
248
|
+
this.timeStart = 0;
|
|
249
|
+
this.doctimerStart = 0;
|
|
250
|
+
this.ranges = new Map();
|
|
251
|
+
}
|
|
252
|
+
processOp(message, msgSize, skipMessage) {
|
|
253
|
+
if (message.sequenceNumber >= this.opLimit) {
|
|
254
|
+
if (message.sequenceNumber !== 1) {
|
|
255
|
+
const timeDiff = durationFromTime(message.timestamp - this.timeStart);
|
|
256
|
+
const opsString = `ops = [${this.opLimit - this.opChunk}, ${this.opLimit - 1}]`.padEnd(26);
|
|
257
|
+
// eslint-disable-next-line max-len
|
|
258
|
+
const timeString = `time = [${durationFromTime(this.timeStart - this.doctimerStart)}, ${durationFromTime(message.timestamp - this.doctimerStart)}]`;
|
|
259
|
+
this.ranges.set(`${opsString} ${timeString}`, [timeDiff, this.size]);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
this.doctimerStart = message.timestamp;
|
|
263
|
+
}
|
|
264
|
+
this.opLimit += this.opChunk;
|
|
265
|
+
this.size = 0;
|
|
266
|
+
this.timeStart = message.timestamp;
|
|
267
|
+
}
|
|
268
|
+
if (!skipMessage) {
|
|
269
|
+
this.size += msgSize;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
reportAnalyzes(lastOp) {
|
|
273
|
+
dumpStats(this.ranges, {
|
|
274
|
+
title: "Fastest 1000 op ranges",
|
|
275
|
+
headers: ["Duration(s)", "Bytes"],
|
|
276
|
+
orderByFirstColumn: true,
|
|
277
|
+
reverseSort: true,
|
|
278
|
+
removeTotals: true,
|
|
279
|
+
lines: 3,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Helper class to analyze collab window size
|
|
285
|
+
*/
|
|
286
|
+
class CollabWindowSizeAnalyzer {
|
|
287
|
+
constructor() {
|
|
288
|
+
this.maxCollabWindow = 0;
|
|
289
|
+
this.opSeq = 0;
|
|
290
|
+
}
|
|
291
|
+
processOp(message, msgSize, skipMessage) {
|
|
292
|
+
const value = message.sequenceNumber - message.minimumSequenceNumber;
|
|
293
|
+
if (value > this.maxCollabWindow) {
|
|
294
|
+
this.maxCollabWindow = value;
|
|
295
|
+
this.opSeq = message.sequenceNumber;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
reportAnalyzes(lastOp) {
|
|
299
|
+
console.log(`\nMaximum collab window size: ${this.maxCollabWindow}, seq# ${this.opSeq}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Helper class to analyze frequency of summaries
|
|
304
|
+
*/
|
|
305
|
+
class SummaryAnalyzer {
|
|
306
|
+
constructor() {
|
|
307
|
+
this.lastSummaryOp = 0;
|
|
308
|
+
this.maxDistance = 0;
|
|
309
|
+
this.maxSeq = 0;
|
|
310
|
+
this.minDistance = Number.MAX_SAFE_INTEGER;
|
|
311
|
+
this.minSeq = 0;
|
|
312
|
+
this.maxResponse = 0;
|
|
313
|
+
this.maxResponseSeq = 0;
|
|
314
|
+
}
|
|
315
|
+
processOp(message, msgSize, skipMessage) {
|
|
316
|
+
if (message.type === protocol_definitions_1.MessageType.SummaryAck) {
|
|
317
|
+
const distance = message.sequenceNumber - this.lastSummaryOp - 1;
|
|
318
|
+
if (this.maxDistance < distance) {
|
|
319
|
+
this.maxDistance = distance;
|
|
320
|
+
this.maxSeq = message.sequenceNumber;
|
|
321
|
+
}
|
|
322
|
+
if (this.minDistance > distance) {
|
|
323
|
+
this.minDistance = distance;
|
|
324
|
+
this.minSeq = message.sequenceNumber;
|
|
325
|
+
}
|
|
326
|
+
this.lastSummaryOp = message.sequenceNumber;
|
|
327
|
+
}
|
|
328
|
+
if (message.type === protocol_definitions_1.MessageType.SummaryAck || message.type === protocol_definitions_1.MessageType.SummaryNack) {
|
|
329
|
+
const contents = message.contents.summaryProposal;
|
|
330
|
+
const distance = message.sequenceNumber - contents.summarySequenceNumber;
|
|
331
|
+
if (distance > this.maxResponse) {
|
|
332
|
+
this.maxResponse = distance;
|
|
333
|
+
this.maxResponseSeq = message.sequenceNumber;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
reportAnalyzes(lastOp) {
|
|
338
|
+
const distance = lastOp.sequenceNumber - this.lastSummaryOp;
|
|
339
|
+
if (this.maxDistance < distance) {
|
|
340
|
+
this.maxDistance = distance;
|
|
341
|
+
this.maxSeq = lastOp.sequenceNumber + 1;
|
|
342
|
+
}
|
|
343
|
+
console.log("");
|
|
344
|
+
if (this.minDistance === Number.MAX_SAFE_INTEGER) {
|
|
345
|
+
console.log("No summaries found in this document");
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
console.log(`Maximum distance between summaries: ${this.maxDistance}, seq# ${this.maxSeq}`);
|
|
349
|
+
console.log(`Maximum server response for summary: ${this.maxResponse}, seq# ${this.maxResponseSeq}`);
|
|
350
|
+
console.log(`Minimum distance between summaries: ${this.minDistance}, seq# ${this.minSeq}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Helper class to dump messages to console
|
|
356
|
+
*/
|
|
357
|
+
class MessageDumper {
|
|
358
|
+
processOp(message, msgSize, skipMessage) {
|
|
359
|
+
if (!skipMessage) {
|
|
360
|
+
console.log(JSON.stringify(message, undefined, 2));
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
reportAnalyzes(lastOp) {
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
async function printMessageStats(generator, // AsyncGenerator<ISequencedDocumentMessage[]>,
|
|
367
|
+
dumpMessageStats, dumpMessages, messageTypeFilter = new Set()) {
|
|
368
|
+
var e_1, _a;
|
|
369
|
+
let lastMessage;
|
|
370
|
+
const analyzers = [
|
|
371
|
+
new FilteredMessageAnalyzer(),
|
|
372
|
+
new SessionAnalyzer(),
|
|
373
|
+
new DataStructureAnalyzer(),
|
|
374
|
+
new MessageDensityAnalyzer(),
|
|
375
|
+
new CollabWindowSizeAnalyzer(),
|
|
376
|
+
new SummaryAnalyzer(),
|
|
377
|
+
];
|
|
378
|
+
if (dumpMessages) {
|
|
379
|
+
analyzers.push(new MessageDumper());
|
|
380
|
+
}
|
|
381
|
+
try {
|
|
382
|
+
for (var generator_1 = __asyncValues(generator), generator_1_1; generator_1_1 = await generator_1.next(), !generator_1_1.done;) {
|
|
383
|
+
const messages = generator_1_1.value;
|
|
384
|
+
for (const message of messages) {
|
|
385
|
+
const msgSize = JSON.stringify(message).length;
|
|
386
|
+
lastMessage = message;
|
|
387
|
+
const skipMessage = messageTypeFilter.size !== 0 && !messageTypeFilter.has(message.type);
|
|
388
|
+
for (const analyzer of analyzers) {
|
|
389
|
+
analyzer.processOp(message, msgSize, skipMessage);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
395
|
+
finally {
|
|
396
|
+
try {
|
|
397
|
+
if (generator_1_1 && !generator_1_1.done && (_a = generator_1.return)) await _a.call(generator_1);
|
|
398
|
+
}
|
|
399
|
+
finally { if (e_1) throw e_1.error; }
|
|
400
|
+
}
|
|
401
|
+
if (lastMessage !== undefined) {
|
|
402
|
+
if (dumpMessageStats) {
|
|
403
|
+
for (const analyzer of analyzers) {
|
|
404
|
+
analyzer.reportAnalyzes(lastMessage);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
// Warn about filtered messages
|
|
409
|
+
analyzers[0].reportAnalyzes(lastMessage);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
console.log("");
|
|
413
|
+
}
|
|
414
|
+
exports.printMessageStats = printMessageStats;
|
|
415
|
+
function processOp(message, dataType, objectStats, msgSize, dataTypeStats, messageTypeStats) {
|
|
416
|
+
let type = message.type;
|
|
417
|
+
let recorded = false;
|
|
418
|
+
if (container_runtime_1.isRuntimeMessage(message)) {
|
|
419
|
+
const runtimeMessage = container_runtime_1.unpackRuntimeMessage(message);
|
|
420
|
+
switch (runtimeMessage.type) {
|
|
421
|
+
case container_runtime_1.ContainerMessageType.Attach: {
|
|
422
|
+
const attachMessage = runtimeMessage.contents;
|
|
423
|
+
processDataStoreAttachOp(attachMessage, dataType);
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
// skip for now because these ops do not have contents
|
|
427
|
+
case container_runtime_1.ContainerMessageType.BlobAttach: {
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
default: {
|
|
431
|
+
let envelope = runtimeMessage.contents;
|
|
432
|
+
// TODO: Legacy?
|
|
433
|
+
if (envelope && typeof envelope === "string") {
|
|
434
|
+
envelope = JSON.parse(envelope);
|
|
435
|
+
}
|
|
436
|
+
const innerContent = envelope.contents;
|
|
437
|
+
const address = envelope.address;
|
|
438
|
+
type = `${type}/${innerContent.type}`;
|
|
439
|
+
switch (innerContent.type) {
|
|
440
|
+
case datastore_1.DataStoreMessageType.Attach: {
|
|
441
|
+
const attachMessage = innerContent.content;
|
|
442
|
+
let objectType = attachMessage.type;
|
|
443
|
+
if (objectType.startsWith(objectTypePrefix)) {
|
|
444
|
+
objectType = objectType.substring(objectTypePrefix.length);
|
|
445
|
+
}
|
|
446
|
+
dataType.set(getObjectId(address, attachMessage.id), objectType);
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
case datastore_1.DataStoreMessageType.ChannelOp:
|
|
450
|
+
default: {
|
|
451
|
+
const innerEnvelope = innerContent.content;
|
|
452
|
+
const innerContent2 = innerEnvelope.contents;
|
|
453
|
+
const objectId = getObjectId(address, innerEnvelope.address);
|
|
454
|
+
incr(objectStats, objectId, msgSize);
|
|
455
|
+
let objectType = dataType.get(objectId);
|
|
456
|
+
if (objectType === undefined) {
|
|
457
|
+
// Somehow we do not have data...
|
|
458
|
+
dataType.set(objectId, objectId);
|
|
459
|
+
objectType = objectId;
|
|
460
|
+
}
|
|
461
|
+
incr(dataTypeStats, objectType, msgSize);
|
|
462
|
+
recorded = true;
|
|
463
|
+
let subType = innerContent2.type;
|
|
464
|
+
if (innerContent2.type === "set" &&
|
|
465
|
+
typeof innerContent2.value === "object" &&
|
|
466
|
+
innerContent2.value !== null) {
|
|
467
|
+
type = `${type}/${subType}`;
|
|
468
|
+
subType = innerContent2.value.type;
|
|
469
|
+
}
|
|
470
|
+
else if (objectType === "mergeTree" && subType !== undefined) {
|
|
471
|
+
const types = ["insert", "remove", "annotate", "group"];
|
|
472
|
+
if (types[subType]) {
|
|
473
|
+
subType = types[subType];
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if (subType !== undefined) {
|
|
477
|
+
type = `${type}/${subType}`;
|
|
478
|
+
}
|
|
479
|
+
type = `${type} (${objectType})`;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
incr(messageTypeStats, type, msgSize);
|
|
486
|
+
if (!recorded) {
|
|
487
|
+
// const objectId = `${type} (system)`;
|
|
488
|
+
const objectId = `(system messages)`;
|
|
489
|
+
const objectType = objectId;
|
|
490
|
+
if (dataType.get(objectId) === undefined) {
|
|
491
|
+
dataType.set(objectId, objectId);
|
|
492
|
+
}
|
|
493
|
+
incr(objectStats, objectId, msgSize);
|
|
494
|
+
incr(dataTypeStats, objectType, msgSize);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function processDataStoreAttachOp(attachMessage, dataType) {
|
|
498
|
+
// dataType.set(getObjectId(attachMessage.id), attachMessage.type);
|
|
499
|
+
// That's data store, and it brings a bunch of data structures.
|
|
500
|
+
// Let's try to crack it.
|
|
501
|
+
let parsedAttachMessage;
|
|
502
|
+
if (typeof attachMessage === "string") {
|
|
503
|
+
parsedAttachMessage = JSON.parse(attachMessage);
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
parsedAttachMessage = attachMessage;
|
|
507
|
+
}
|
|
508
|
+
for (const entry of parsedAttachMessage.snapshot.entries) {
|
|
509
|
+
if (entry.type === protocol_definitions_1.TreeEntry.Tree) {
|
|
510
|
+
for (const entry2 of entry.value.entries) {
|
|
511
|
+
if (entry2.path === ".attributes" && entry2.type === protocol_definitions_1.TreeEntry.Blob) {
|
|
512
|
+
const attrib = JSON.parse(entry2.value.contents);
|
|
513
|
+
let objectType = attrib.type;
|
|
514
|
+
if (objectType.startsWith(objectTypePrefix)) {
|
|
515
|
+
objectType = objectType.substring(objectTypePrefix.length);
|
|
516
|
+
}
|
|
517
|
+
dataType.set(getObjectId(parsedAttachMessage.id, entry.path), objectType);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function reportOpenSessions(lastOpTimestamp, sessionsInProgress, sessions, users) {
|
|
524
|
+
const activeSessions = new Map();
|
|
525
|
+
for (const [clientId, ses] of sessionsInProgress) {
|
|
526
|
+
const sessionInfo = ses.leave(lastOpTimestamp);
|
|
527
|
+
if (clientId !== noClientName) {
|
|
528
|
+
const sessionName = `${clientId} (${sessionInfo.email})`;
|
|
529
|
+
const sessionPayload = [durationFromTime(sessionInfo.duration), sessionInfo.opCount];
|
|
530
|
+
sessions.set(sessionName, sessionPayload);
|
|
531
|
+
activeSessions.set(sessionName, sessionPayload);
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
sessions.set(`Full file lifespan (noClient messages)`, [durationFromTime(sessionInfo.duration), sessionInfo.opCount]);
|
|
535
|
+
}
|
|
536
|
+
incr(users, sessionInfo.email, sessionInfo.opCount);
|
|
537
|
+
}
|
|
538
|
+
if (activeSessions.size > 0) {
|
|
539
|
+
dumpStats(activeSessions, {
|
|
540
|
+
title: "Active sessions",
|
|
541
|
+
headers: ["Duration", "Op count"],
|
|
542
|
+
lines: 6,
|
|
543
|
+
orderByFirstColumn: true,
|
|
544
|
+
removeTotals: true,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
function calcChannelStats(dataType, objectStats) {
|
|
549
|
+
const channelStats = new Map();
|
|
550
|
+
for (const [objectId, type] of dataType) {
|
|
551
|
+
let value = objectStats.get(objectId);
|
|
552
|
+
if (value === undefined) {
|
|
553
|
+
value = [0, 0];
|
|
554
|
+
}
|
|
555
|
+
if (type === objectId) {
|
|
556
|
+
channelStats.set(`${objectId}`, value);
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
channelStats.set(`${objectId} (${type})`, value);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return channelStats;
|
|
563
|
+
}
|
|
564
|
+
function processQuorumMessages(message, skipMessage, sessionsInProgress, sessions, users) {
|
|
565
|
+
let session;
|
|
566
|
+
const dataString = message.data;
|
|
567
|
+
if (message.type === "join") {
|
|
568
|
+
const data = JSON.parse(dataString);
|
|
569
|
+
session = ActiveSession.create(data.detail.user.id, message);
|
|
570
|
+
sessionsInProgress.set(data.clientId, session);
|
|
571
|
+
}
|
|
572
|
+
else if (message.type === "leave") {
|
|
573
|
+
const clientId = JSON.parse(dataString);
|
|
574
|
+
session = sessionsInProgress.get(clientId);
|
|
575
|
+
sessionsInProgress.delete(clientId);
|
|
576
|
+
common_utils_1.assert(!!session, 0x1b7 /* "Bad session state for processing quorum messages" */);
|
|
577
|
+
if (session) {
|
|
578
|
+
if (!skipMessage) {
|
|
579
|
+
session.reportOp(message.timestamp);
|
|
580
|
+
}
|
|
581
|
+
const sessionInfo = session.leave(message.timestamp);
|
|
582
|
+
sessions.set(`${clientId} (${sessionInfo.email})`, [durationFromTime(sessionInfo.duration), sessionInfo.opCount]);
|
|
583
|
+
incr(users, sessionInfo.email, sessionInfo.opCount);
|
|
584
|
+
session = undefined; // Do not record it second time
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
// message.clientId can be null
|
|
589
|
+
session = sessionsInProgress.get(message.clientId);
|
|
590
|
+
if (session === undefined) {
|
|
591
|
+
session = sessionsInProgress.get(noClientName);
|
|
592
|
+
common_utils_1.assert(!!session, 0x1b8 /* "Bad session state for processing quorum messages" */);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return session;
|
|
596
|
+
}
|
|
597
|
+
const durationFromTime = (time) => Math.floor(time / 1000);
|
|
598
|
+
//# sourceMappingURL=fluidAnalyzeMessages.js.map
|