diff options
| author | Copilot <198982749+Copilot@users.noreply.github.com> | 2025-12-03 16:02:49 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-12-03 16:02:49 +0900 |
| commit | 0b77dc8c483ea8cbbb719679da3ca438d0d92535 (patch) | |
| tree | fb99f81376a16621257063f183aa6e08ceaaae37 /packages/backend/scripts | |
| parent | add DeepWiki badge to enable auto-refresh (diff) | |
| download | misskey-0b77dc8c483ea8cbbb719679da3ca438d0d92535.tar.gz misskey-0b77dc8c483ea8cbbb719679da3ca438d0d92535.tar.bz2 misskey-0b77dc8c483ea8cbbb719679da3ca438d0d92535.zip | |
Add backend memory usage comparison action for PRs (#16926)
* Initial plan
* Add backend memory usage comparison action
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
* Fix deprecated serverProcess.killed usage
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
* Add explicit permissions to save-pr-number job
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
* Change PR comment text from Japanese to English
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
* Inline memory measurement script to fix base ref compatibility
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
* Revert "Inline memory measurement script to fix base ref compatibility"
This reverts commit 6f76a121efd450c257167cce6e298c59936f4e37.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Diffstat (limited to 'packages/backend/scripts')
| -rw-r--r-- | packages/backend/scripts/measure-memory.mjs | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/packages/backend/scripts/measure-memory.mjs b/packages/backend/scripts/measure-memory.mjs new file mode 100644 index 0000000000..017252d7ec --- /dev/null +++ b/packages/backend/scripts/measure-memory.mjs @@ -0,0 +1,152 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +/** + * This script starts the Misskey backend server, waits for it to be ready, + * measures memory usage, and outputs the result as JSON. + * + * Usage: node scripts/measure-memory.mjs + */ + +import { fork } from 'node:child_process'; +import { setTimeout } from 'node:timers/promises'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const STARTUP_TIMEOUT = 120000; // 120 seconds timeout for server startup +const MEMORY_SETTLE_TIME = 10000; // Wait 10 seconds after startup for memory to settle + +async function measureMemory() { + const startTime = Date.now(); + + // Start the Misskey backend server using fork to enable IPC + const serverProcess = fork(join(__dirname, '../built/boot/entry.js'), [], { + cwd: join(__dirname, '..'), + env: { + ...process.env, + NODE_ENV: 'test', + }, + stdio: ['pipe', 'pipe', 'pipe', 'ipc'], + }); + + let serverReady = false; + + // Listen for the 'ok' message from the server indicating it's ready + serverProcess.on('message', (message) => { + if (message === 'ok') { + serverReady = true; + } + }); + + // Handle server output + serverProcess.stdout?.on('data', (data) => { + process.stderr.write(`[server stdout] ${data}`); + }); + + serverProcess.stderr?.on('data', (data) => { + process.stderr.write(`[server stderr] ${data}`); + }); + + // Handle server error + serverProcess.on('error', (err) => { + process.stderr.write(`[server error] ${err}\n`); + }); + + // Wait for server to be ready or timeout + const startupStartTime = Date.now(); + while (!serverReady) { + if (Date.now() - startupStartTime > STARTUP_TIMEOUT) { + serverProcess.kill('SIGTERM'); + throw new Error('Server startup timeout'); + } + await setTimeout(100); + } + + const startupTime = Date.now() - startupStartTime; + process.stderr.write(`Server started in ${startupTime}ms\n`); + + // Wait for memory to settle + await setTimeout(MEMORY_SETTLE_TIME); + + // Get memory usage from the server process via /proc + const pid = serverProcess.pid; + let memoryInfo; + + try { + const fs = await import('node:fs/promises'); + + // Read /proc/[pid]/status for detailed memory info + const status = await fs.readFile(`/proc/${pid}/status`, 'utf-8'); + const vmRssMatch = status.match(/VmRSS:\s+(\d+)\s+kB/); + const vmDataMatch = status.match(/VmData:\s+(\d+)\s+kB/); + const vmSizeMatch = status.match(/VmSize:\s+(\d+)\s+kB/); + + memoryInfo = { + rss: vmRssMatch ? parseInt(vmRssMatch[1], 10) * 1024 : null, + heapUsed: vmDataMatch ? parseInt(vmDataMatch[1], 10) * 1024 : null, + vmSize: vmSizeMatch ? parseInt(vmSizeMatch[1], 10) * 1024 : null, + }; + } catch (err) { + // Fallback: use ps command + process.stderr.write(`Warning: Could not read /proc/${pid}/status: ${err}\n`); + + const { execSync } = await import('node:child_process'); + try { + const ps = execSync(`ps -o rss= -p ${pid}`, { encoding: 'utf-8' }); + const rssKb = parseInt(ps.trim(), 10); + memoryInfo = { + rss: rssKb * 1024, + heapUsed: null, + vmSize: null, + }; + } catch { + memoryInfo = { + rss: null, + heapUsed: null, + vmSize: null, + error: 'Could not measure memory', + }; + } + } + + // Stop the server + serverProcess.kill('SIGTERM'); + + // Wait for process to exit + let exited = false; + await new Promise((resolve) => { + serverProcess.on('exit', () => { + exited = true; + resolve(undefined); + }); + // Force kill after 10 seconds if not exited + setTimeout(10000).then(() => { + if (!exited) { + serverProcess.kill('SIGKILL'); + } + resolve(undefined); + }); + }); + + const result = { + timestamp: new Date().toISOString(), + startupTimeMs: startupTime, + memory: memoryInfo, + }; + + // Output as JSON to stdout + console.log(JSON.stringify(result, null, 2)); +} + +measureMemory().catch((err) => { + console.error(JSON.stringify({ + error: err.message, + timestamp: new Date().toISOString(), + })); + process.exit(1); +}); |