# this name is used in report-backend-memory.yml so be careful when change name name: Get backend memory usage on: pull_request: branches: - master - develop paths: - packages/backend/** - packages/misskey-js/** - .github/workflows/get-backend-memory.yml jobs: get-memory-usage: runs-on: ubuntu-latest permissions: contents: read strategy: matrix: memory-json-name: [memory-base.json, memory-head.json] include: - memory-json-name: memory-base.json ref: ${{ github.base_ref }} - memory-json-name: memory-head.json ref: refs/pull/${{ github.event.number }}/merge services: postgres: image: postgres:18 ports: - 54312:5432 env: POSTGRES_DB: test-misskey POSTGRES_HOST_AUTH_METHOD: trust redis: image: redis:7 ports: - 56312:6379 steps: - uses: actions/checkout@v4.3.0 with: ref: ${{ matrix.ref }} submodules: true - name: Setup pnpm uses: pnpm/action-setup@v4.2.0 - name: Use Node.js uses: actions/setup-node@v4.4.0 with: node-version-file: '.node-version' cache: 'pnpm' - run: pnpm i --frozen-lockfile - name: Check pnpm-lock.yaml run: git diff --exit-code pnpm-lock.yaml - name: Copy Configure run: cp .github/misskey/test.yml .config/default.yml - name: Build run: pnpm build - name: Run migrations run: pnpm --filter backend migrate - name: Measure memory usage run: | # Inline script to start the server and measure memory usage # This is inlined to work with both base and head refs node --input-type=module -e ' import { fork } from "node:child_process"; import { setTimeout } from "node:timers/promises"; import { readFile } from "node:fs/promises"; import { execSync } from "node:child_process"; const STARTUP_TIMEOUT = 120000; const MEMORY_SETTLE_TIME = 10000; async function measureMemory() { const serverProcess = fork("./packages/backend/built/boot/entry.js", [], { cwd: process.cwd(), env: { ...process.env, NODE_ENV: "test" }, stdio: ["pipe", "pipe", "pipe", "ipc"], }); let serverReady = false; serverProcess.on("message", (msg) => { if (msg === "ok") serverReady = true; }); serverProcess.stdout?.on("data", (d) => process.stderr.write("[server] " + d)); serverProcess.stderr?.on("data", (d) => process.stderr.write("[server] " + d)); serverProcess.on("error", (e) => process.stderr.write("[error] " + e + "\n")); const start = Date.now(); while (!serverReady) { if (Date.now() - start > STARTUP_TIMEOUT) { serverProcess.kill("SIGTERM"); throw new Error("Server startup timeout"); } await setTimeout(100); } const startupTime = Date.now() - start; process.stderr.write("Server started in " + startupTime + "ms\n"); await setTimeout(MEMORY_SETTLE_TIME); const pid = serverProcess.pid; let memoryInfo; try { const status = await readFile("/proc/" + pid + "/status", "utf-8"); const rss = status.match(/VmRSS:\s+(\d+)\s+kB/); memoryInfo = { rss: rss ? parseInt(rss[1], 10) * 1024 : null }; } catch { try { const ps = execSync("ps -o rss= -p " + pid, { encoding: "utf-8" }); memoryInfo = { rss: parseInt(ps.trim(), 10) * 1024 }; } catch { memoryInfo = { rss: null, error: "Could not measure memory" }; } } serverProcess.kill("SIGTERM"); let exited = false; await new Promise((resolve) => { serverProcess.on("exit", () => { exited = true; resolve(); }); setTimeout(10000).then(() => { if (!exited) serverProcess.kill("SIGKILL"); resolve(); }); }); console.log(JSON.stringify({ timestamp: new Date().toISOString(), startupTimeMs: startupTime, memory: memoryInfo }, null, 2)); } measureMemory().catch((e) => { console.error(JSON.stringify({ error: e.message })); process.exit(1); }); ' > ${{ matrix.memory-json-name }} - name: Upload Artifact uses: actions/upload-artifact@v4 with: name: memory-artifact-${{ matrix.memory-json-name }} path: ${{ matrix.memory-json-name }} save-pr-number: runs-on: ubuntu-latest permissions: {} steps: - name: Save PR number env: PR_NUMBER: ${{ github.event.number }} run: | echo "$PR_NUMBER" > ./pr_number - uses: actions/upload-artifact@v4 with: name: memory-artifact-pr-number path: pr_number