1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-07-25 16:45:00 +02:00
This commit is contained in:
syuilo
2026-07-03 11:21:28 +09:00
parent cd953e918c
commit b487f91087
4 changed files with 82 additions and 3 deletions

View File

@@ -42,8 +42,23 @@ export type NetworkRequest = {
errorText?: string;
};
export type WebSocketConnection = {
requestId: string;
url: string;
createdAt: number;
handshakeRequestHeaders?: Record<string, string>;
handshakeResponseStatus?: number;
handshakeResponseStatusText?: string;
handshakeResponseHeaders?: Record<string, string>;
closedAt?: number;
sentFrameCount: number;
receivedFrameCount: number;
errorCount: number;
};
export type NetworkSummary = {
requestCount: number;
webSocketConnectionCount: number;
finishedRequestCount: number;
failedRequestCount: number;
cachedRequestCount: number;
@@ -332,6 +347,7 @@ export class Chrome {
private handle: ChromeHandle;
public cdp: CdpClient;
public networkRequests: NetworkRequest[] = [];
public webSocketConnections: WebSocketConnection[] = [];
private scenarioTimeoutMs: number;
private pendingNetworkDetailReads: Promise<void>[] = [];
@@ -369,6 +385,7 @@ export class Chrome {
public async enableNetworkTracking() {
const requests = new Map<string, NetworkRequest>();
const webSockets = new Map<string, WebSocketConnection>();
const readRequestBody = (row: NetworkRequest) => {
if (!row.hasRequestBody || row.requestBody != null) return;
@@ -405,6 +422,58 @@ export class Chrome {
this.networkRequests.push(row);
});
this.cdp.on('Network.webSocketCreated', params => {
if (params.requestId == null || params.url == null) return;
const row: WebSocketConnection = {
requestId: params.requestId,
url: params.url,
createdAt: params.timestamp ?? 0,
sentFrameCount: 0,
receivedFrameCount: 0,
errorCount: 0,
};
webSockets.set(params.requestId, row);
this.webSocketConnections.push(row);
});
this.cdp.on('Network.webSocketWillSendHandshakeRequest', params => {
const row = webSockets.get(params.requestId);
if (row == null) return;
row.handshakeRequestHeaders = normalizeHeaders(params.request?.headers);
});
this.cdp.on('Network.webSocketHandshakeResponseReceived', params => {
const row = webSockets.get(params.requestId);
if (row == null) return;
row.handshakeResponseStatus = params.response?.status;
row.handshakeResponseStatusText = params.response?.statusText;
row.handshakeResponseHeaders = normalizeHeaders(params.response?.headers);
});
this.cdp.on('Network.webSocketFrameSent', params => {
const row = webSockets.get(params.requestId);
if (row == null) return;
row.sentFrameCount += 1;
});
this.cdp.on('Network.webSocketFrameReceived', params => {
const row = webSockets.get(params.requestId);
if (row == null) return;
row.receivedFrameCount += 1;
});
this.cdp.on('Network.webSocketFrameError', params => {
const row = webSockets.get(params.requestId);
if (row == null) return;
row.errorCount += 1;
});
this.cdp.on('Network.webSocketClosed', params => {
const row = webSockets.get(params.requestId);
if (row == null) return;
row.closedAt = params.timestamp ?? 0;
});
this.cdp.on('Network.responseReceived', params => {
const row = requests.get(params.requestId);
if (row == null) return;
@@ -609,7 +678,7 @@ function isMeasurableRequest(row: NetworkRequest) {
return !row.url.startsWith('data:') && !row.url.startsWith('blob:') && !row.url.startsWith('devtools:');
}
export function summarizeNetwork(requestRows: NetworkRequest[], baseUrl: string): NetworkSummary {
export function summarizeNetwork(requestRows: NetworkRequest[], baseUrl: string, webSocketRows?: WebSocketConnection[]): NetworkSummary {
const origin = new URL(baseUrl).origin;
const rows = requestRows.filter(isMeasurableRequest);
const byResourceType = {} as NetworkSummary['byResourceType'];
@@ -636,6 +705,9 @@ export function summarizeNetwork(requestRows: NetworkRequest[], baseUrl: string)
return {
requestCount: rows.length,
webSocketConnectionCount: webSocketRows == null
? rows.filter(row => row.resourceType === 'WebSocket').length
: webSocketRows.length,
finishedRequestCount: rows.filter(row => row.finished).length,
failedRequestCount: rows.filter(row => row.failed).length,
cachedRequestCount: rows.filter(row => row.fromDiskCache).length,

View File

@@ -18,6 +18,7 @@ export type BrowserMeasurement = {
durationMs: number;
network: {
requestCount: number;
webSocketConnectionCount?: number;
finishedRequestCount: number;
failedRequestCount: number;
cachedRequestCount: number;
@@ -157,6 +158,10 @@ function resourceTypeSampleBytes(sample: BrowserMeasurementSample, resourceTypes
return resourceTypeBytes(sample, resourceTypes);
}
function webSocketConnectionCount(report: BrowserMeasurement) {
return report.network.webSocketConnectionCount ?? report.network.byResourceType.WebSocket?.requests ?? 0;
}
function getMetric(report: BrowserMeasurement, key: string) {
return report.performance.cdpMetrics[key];
}
@@ -165,6 +170,7 @@ function renderSummaryTable(base: BrowserMetricsReport, head: BrowserMetricsRepo
const rows = [
//metricRow('Scenario duration', base, head, summary => summary.durationMs, sample => sample.durationMs, formatMs),
metricRow('Requests', base, head, summary => summary.network.requestCount, sample => sample.network.requestCount, util.formatNumber),
metricRow('WebSocket connections', base, head, webSocketConnectionCount, webSocketConnectionCount, util.formatNumber),
//metricRow('Failed requests', base, head, summary => summary.network.failedRequestCount, sample => sample.network.failedRequestCount, util.formatNumber),
metricRow('Encoded network', base, head, summary => summary.network.totalEncodedBytes, sample => sample.network.totalEncodedBytes, util.formatBytes, 10000),
metricRow('Decoded body', base, head, summary => summary.network.totalDecodedBodyBytes, sample => sample.network.totalDecodedBodyBytes, util.formatBytes, 10000),

View File

@@ -132,6 +132,7 @@ function summarizeNetworkSamples(samples: BrowserMeasurementSample[]): NetworkSu
return {
requestCount: finiteMedian(samples.map(sample => sample.network.requestCount)),
webSocketConnectionCount: finiteMedian(samples.map(sample => sample.network.webSocketConnectionCount)),
finishedRequestCount: finiteMedian(samples.map(sample => sample.network.finishedRequestCount)),
failedRequestCount: finiteMedian(samples.map(sample => sample.network.failedRequestCount)),
cachedRequestCount: finiteMedian(samples.map(sample => sample.network.cachedRequestCount)),
@@ -242,7 +243,7 @@ async function measureSample(label: 'base' | 'head', round: number, heapSnapshot
url: baseUrl,
scenario: 'fresh browser signup, first timeline note, after the note becomes visible',
durationMs,
network: summarizeNetwork(chrome.networkRequests, baseUrl),
network: summarizeNetwork(chrome.networkRequests, baseUrl, chrome.webSocketConnections),
networkRequests: chrome.networkRequests,
performance,
heapSnapshot,

View File

@@ -5,7 +5,7 @@
// NOTE: このファイルはworkflow上でバックエンドからも参照されるため、side effectがあってはならない
import { ChildProcessWithoutNullStreams, spawn, spawnSync } from 'node:child_process';
import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from 'node:child_process';
import { promises as fs } from 'node:fs';
import path from 'node:path';