1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-07-29 20:24:36 +02:00

chore(dev): tweak backend-memory-report

This commit is contained in:
syuilo
2026-06-25 09:02:40 +09:00
parent 079ec865e0
commit 453f38b6b6
3 changed files with 205 additions and 6 deletions

View File

@@ -287,10 +287,25 @@ function getHeapSnapshotCategoryValue(report, phase, category) {
return Number.isFinite(value) ? value : null;
}
function getHeapSnapshotBreakdownEntries(report, phase, category) {
const breakdown = report?.[phase]?.heapSnapshot?.breakdowns?.[category];
if (breakdown == null || typeof breakdown !== 'object') return [];
return Object.entries(breakdown)
.filter(([, value]) => Number.isFinite(value) && value > 0)
.toSorted((a, b) => b[1] - a[1]);
}
function escapeCsvValue(value) {
return `"${String(value).replaceAll('"', '""')}"`;
}
function formatSankeyValue(value) {
const rounded = Math.round(value * 100) / 100;
if (Number.isInteger(rounded)) return String(rounded);
return rounded.toFixed(2).replace(/0+$/, '').replace(/\.$/, '');
}
function renderHeapSnapshotSankey(report, phase, title) {
const total = getHeapSnapshotCategoryValue(report, phase, 'Total');
if (total == null || total <= 0) return null;
@@ -300,9 +315,13 @@ function renderHeapSnapshotSankey(report, phase, title) {
.map(category => {
const value = getHeapSnapshotCategoryValue(report, phase, category);
if (value == null || value <= 0) return null;
const breakdownEntries = getHeapSnapshotBreakdownEntries(report, phase, category);
const breakdownTotal = breakdownEntries.reduce((sum, [, childValue]) => sum + childValue, 0);
return {
category,
value,
breakdownTotal,
breakdownEntries,
};
})
.filter(value => value != null);
@@ -312,8 +331,13 @@ function renderHeapSnapshotSankey(report, phase, title) {
const nodeColors = {
[title]: heapSnapshotCategoriesColorsHex.Total,
};
for (const { category } of categories) {
nodeColors[category] = heapSnapshotCategoriesColorsHex[category];
for (const { category, breakdownEntries } of categories) {
const categoryColor = heapSnapshotCategoriesColorsHex[category] ?? heapSnapshotCategoriesColorsHex.Total;
nodeColors[category] = categoryColor;
for (const [childName] of breakdownEntries) {
nodeColors[`${category}: ${childName}`] = categoryColor;
}
}
const lines = [
@@ -327,11 +351,16 @@ function renderHeapSnapshotSankey(report, phase, title) {
nodeColors,
},
})}}%%`,
'sankey',
'sankey-beta',
];
for (const { category, value } of categories) {
lines.push(`${escapeCsvValue(title)},${escapeCsvValue(category)},${value}`);
for (const { category, value, breakdownTotal, breakdownEntries } of categories) {
lines.push(`${escapeCsvValue(title)},${escapeCsvValue(category)},${formatSankeyValue(value)}`);
for (const [childName, childValue] of breakdownEntries) {
const normalizedValue = breakdownTotal > 0 ? childValue * value / breakdownTotal : childValue;
lines.push(`${escapeCsvValue(category)},${escapeCsvValue(`${category}: ${childName}`)},${formatSankeyValue(normalizedValue)}`);
}
}
lines.push('```');
@@ -653,7 +682,7 @@ const head = JSON.parse(await readFile(headFile, 'utf8'));
const baseJsFootprint = baseJsFootprintFile == null ? null : JSON.parse(await readFile(baseJsFootprintFile, 'utf8'));
const headJsFootprint = headJsFootprintFile == null ? null : JSON.parse(await readFile(headJsFootprintFile, 'utf8'));
const lines = [
'## Backend Memory Usage Report',
'## ⚙️ Backend Memory Usage Report',
'',
];

View File

@@ -37,6 +37,8 @@ function readIntegerEnv(name, defaultValue, min) {
return value;
}
const HEAP_SNAPSHOT_BREAKDOWN_TOP_N = readIntegerEnv('MK_MEMORY_HEAP_SNAPSHOT_BREAKDOWN_TOP_N', 6, 1);
function commandName(command) {
if (process.platform !== 'win32') return command;
if (command === 'pnpm') return 'pnpm.cmd';
@@ -111,6 +113,51 @@ function median(values) {
return Math.round((sorted[center - 1] + sorted[center]) / 2);
}
function summarizeHeapSnapshotBreakdowns(samples, phase) {
const breakdowns = {};
for (const category of heapSnapshotCategories) {
if (category === 'Total') continue;
const childKeys = new Set();
for (const sample of samples) {
for (const childKey of Object.keys(sample[phase]?.heapSnapshot?.breakdowns?.[category] ?? {})) {
childKeys.add(childKey);
}
}
const categoryBreakdown = {};
for (const childKey of childKeys) {
const values = samples
.map(sample => sample[phase]?.heapSnapshot?.breakdowns?.[category]?.[childKey])
.filter(value => Number.isFinite(value));
if (values.length > 0) categoryBreakdown[childKey] = median(values);
}
if (Object.keys(categoryBreakdown).length > 0) {
breakdowns[category] = collapseHeapSnapshotBreakdown(categoryBreakdown);
}
}
return breakdowns;
}
function collapseHeapSnapshotBreakdown(breakdown) {
const entries = Object.entries(breakdown)
.filter(([, value]) => value > 0)
.toSorted((a, b) => b[1] - a[1]);
const topEntries = entries.slice(0, HEAP_SNAPSHOT_BREAKDOWN_TOP_N);
const otherValue = entries
.slice(HEAP_SNAPSHOT_BREAKDOWN_TOP_N)
.reduce((sum, [, value]) => sum + value, 0);
const collapsed = Object.fromEntries(topEntries);
if (otherValue > 0) collapsed.Other = otherValue;
return collapsed;
}
function summarizeSamples(samples) {
const summary = {};
@@ -151,9 +198,12 @@ function summarizeSamples(samples) {
}
if (Object.keys(heapSnapshotCategoryValues).length > 0) {
const heapSnapshotBreakdowns = summarizeHeapSnapshotBreakdowns(samples, phase);
summary[phase].heapSnapshot = {
categories: heapSnapshotCategoryValues,
nodeCounts: heapSnapshotNodeCountValues,
...(Object.keys(heapSnapshotBreakdowns).length > 0 ? { breakdowns: heapSnapshotBreakdowns } : {}),
};
}
}