153 lines
3.9 KiB
JavaScript
153 lines
3.9 KiB
JavaScript
/*
|
|
* 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);
|
|
});
|