summaryrefslogtreecommitdiff
path: root/packages/backend/scripts
diff options
context:
space:
mode:
authorCopilot <198982749+Copilot@users.noreply.github.com>2025-12-03 16:02:49 +0900
committerGitHub <noreply@github.com>2025-12-03 16:02:49 +0900
commit0b77dc8c483ea8cbbb719679da3ca438d0d92535 (patch)
treefb99f81376a16621257063f183aa6e08ceaaae37 /packages/backend/scripts
parentadd DeepWiki badge to enable auto-refresh (diff)
downloadmisskey-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.mjs152
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);
+});