enhance(dev): improve mem report (#17118)

* wip

* wip

* Update report-backend-memory.yml

* Update report-backend-memory.yml

* Update .github/workflows/report-backend-memory.yml

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
syuilo 2026-01-22 14:32:57 +09:00 committed by GitHub
parent f744b5711f
commit 2fa6ecc7ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 99 additions and 40 deletions

View File

@ -54,9 +54,10 @@ jobs:
BASE_MEMORY=$(cat ./artifacts/memory-base.json) BASE_MEMORY=$(cat ./artifacts/memory-base.json)
HEAD_MEMORY=$(cat ./artifacts/memory-head.json) HEAD_MEMORY=$(cat ./artifacts/memory-head.json)
variation() {
calc() { calc() {
BASE=$(echo "$BASE_MEMORY" | jq -r '.memory.'"$1"' // 0') BASE=$(echo "$BASE_MEMORY" | jq -r ".${1}.${2} // 0")
HEAD=$(echo "$HEAD_MEMORY" | jq -r '.memory.'"$1"' // 0') HEAD=$(echo "$HEAD_MEMORY" | jq -r ".${1}.${2} // 0")
DIFF=$((HEAD - BASE)) DIFF=$((HEAD - BASE))
if [ "$BASE" -gt 0 ]; then if [ "$BASE" -gt 0 ]; then
@ -70,15 +71,31 @@ jobs:
HEAD_MB=$(echo "scale=2; $HEAD / 1024" | bc) HEAD_MB=$(echo "scale=2; $HEAD / 1024" | bc)
DIFF_MB=$(echo "scale=2; $DIFF / 1024" | bc) DIFF_MB=$(echo "scale=2; $DIFF / 1024" | bc)
echo "$1-base=$BASE_MB" >> "$GITHUB_OUTPUT" JSON=$(jq -c -n \
echo "$1-head=$HEAD_MB" >> "$GITHUB_OUTPUT" --arg base "$BASE_MB" \
echo "$1-diff=$DIFF_MB" >> "$GITHUB_OUTPUT" --arg head "$HEAD_MB" \
echo "$1-diff_percent=$DIFF_PERCENT" >> "$GITHUB_OUTPUT" --arg diff "$DIFF_MB" \
--arg diff_percent "$DIFF_PERCENT" \
'{base: $base, head: $head, diff: $diff, diff_percent: $diff_percent}')
echo "$JSON"
} }
calc VmRSS JSON=$(jq -c -n \
calc VmHWM --argjson VmRSS "$(calc $1 VmRSS)" \
calc VmSize --argjson VmHWM "$(calc $1 VmHWM)" \
--argjson VmSize "$(calc $1 VmSize)" \
'{VmRSS: $VmRSS, VmHWM: $VmHWM, VmSize: $VmSize}')
echo "$JSON"
}
JSON=$(jq -c -n \
--argjson beforeGc "$(variation beforeGc)" \
--argjson afterGc "$(variation afterGc)" \
'{beforeGc: $beforeGc, afterGc: $afterGc}')
echo "res=$JSON" >> "$GITHUB_OUTPUT"
- id: build-comment - id: build-comment
name: Build memory comment name: Build memory comment
run: | run: |
@ -88,15 +105,33 @@ jobs:
echo "$HEADER" > ./output.md echo "$HEADER" > ./output.md
echo >> ./output.md echo >> ./output.md
table() {
line() {
BASE=$(echo "${{ steps.compare.outputs.res }}" | jq -r ".${1}.${2}.base")
HEAD=$(echo "${{ steps.compare.outputs.res }}" | jq -r ".${1}.${2}.head")
DIFF=$(echo "${{ steps.compare.outputs.res }}" | jq -r ".${1}.${2}.diff")
DIFF_PERCENT=$(echo "${{ steps.compare.outputs.res }}" | jq -r ".${1}.${2}.diff_percent")
echo "| ${2} | ${BASE} MB | ${HEAD} MB | ${DIFF} MB (${DIFF_PERCENT}%) |" >> ./output.md
}
echo "| Metric | base | head | Diff |" >> ./output.md echo "| Metric | base | head | Diff |" >> ./output.md
echo "|--------|------|------|------|" >> ./output.md echo "|--------|------|------|------|" >> ./output.md
echo "| RSS | ${{ steps.compare.outputs.VmRSS-base }} MB | ${{ steps.compare.outputs.VmRSS-head }} MB | ${{ steps.compare.outputs.VmRSS-diff }} MB (${{ steps.compare.outputs.VmRSS-diff_percent }}%) |" >> ./output.md line $1 VmRSS
echo "| HWM | ${{ steps.compare.outputs.VmHWM-base }} MB | ${{ steps.compare.outputs.VmHWM-head }} MB | ${{ steps.compare.outputs.VmHWM-diff }} MB (${{ steps.compare.outputs.VmHWM-diff_percent }}%) |" >> ./output.md line $1 VmHWM
echo "| VMS | ${{ steps.compare.outputs.VmSize-base }} MB | ${{ steps.compare.outputs.VmSize-head }} MB | ${{ steps.compare.outputs.VmSize-diff }} MB (${{ steps.compare.outputs.VmSize-diff_percent }}%) |" >> ./output.md line $1 VmSize
}
echo "### Before GC" >> ./output.md
table beforeGc
echo >> ./output.md
echo "### After GC" >> ./output.md
table afterGc
echo >> ./output.md echo >> ./output.md
# Determine if this is a significant change (more than 5% increase) # Determine if this is a significant change (more than 5% increase)
if [ "$(echo "${{ steps.compare.outputs.VmRSS-diff_percent }} > 5" | bc)" -eq 1 ]; then if [ "$(echo "${{ steps.compare.outputs.res }}" | jq -r '.afterGc.VmRSS.diff_percent | tonumber > 5')" = "true" ]; then
echo "⚠️ **Warning**: Memory usage has increased by more than 5%. Please verify this is not an unintended change." >> ./output.md echo "⚠️ **Warning**: Memory usage has increased by more than 5%. Please verify this is not an unintended change." >> ./output.md
echo >> ./output.md echo >> ./output.md
fi fi

View File

@ -60,9 +60,9 @@ async function measureMemory() {
...process.env, ...process.env,
NODE_ENV: 'production', NODE_ENV: 'production',
MK_DISABLE_CLUSTERING: '1', MK_DISABLE_CLUSTERING: '1',
MK_FORCE_GC: '1',
}, },
stdio: ['pipe', 'pipe', 'pipe', 'ipc'], stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
execArgv: [...process.execArgv, '--expose-gc'],
}); });
let serverReady = false; let serverReady = false;
@ -104,9 +104,21 @@ async function measureMemory() {
// Wait for memory to settle // Wait for memory to settle
await setTimeout(MEMORY_SETTLE_TIME); await setTimeout(MEMORY_SETTLE_TIME);
// Get memory usage from the server process via /proc
const pid = serverProcess.pid; const pid = serverProcess.pid;
const memoryInfo = await getMemoryUsage(pid);
const beforeGc = await getMemoryUsage(pid);
serverProcess.send('gc');
await new Promise((resolve) => {
serverProcess.once('message', (message) => {
if (message === 'gc ok') resolve();
});
});
await setTimeout(1000);
const afterGc = await getMemoryUsage(pid);
// Stop the server // Stop the server
serverProcess.kill('SIGTERM'); serverProcess.kill('SIGTERM');
@ -129,7 +141,8 @@ async function measureMemory() {
const result = { const result = {
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
memory: memoryInfo, beforeGc,
afterGc,
}; };
return result; return result;
@ -144,19 +157,23 @@ async function main() {
} }
// Calculate averages // Calculate averages
const avgMemory = structuredClone(keys); const beforeGc = structuredClone(keys);
const afterGc = structuredClone(keys);
for (const res of results) { for (const res of results) {
for (const key of Object.keys(avgMemory)) { for (const key of Object.keys(keys)) {
avgMemory[key] += res.memory[key]; beforeGc[key] += res.beforeGc[key];
afterGc[key] += res.afterGc[key];
} }
} }
for (const key of Object.keys(avgMemory)) { for (const key of Object.keys(keys)) {
avgMemory[key] = Math.round(avgMemory[key] / SAMPLE_COUNT); beforeGc[key] = Math.round(beforeGc[key] / SAMPLE_COUNT);
afterGc[key] = Math.round(afterGc[key] / SAMPLE_COUNT);
} }
const result = { const result = {
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
memory: avgMemory, beforeGc,
afterGc,
}; };
// Output as JSON to stdout // Output as JSON to stdout

View File

@ -86,9 +86,17 @@ if (!envOption.disableClustering) {
ev.mount(); ev.mount();
} }
if (envOption.forceGc && global.gc != null) { process.on('message', msg => {
if (msg === 'gc') {
if (global.gc != null) {
logger.info('Manual GC triggered');
global.gc(); global.gc();
if (process.send != null) process.send('gc ok');
} else {
logger.warn('Manual GC requested but gc is not available. Start the process with --expose-gc to enable this feature.');
} }
}
});
readyRef.value = true; readyRef.value = true;

View File

@ -11,7 +11,6 @@ const envOption = {
verbose: false, verbose: false,
withLogTime: false, withLogTime: false,
quiet: false, quiet: false,
forceGc: false,
}; };
for (const key of Object.keys(envOption) as (keyof typeof envOption)[]) { for (const key of Object.keys(envOption) as (keyof typeof envOption)[]) {