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:
parent
f744b5711f
commit
2fa6ecc7ef
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)[]) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue