summaryrefslogtreecommitdiff
path: root/packages/backend/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/backend/scripts')
-rw-r--r--packages/backend/scripts/check_connect.js30
-rw-r--r--packages/backend/scripts/generate_api_json.js6
-rw-r--r--packages/backend/scripts/measure-memory.mjs163
-rw-r--r--packages/backend/scripts/watch.mjs2
4 files changed, 138 insertions, 63 deletions
diff --git a/packages/backend/scripts/check_connect.js b/packages/backend/scripts/check_connect.js
index 96c4549ccb..a1cb839303 100644
--- a/packages/backend/scripts/check_connect.js
+++ b/packages/backend/scripts/check_connect.js
@@ -4,8 +4,8 @@
*/
import Redis from 'ioredis';
-import { loadConfig } from '../built/config.js';
-import { createPostgresDataSource } from '../built/postgres.js';
+import { loadConfig } from '../src-js/config.js';
+import { createPostgresDataSource } from '../src-js/postgres.js';
const config = loadConfig();
@@ -16,26 +16,22 @@ async function connectToPostgres() {
}
async function connectToRedis(redisOptions) {
- return await new Promise(async (resolve, reject) => {
- const redis = new Redis({
+ let redis;
+ try {
+ redis = new Redis({
...redisOptions,
lazyConnect: true,
reconnectOnError: false,
showFriendlyErrorStack: true,
});
- redis.on('error', e => reject(e));
- try {
- await redis.connect();
- resolve();
-
- } catch (e) {
- reject(e);
-
- } finally {
- redis.disconnect(false);
- }
- });
+ await Promise.race([
+ new Promise((_, reject) => redis.on('error', e => reject(e))),
+ redis.connect(),
+ ]);
+ } finally {
+ redis.disconnect(false);
+ }
}
// If not all of these are defined, the default one gets reused.
@@ -50,7 +46,7 @@ const promises = Array
]))
.map(connectToRedis)
.concat([
- connectToPostgres()
+ connectToPostgres(),
]);
await Promise.all(promises);
diff --git a/packages/backend/scripts/generate_api_json.js b/packages/backend/scripts/generate_api_json.js
index 798e243004..237f63a4d3 100644
--- a/packages/backend/scripts/generate_api_json.js
+++ b/packages/backend/scripts/generate_api_json.js
@@ -3,8 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+import { writeFileSync, existsSync } from 'node:fs';
import { execa } from 'execa';
-import { writeFileSync, existsSync } from "node:fs";
async function main() {
if (!process.argv.includes('--no-build')) {
@@ -19,10 +19,10 @@ async function main() {
}
/** @type {import('../src/config.js')} */
- const { loadConfig } = await import('../built/config.js');
+ const { loadConfig } = await import('../src-js/config.js');
/** @type {import('../src/server/api/openapi/gen-spec.js')} */
- const { genOpenapiSpec } = await import('../built/server/api/openapi/gen-spec.js');
+ const { genOpenapiSpec } = await import('../src-js/server/api/openapi/gen-spec.js');
const config = loadConfig();
const spec = genOpenapiSpec(config, true);
diff --git a/packages/backend/scripts/measure-memory.mjs b/packages/backend/scripts/measure-memory.mjs
index 017252d7ec..3f30e24fb4 100644
--- a/packages/backend/scripts/measure-memory.mjs
+++ b/packages/backend/scripts/measure-memory.mjs
@@ -14,24 +14,56 @@ import { fork } from 'node:child_process';
import { setTimeout } from 'node:timers/promises';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
+import * as http from 'node:http';
+import * as fs from 'node:fs/promises';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
+const SAMPLE_COUNT = 3; // Number of samples to measure
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();
+const keys = {
+ VmPeak: 0,
+ VmSize: 0,
+ VmHWM: 0,
+ VmRSS: 0,
+ VmData: 0,
+ VmStk: 0,
+ VmExe: 0,
+ VmLib: 0,
+ VmPTE: 0,
+ VmSwap: 0,
+};
+
+async function getMemoryUsage(pid) {
+ const status = await fs.readFile(`/proc/${pid}/status`, 'utf-8');
+
+ const result = {};
+ for (const key of Object.keys(keys)) {
+ const match = status.match(new RegExp(`${key}:\\s+(\\d+)\\s+kB`));
+ if (match) {
+ result[key] = parseInt(match[1], 10);
+ } else {
+ throw new Error(`Failed to parse ${key} from /proc/${pid}/status`);
+ }
+ }
+ return result;
+}
+
+async function measureMemory() {
// Start the Misskey backend server using fork to enable IPC
- const serverProcess = fork(join(__dirname, '../built/boot/entry.js'), [], {
+ const serverProcess = fork(join(__dirname, '../built/boot/entry.js'), ['expose-gc'], {
cwd: join(__dirname, '..'),
env: {
...process.env,
- NODE_ENV: 'test',
+ NODE_ENV: 'production',
+ MK_DISABLE_CLUSTERING: '1',
},
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
+ execArgv: [...process.execArgv, '--expose-gc'],
});
let serverReady = false;
@@ -57,6 +89,40 @@ async function measureMemory() {
process.stderr.write(`[server error] ${err}\n`);
});
+ async function triggerGc() {
+ const ok = new Promise((resolve) => {
+ serverProcess.once('message', (message) => {
+ if (message === 'gc ok') resolve();
+ });
+ });
+
+ serverProcess.send('gc');
+
+ await ok;
+
+ await setTimeout(1000);
+ }
+
+ function createRequest() {
+ return new Promise((resolve, reject) => {
+ const req = http.request({
+ host: 'localhost',
+ port: 61812,
+ path: '/api/meta',
+ method: 'POST',
+ }, (res) => {
+ res.on('data', () => { });
+ res.on('end', () => {
+ resolve();
+ });
+ });
+ req.on('error', (err) => {
+ reject(err);
+ });
+ req.end();
+ });
+ }
+
// Wait for server to be ready or timeout
const startupStartTime = Date.now();
while (!serverReady) {
@@ -73,46 +139,23 @@ async function measureMemory() {
// 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');
+ const beforeGc = await getMemoryUsage(pid);
- // 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/);
+ await triggerGc();
- 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 afterGc = await getMemoryUsage(pid);
- 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',
- };
- }
- }
+ // create some http requests to simulate load
+ const REQUEST_COUNT = 10;
+ await Promise.all(
+ Array.from({ length: REQUEST_COUNT }).map(() => createRequest()),
+ );
+
+ await triggerGc();
+
+ const afterRequest = await getMemoryUsage(pid);
// Stop the server
serverProcess.kill('SIGTERM');
@@ -135,15 +178,51 @@ async function measureMemory() {
const result = {
timestamp: new Date().toISOString(),
- startupTimeMs: startupTime,
- memory: memoryInfo,
+ beforeGc,
+ afterGc,
+ afterRequest,
+ };
+
+ return result;
+}
+
+async function main() {
+ // 直列の方が時間的に分散されて正確そうだから直列でやる
+ const results = [];
+ for (let i = 0; i < SAMPLE_COUNT; i++) {
+ const res = await measureMemory();
+ results.push(res);
+ }
+
+ // Calculate averages
+ const beforeGc = structuredClone(keys);
+ const afterGc = structuredClone(keys);
+ const afterRequest = structuredClone(keys);
+ for (const res of results) {
+ for (const key of Object.keys(keys)) {
+ beforeGc[key] += res.beforeGc[key];
+ afterGc[key] += res.afterGc[key];
+ afterRequest[key] += res.afterRequest[key];
+ }
+ }
+ for (const key of Object.keys(keys)) {
+ beforeGc[key] = Math.round(beforeGc[key] / SAMPLE_COUNT);
+ afterGc[key] = Math.round(afterGc[key] / SAMPLE_COUNT);
+ afterRequest[key] = Math.round(afterRequest[key] / SAMPLE_COUNT);
+ }
+
+ const result = {
+ timestamp: new Date().toISOString(),
+ beforeGc,
+ afterGc,
+ afterRequest,
};
// Output as JSON to stdout
console.log(JSON.stringify(result, null, 2));
}
-measureMemory().catch((err) => {
+main().catch((err) => {
console.error(JSON.stringify({
error: err.message,
timestamp: new Date().toISOString(),
diff --git a/packages/backend/scripts/watch.mjs b/packages/backend/scripts/watch.mjs
index a0ccea3b16..9d608b233c 100644
--- a/packages/backend/scripts/watch.mjs
+++ b/packages/backend/scripts/watch.mjs
@@ -21,7 +21,7 @@ import { execa } from 'execa';
});
}, 3000);
- execa('tsc', ['-w', '-p', 'tsconfig.json'], {
+ execa('tsgo', ['-w', '-p', 'tsconfig.json'], {
stdout: process.stdout,
stderr: process.stderr,
});