From 40d8524f823b533634a23e66c2cd7a616b9f5666 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Mon, 13 Apr 2026 14:23:35 +0200 Subject: [PATCH] chore(ci): Remove node-overhead GitHub Action The node overhead check was too flaky to be reliable and was mostly ignored in practice. Remove the entire `node-overhead-gh-action` dev-package and its CI job. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build.yml | 34 --- .../node-overhead-gh-action/.oxlintrc.json | 7 - .../node-overhead-gh-action/README.md | 3 - .../node-overhead-gh-action/action.yml | 17 -- .../node-overhead-gh-action/db/init/init.sql | 25 -- .../docker-compose.yml | 12 - .../node-overhead-gh-action/index.mjs | 236 ----------------- .../lib/getArtifactsForBranchAndWorkflow.mjs | 122 --------- .../lib/getOverheadMeasurements.mjs | 250 ------------------ .../lib/markdown-table-formatter.mjs | 112 -------- .../node-overhead-gh-action/package.json | 45 ---- .../node-overhead-gh-action/run-local.mjs | 11 - .../node-overhead-gh-action/src/app.mjs | 58 ---- .../src/instrument-error-only.mjs | 5 - .../src/instrument.mjs | 6 - package.json | 1 - 16 files changed, 944 deletions(-) delete mode 100644 dev-packages/node-overhead-gh-action/.oxlintrc.json delete mode 100644 dev-packages/node-overhead-gh-action/README.md delete mode 100644 dev-packages/node-overhead-gh-action/action.yml delete mode 100644 dev-packages/node-overhead-gh-action/db/init/init.sql delete mode 100644 dev-packages/node-overhead-gh-action/docker-compose.yml delete mode 100644 dev-packages/node-overhead-gh-action/index.mjs delete mode 100644 dev-packages/node-overhead-gh-action/lib/getArtifactsForBranchAndWorkflow.mjs delete mode 100644 dev-packages/node-overhead-gh-action/lib/getOverheadMeasurements.mjs delete mode 100644 dev-packages/node-overhead-gh-action/lib/markdown-table-formatter.mjs delete mode 100644 dev-packages/node-overhead-gh-action/package.json delete mode 100644 dev-packages/node-overhead-gh-action/run-local.mjs delete mode 100644 dev-packages/node-overhead-gh-action/src/app.mjs delete mode 100644 dev-packages/node-overhead-gh-action/src/instrument-error-only.mjs delete mode 100644 dev-packages/node-overhead-gh-action/src/instrument.mjs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1bf07adf661c..db623c3cec93 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -152,9 +152,6 @@ jobs: changed_node: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry/node') }} - changed_node_overhead_action: - ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, - '@sentry-internal/node-overhead-gh-action') }} changed_deno: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry/deno') }} @@ -208,37 +205,6 @@ jobs: # Only run comparison against develop if this is a PR comparison_branch: ${{ (github.event_name == 'pull_request' && github.base_ref) || ''}} - job_node_overhead_check: - name: Node Overhead Check - needs: [job_get_metadata, job_build] - timeout-minutes: 15 - runs-on: ubuntu-24.04 - if: - (needs.job_build.outputs.changed_node == 'true' && github.event_name == 'pull_request') || - (needs.job_build.outputs.changed_node_overhead_action == 'true' && github.event_name == 'pull_request') || - needs.job_get_metadata.outputs.is_base_branch == 'true' || needs.job_get_metadata.outputs.is_release == 'true' - steps: - - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) - uses: actions/checkout@v6 - with: - ref: ${{ env.HEAD_COMMIT }} - - name: Set up Node - uses: actions/setup-node@v6 - with: - node-version-file: 'package.json' - - name: Restore caches - uses: ./.github/actions/restore-cache - with: - dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - - name: Check node overhead - uses: ./dev-packages/node-overhead-gh-action - env: - DEBUG: '1' - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - # Only run comparison against develop if this is a PR - comparison_branch: ${{ (github.event_name == 'pull_request' && github.base_ref) || ''}} - job_lint: name: Lint # Even though the linter only checks source code, not built code, it needs the built code in order check that all diff --git a/dev-packages/node-overhead-gh-action/.oxlintrc.json b/dev-packages/node-overhead-gh-action/.oxlintrc.json deleted file mode 100644 index 5bffa72a1a08..000000000000 --- a/dev-packages/node-overhead-gh-action/.oxlintrc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "../../node_modules/oxlint/configuration_schema.json", - "extends": ["../.oxlintrc.json"], - "env": { - "node": true - } -} diff --git a/dev-packages/node-overhead-gh-action/README.md b/dev-packages/node-overhead-gh-action/README.md deleted file mode 100644 index 1759ab7bd7c3..000000000000 --- a/dev-packages/node-overhead-gh-action/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# node-overhead-gh-action - -Capture the overhead of Sentry in a node app. diff --git a/dev-packages/node-overhead-gh-action/action.yml b/dev-packages/node-overhead-gh-action/action.yml deleted file mode 100644 index e90aef2e4342..000000000000 --- a/dev-packages/node-overhead-gh-action/action.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: 'node-overhead-gh-action' -description: 'Run node overhead comparison' -inputs: - github_token: - required: true - description: 'a github access token' - comparison_branch: - required: false - default: '' - description: 'If set, compare the current branch with this branch' - threshold: - required: false - default: '3' - description: 'The percentage threshold for size changes before posting a comment' -runs: - using: 'node24' - main: 'index.mjs' diff --git a/dev-packages/node-overhead-gh-action/db/init/init.sql b/dev-packages/node-overhead-gh-action/db/init/init.sql deleted file mode 100644 index 44071266aab5..000000000000 --- a/dev-packages/node-overhead-gh-action/db/init/init.sql +++ /dev/null @@ -1,25 +0,0 @@ -CREATE DATABASE mydb; -USE mydb - --- SQL script to create the 'users' table and insert initial data. - --- 1. Create the 'users' table --- This table stores basic user information. --- 'id' is the primary key and will automatically increment for each new record. --- 'name' stores the user's name, up to 255 characters. --- 'age' stores the user's age as an integer. - -CREATE TABLE users ( - id INT PRIMARY KEY AUTO_INCREMENT, - name VARCHAR(255) NOT NULL, - age INT -); - --- 2. Insert 5 rows into the 'users' table --- Populating the table with some sample data. - -INSERT INTO users (name, age) VALUES ('Alice Johnson', 28); -INSERT INTO users (name, age) VALUES ('Bob Smith', 45); -INSERT INTO users (name, age) VALUES ('Charlie Brown', 32); -INSERT INTO users (name, age) VALUES ('Diana Prince', 25); -INSERT INTO users (name, age) VALUES ('Ethan Hunt', 41); diff --git a/dev-packages/node-overhead-gh-action/docker-compose.yml b/dev-packages/node-overhead-gh-action/docker-compose.yml deleted file mode 100644 index a929dd5c5c88..000000000000 --- a/dev-packages/node-overhead-gh-action/docker-compose.yml +++ /dev/null @@ -1,12 +0,0 @@ -services: - db: - image: mysql:8 - restart: always - container_name: node-overhead-gh-action-mysql - ports: - - '3306:3306' - environment: - MYSQL_ROOT_PASSWORD: password - volumes: - # - ./db/data:/var/lib/mysql - - ./db/init:/docker-entrypoint-initdb.d/:ro diff --git a/dev-packages/node-overhead-gh-action/index.mjs b/dev-packages/node-overhead-gh-action/index.mjs deleted file mode 100644 index 8c3e2c56873b..000000000000 --- a/dev-packages/node-overhead-gh-action/index.mjs +++ /dev/null @@ -1,236 +0,0 @@ -import { promises as fs } from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { DefaultArtifactClient } from '@actions/artifact'; -import * as core from '@actions/core'; -import { exec } from '@actions/exec'; -import { context, getOctokit } from '@actions/github'; -import * as glob from '@actions/glob'; -import * as io from '@actions/io'; -import { markdownTable } from 'markdown-table'; -import { getArtifactsForBranchAndWorkflow } from './lib/getArtifactsForBranchAndWorkflow.mjs'; -import { getAveragedOverheadMeasurements } from './lib/getOverheadMeasurements.mjs'; -import { formatResults, hasChanges } from './lib/markdown-table-formatter.mjs'; - -const NODE_OVERHEAD_HEADING = '## node-overhead report 🧳'; -const ARTIFACT_NAME = 'node-overhead-action'; -const RESULTS_FILE = 'node-overhead-results.json'; - -function getResultsFilePath() { - const __dirname = path.dirname(fileURLToPath(import.meta.url)); - return path.resolve(__dirname, RESULTS_FILE); -} - -const { getInput, setFailed } = core; - -async function fetchPreviousComment(octokit, repo, pr) { - const { data: commentList } = await octokit.rest.issues.listComments({ - ...repo, - issue_number: pr.number, - }); - - return commentList.find(comment => comment.body.startsWith(NODE_OVERHEAD_HEADING)); -} - -async function run() { - const __dirname = path.dirname(fileURLToPath(import.meta.url)); - - try { - const { payload, repo } = context; - const pr = payload.pull_request; - - const comparisonBranch = getInput('comparison_branch'); - const githubToken = getInput('github_token'); - const threshold = getInput('threshold') || 1; - - if (comparisonBranch && !pr) { - throw new Error('No PR found. Only pull_request workflows are supported.'); - } - - const octokit = getOctokit(githubToken); - const resultsFilePath = getResultsFilePath(); - - // If we have no comparison branch, we just run overhead check & store the result as artifact - if (!comparisonBranch) { - return runNodeOverheadOnComparisonBranch(); - } - - // Else, we run overhead check for the current branch, AND fetch it for the comparison branch - let base; - let current; - let baseIsNotLatest = false; - let baseWorkflowRun; - - try { - const workflowName = `${process.env.GITHUB_WORKFLOW || ''}`; - core.startGroup(`getArtifactsForBranchAndWorkflow - workflow:"${workflowName}", branch:"${comparisonBranch}"`); - const artifacts = await getArtifactsForBranchAndWorkflow(octokit, { - ...repo, - artifactName: ARTIFACT_NAME, - branch: comparisonBranch, - workflowName, - }); - core.endGroup(); - - if (!artifacts) { - throw new Error('No artifacts found'); - } - - baseWorkflowRun = artifacts.workflowRun; - - await downloadOtherWorkflowArtifact(octokit, { - ...repo, - artifactName: ARTIFACT_NAME, - artifactId: artifacts.artifact.id, - downloadPath: __dirname, - }); - - base = JSON.parse(await fs.readFile(resultsFilePath, { encoding: 'utf8' })); - - if (!artifacts.isLatest) { - baseIsNotLatest = true; - core.info('Base artifact is not the latest one. This may lead to incorrect results.'); - } - } catch (error) { - core.startGroup('Warning, unable to find base results'); - core.error(error); - core.endGroup(); - } - - core.startGroup('Getting current overhead measurements'); - try { - current = await getAveragedOverheadMeasurements(); - } catch (error) { - core.error('Error getting current overhead measurements'); - core.endGroup(); - throw error; - } - core.debug(`Current overhead measurements: ${JSON.stringify(current, null, 2)}`); - core.endGroup(); - - const thresholdNumber = Number(threshold); - - const nodeOverheadComment = await fetchPreviousComment(octokit, repo, pr); - - if (nodeOverheadComment) { - core.debug('Found existing node overhead comment, updating it instead of creating a new one...'); - } - - const shouldComment = isNaN(thresholdNumber) || hasChanges(base, current, thresholdNumber) || nodeOverheadComment; - - if (shouldComment) { - const bodyParts = [ - NODE_OVERHEAD_HEADING, - 'Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.', - ]; - - if (baseIsNotLatest) { - bodyParts.push( - '⚠️ **Warning:** Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.', - ); - } - try { - bodyParts.push(markdownTable(formatResults(base, current))); - } catch (error) { - core.error('Error generating markdown table'); - throw error; - } - - if (baseWorkflowRun) { - bodyParts.push(''); - bodyParts.push(`[View base workflow run](${baseWorkflowRun.html_url})`); - } - - const body = bodyParts.join('\r\n'); - - try { - if (!nodeOverheadComment) { - await octokit.rest.issues.createComment({ - ...repo, - issue_number: pr.number, - body, - }); - } else { - await octokit.rest.issues.updateComment({ - ...repo, - comment_id: nodeOverheadComment.id, - body, - }); - } - } catch { - core.error( - "Error updating comment. This can happen for PR's originating from a fork without write permissions.", - ); - } - } else { - core.debug('Skipping comment because there are no changes.'); - } - } catch (error) { - core.error(error); - setFailed(error.message); - } -} - -async function runNodeOverheadOnComparisonBranch() { - const __dirname = path.dirname(fileURLToPath(import.meta.url)); - const resultsFilePath = getResultsFilePath(); - - const artifactClient = new DefaultArtifactClient(); - - const result = await getAveragedOverheadMeasurements(); - - try { - await fs.writeFile(resultsFilePath, JSON.stringify(result), 'utf8'); - } catch (error) { - core.error('Error parsing node overhead output. The output should be a json.'); - throw error; - } - - const globber = await glob.create(resultsFilePath, { - followSymbolicLinks: false, - }); - const files = await globber.glob(); - - await artifactClient.uploadArtifact(ARTIFACT_NAME, files, __dirname); -} - -run(); - -/** - * Use GitHub API to fetch artifact download url, then - * download and extract artifact to `downloadPath` - */ -async function downloadOtherWorkflowArtifact(octokit, { owner, repo, artifactId, artifactName, downloadPath }) { - const artifact = await octokit.rest.actions.downloadArtifact({ - owner, - repo, - artifact_id: artifactId, - archive_format: 'zip', - }); - - // Make sure output path exists - try { - await io.mkdirP(downloadPath); - } catch { - // ignore errors - } - - const downloadFile = path.resolve(downloadPath, `${artifactName}.zip`); - - await exec('wget', [ - '-nv', - '--retry-connrefused', - '--waitretry=1', - '--read-timeout=20', - '--timeout=15', - '-t', - '0', - '-O', - downloadFile, - artifact.url, - ]); - - await exec('unzip', ['-q', '-d', downloadPath, downloadFile], { - silent: true, - }); -} diff --git a/dev-packages/node-overhead-gh-action/lib/getArtifactsForBranchAndWorkflow.mjs b/dev-packages/node-overhead-gh-action/lib/getArtifactsForBranchAndWorkflow.mjs deleted file mode 100644 index ca7e4e20e9e5..000000000000 --- a/dev-packages/node-overhead-gh-action/lib/getArtifactsForBranchAndWorkflow.mjs +++ /dev/null @@ -1,122 +0,0 @@ -import * as core from '@actions/core'; - -// max pages of workflows to pagination through -const DEFAULT_MAX_PAGES = 50; -// max results per page -const DEFAULT_PAGE_LIMIT = 10; - -/** - * Fetch artifacts from a workflow run from a branch - * - * This is a bit hacky since GitHub Actions currently does not directly - * support downloading artifacts from other workflows - */ -export async function getArtifactsForBranchAndWorkflow(octokit, { owner, repo, workflowName, branch, artifactName }) { - let repositoryWorkflow = null; - - // For debugging - const allWorkflows = []; - - // - // Find workflow id from `workflowName` - // - for await (const response of octokit.paginate.iterator(octokit.rest.actions.listRepoWorkflows, { - owner, - repo, - })) { - const targetWorkflow = response.data.find(({ name }) => name === workflowName); - - allWorkflows.push(...response.data.map(({ name }) => name)); - - // If not found in responses, continue to search on next page - if (!targetWorkflow) { - continue; - } - - repositoryWorkflow = targetWorkflow; - break; - } - - if (!repositoryWorkflow) { - core.info( - `Unable to find workflow with name "${workflowName}" in the repository. Found workflows: ${allWorkflows.join( - ', ', - )}`, - ); - return null; - } - - const workflow_id = repositoryWorkflow.id; - - let currentPage = 0; - let latestWorkflowRun = null; - - for await (const response of octokit.paginate.iterator(octokit.rest.actions.listWorkflowRuns, { - owner, - repo, - workflow_id, - branch, - per_page: DEFAULT_PAGE_LIMIT, - event: 'push', - })) { - if (!response.data.length) { - core.warning(`Workflow ${workflow_id} not found in branch ${branch}`); - return null; - } - - // Do not allow downloading artifacts from a fork. - const filtered = response.data.filter(workflowRun => workflowRun.head_repository.full_name === `${owner}/${repo}`); - - // Sort to ensure the latest workflow run is the first - filtered.sort((a, b) => { - return new Date(b.created_at).getTime() - new Date(a.created_at).getTime(); - }); - - // Store the first workflow run, to determine if this is the latest one... - if (!latestWorkflowRun) { - latestWorkflowRun = filtered[0]; - } - - // Search through workflow artifacts until we find a workflow run w/ artifact name that we are looking for - for (const workflowRun of filtered) { - core.info(`Checking artifacts for workflow run: ${workflowRun.html_url}`); - - const { - data: { artifacts }, - } = await octokit.rest.actions.listWorkflowRunArtifacts({ - owner, - repo, - run_id: workflowRun.id, - }); - - if (!artifacts) { - core.warning( - `Unable to fetch artifacts for branch: ${branch}, workflow: ${workflow_id}, workflowRunId: ${workflowRun.id}`, - ); - } else { - const foundArtifact = artifacts.find(({ name }) => name === artifactName); - if (foundArtifact) { - core.info(`Found suitable artifact: ${foundArtifact.url}`); - return { - artifact: foundArtifact, - workflowRun, - isLatest: latestWorkflowRun.id === workflowRun.id, - }; - } else { - core.info(`No artifact found for ${artifactName}, trying next workflow run...`); - } - } - } - - if (currentPage > DEFAULT_MAX_PAGES) { - core.warning(`Workflow ${workflow_id} not found in branch: ${branch}`); - return null; - } - - currentPage++; - } - - core.warning(`Artifact not found: ${artifactName}`); - core.endGroup(); - return null; -} diff --git a/dev-packages/node-overhead-gh-action/lib/getOverheadMeasurements.mjs b/dev-packages/node-overhead-gh-action/lib/getOverheadMeasurements.mjs deleted file mode 100644 index 266b62cd7742..000000000000 --- a/dev-packages/node-overhead-gh-action/lib/getOverheadMeasurements.mjs +++ /dev/null @@ -1,250 +0,0 @@ -import { execSync, spawn } from 'child_process'; -import { dirname, join } from 'path'; -import treeKill from 'tree-kill'; -import { fileURLToPath } from 'url'; - -const DEBUG = !!process.env.DEBUG; - -const packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..'); - -async function getMeasurements(instrumentFile, autocannonCommand = 'yarn test:get') { - const args = [join(packageRoot, './src/app.mjs')]; - - if (instrumentFile) { - args.unshift('--import', join(packageRoot, instrumentFile)); - } - - const cmd = `node ${args.join(' ')}`; - - log('--------------------------------'); - log(`Getting measurements for "${cmd}"`); - - const killAppProcess = await startAppProcess(cmd); - - log('Example app listening, running autocannon...'); - - try { - const result = await startAutocannonProcess(autocannonCommand); - await killAppProcess(); - return result; - } catch (error) { - //oxlint-disable-next-line restrict-template-expressions - log(`Error running autocannon: ${error}`); - await killAppProcess(); - throw error; - } -} - -async function startAppProcess(cmd) { - const appProcess = spawn(cmd, { shell: true }); - - log('Child process started, waiting for example app...'); - - // Promise to keep track of the app process being closed - let resolveAppClose, rejectAppClose; - const appClosePromise = new Promise((resolve, reject) => { - resolveAppClose = resolve; - rejectAppClose = reject; - }); - - appProcess.on('close', code => { - if (code && code !== 0) { - rejectAppClose(new Error(`App process exited with code ${code}`)); - } else { - resolveAppClose(); - } - }); - - await new Promise((resolve, reject) => { - appProcess.stdout.on('data', data => { - log(`appProcess: ${data}`); - if (`${data}`.includes('Example app listening on port')) { - resolve(); - } - }); - - appProcess.stderr.on('data', data => { - log(`appProcess stderr: ${data}`); - killProcess(appProcess); - reject(data); - }); - }); - - return async () => { - log('Killing app process...'); - appProcess.stdin.end(); - appProcess.stdout.end(); - appProcess.stderr.end(); - - await killProcess(appProcess); - await appClosePromise; - log('App process killed'); - }; -} - -async function startAutocannonProcess(autocannonCommand) { - const autocannon = spawn(autocannonCommand, { - shell: true, - cwd: packageRoot, - }); - - let lastJson = undefined; - autocannon.stdout.on('data', data => { - log(`autocannon: ${data}`); - try { - lastJson = JSON.parse(data); - } catch { - // do nothing - } - }); - - return new Promise((resolve, reject) => { - autocannon.stderr.on('data', data => { - log(`autocannon stderr: ${data}`); - lastJson = undefined; - killProcess(autocannon); - }); - - autocannon.on('close', code => { - log(`autocannon closed with code ${code}`); - log(`Average requests: ${lastJson?.requests.average}`); - - if ((code && code !== 0) || !lastJson?.requests.average) { - reject(new Error(`Autocannon process exited with code ${code}`)); - } else { - resolve(Math.floor(lastJson.requests.average)); - } - }); - }); -} - -function startDb() { - const closeDb = () => { - execSync('yarn db:down', { - shell: true, - cwd: packageRoot, - }); - }; - - // Ensure eventually open DB is closed fist - closeDb(); - - return new Promise((resolve, reject) => { - const child = spawn('yarn db:up', { - shell: true, - cwd: packageRoot, - }); - - const timeout = setTimeout(() => { - closeDb(); - reject(new Error('Timed out waiting for docker-compose')); - }, 60000); - - const readyMatch = 'port: 3306'; - - function newData(data) { - const text = data.toString('utf8'); - log(text); - - if (text.includes(readyMatch)) { - child.stdout.removeAllListeners(); - child.stderr.removeAllListeners(); - clearTimeout(timeout); - resolve(closeDb); - } - } - - child.stdout.on('data', newData); - child.stderr.on('data', newData); - }); -} - -async function getOverheadMeasurements() { - const GET = { - baseline: await getMeasurements(undefined, 'yarn test:get'), - withInstrument: await getMeasurements('./src/instrument.mjs', 'yarn test:get'), - withInstrumentErrorOnly: await getMeasurements('./src/instrument-error-only.mjs', 'yarn test:get'), - }; - - const POST = { - baseline: await getMeasurements(undefined, 'yarn test:post'), - withInstrument: await getMeasurements('./src/instrument.mjs', 'yarn test:post'), - withInstrumentErrorOnly: await getMeasurements('./src/instrument-error-only.mjs', 'yarn test:post'), - }; - - const MYSQL = { - baseline: await getMeasurements(undefined, 'yarn test:mysql'), - withInstrument: await getMeasurements('./src/instrument.mjs', 'yarn test:mysql'), - withInstrumentErrorOnly: await getMeasurements('./src/instrument-error-only.mjs', 'yarn test:mysql'), - }; - - return { - GET, - POST, - MYSQL, - }; -} - -export async function getAveragedOverheadMeasurements() { - const closeDb = await startDb(); - const repeat = process.env.REPEAT ? parseInt(process.env.REPEAT) : 1; - - const results = []; - for (let i = 0; i < repeat; i++) { - const result = await getOverheadMeasurements(); - results.push(result); - } - - closeDb(); - - // Calculate averages for each scenario - const averaged = { - GET: { - baseline: Math.floor(results.reduce((sum, r) => sum + r.GET.baseline, 0) / results.length), - withInstrument: Math.floor(results.reduce((sum, r) => sum + r.GET.withInstrument, 0) / results.length), - withInstrumentErrorOnly: Math.floor( - results.reduce((sum, r) => sum + r.GET.withInstrumentErrorOnly, 0) / results.length, - ), - }, - POST: { - baseline: Math.floor(results.reduce((sum, r) => sum + r.POST.baseline, 0) / results.length), - withInstrument: Math.floor(results.reduce((sum, r) => sum + r.POST.withInstrument, 0) / results.length), - withInstrumentErrorOnly: Math.floor( - results.reduce((sum, r) => sum + r.POST.withInstrumentErrorOnly, 0) / results.length, - ), - }, - MYSQL: { - baseline: Math.floor(results.reduce((sum, r) => sum + r.MYSQL.baseline, 0) / results.length), - withInstrument: Math.floor(results.reduce((sum, r) => sum + r.MYSQL.withInstrument, 0) / results.length), - withInstrumentErrorOnly: Math.floor( - results.reduce((sum, r) => sum + r.MYSQL.withInstrumentErrorOnly, 0) / results.length, - ), - }, - }; - - return averaged; -} - -function log(message) { - if (DEBUG) { - // eslint-disable-next-line no-console - console.log(message); - } -} - -function killProcess(process) { - return new Promise(resolve => { - const pid = process.pid; - - if (!pid) { - log('Process has no PID, fallback killing process...'); - process.kill(); - resolve(); - return; - } - - treeKill(pid, () => { - resolve(); - }); - }); -} diff --git a/dev-packages/node-overhead-gh-action/lib/markdown-table-formatter.mjs b/dev-packages/node-overhead-gh-action/lib/markdown-table-formatter.mjs deleted file mode 100644 index 3119d6ad0edd..000000000000 --- a/dev-packages/node-overhead-gh-action/lib/markdown-table-formatter.mjs +++ /dev/null @@ -1,112 +0,0 @@ -const NODE_OVERHEAD_RESULTS_HEADER = ['Scenario', 'Requests/s', '% of Baseline', 'Prev. Requests/s', 'Change %']; - -const ROUND_NUMBER_FORMATTER = new Intl.NumberFormat('en-US', { - style: 'decimal', - minimumFractionDigits: 0, - maximumFractionDigits: 0, -}); - -export function formatResults(baseScenarios, currentScenarios) { - const headers = NODE_OVERHEAD_RESULTS_HEADER; - - const scenarios = getScenarios(baseScenarios, currentScenarios); - const rows = [headers]; - - scenarios.forEach(scenario => { - const base = baseScenarios?.[scenario]; - const current = currentScenarios?.[scenario]; - const baseline = current?.baseline; - - rows.push(formatResult(`${scenario} Baseline`, base?.baseline, current?.baseline)); - rows.push(formatResult(`${scenario} With Sentry`, base?.withInstrument, current?.withInstrument, baseline)); - rows.push( - formatResult( - `${scenario} With Sentry (error only)`, - base?.withInstrumentErrorOnly, - current?.withInstrumentErrorOnly, - baseline, - ), - ); - }); - - return rows; -} -export function hasChanges(baseScenarios, currentScenarios, threshold = 0) { - if (!baseScenarios || !currentScenarios) { - return true; - } - - const names = ['baseline', 'withInstrument', 'withInstrumentErrorOnly']; - const scenarios = getScenarios(baseScenarios, currentScenarios); - - return scenarios.some(scenario => { - const base = baseScenarios?.[scenario]; - const current = currentScenarios?.[scenario]; - - return names.some(name => { - const baseResult = base[name]; - const currentResult = current[name]; - - if (!baseResult || !currentResult) { - return true; - } - - return Math.abs((currentResult - baseResult) / baseResult) * 100 > threshold; - }); - }); -} - -function formatResult(name, base, current, baseline) { - const currentValue = current ? ROUND_NUMBER_FORMATTER.format(current) : '-'; - const baseValue = base ? ROUND_NUMBER_FORMATTER.format(base) : '-'; - - return [ - name, - currentValue, - baseline != null ? formatPercentageDecrease(baseline, current) : '-', - baseValue, - formatPercentageChange(base, current), - ]; -} - -function formatPercentageChange(baseline, value) { - if (!baseline) { - return 'added'; - } - - if (!value) { - return 'removed'; - } - - const percentage = ((value - baseline) / baseline) * 100; - return formatChange(percentage); -} - -function formatPercentageDecrease(baseline, value) { - if (!baseline) { - return 'added'; - } - - if (!value) { - return 'removed'; - } - - const percentage = (value / baseline) * 100; - return `${ROUND_NUMBER_FORMATTER.format(percentage)}%`; -} - -function formatChange(value) { - if (value === 0) { - return '-'; - } - - if (value > 0) { - return `+${ROUND_NUMBER_FORMATTER.format(value)}%`; - } - - return `${ROUND_NUMBER_FORMATTER.format(value)}%`; -} - -function getScenarios(baseScenarios = {}, currentScenarios = {}) { - return Array.from(new Set([...Object.keys(baseScenarios), ...Object.keys(currentScenarios)])); -} diff --git a/dev-packages/node-overhead-gh-action/package.json b/dev-packages/node-overhead-gh-action/package.json deleted file mode 100644 index 54a10b24f543..000000000000 --- a/dev-packages/node-overhead-gh-action/package.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "@sentry-internal/node-overhead-gh-action", - "version": "10.48.0", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "private": true, - "type": "module", - "main": "index.mjs", - "scripts": { - "dev": "node ./run-local.mjs", - "start": "node ./src/app.mjs", - "start:sentry": "node --import ./src/instrument.mjs ./src/app.mjs", - "start:sentry-error-only": "node --import ./src/instrument-error-only.mjs ./src/app.mjs", - "test:get": "autocannon --json -c 100 -p 10 -d 10 -W [ -c 100 -d 5] http://localhost:3030/test-get", - "test:mysql": "autocannon --json -c 100 -p 10 -d 10 -W [ -c 100 -d 5] http://localhost:3030/test-mysql", - "test:post": "autocannon --json -m POST -b \"{\\\"data\\\":\\\"test\\\"}\" --headers \"Content-type: application/json\" -c 100 -p 10 -d 10 -W [ -c 100 -d 5] http://localhost:3030/test-post", - "clean": "rimraf -g **/node_modules", - "db:up": "docker compose up", - "db:down": "docker compose down --volumes", - "lint": "OXLINT_TSGOLINT_DANGEROUSLY_SUPPRESS_PROGRAM_DIAGNOSTICS=true oxlint . --type-aware", - "lint:fix": "OXLINT_TSGOLINT_DANGEROUSLY_SUPPRESS_PROGRAM_DIAGNOSTICS=true oxlint . --fix --type-aware" - }, - "dependencies": { - "@sentry/node": "10.48.0", - "express": "^4.21.2", - "mysql2": "^3.19.1" - }, - "devDependencies": { - "@actions/artifact": "5.0.3", - "@actions/core": "1.10.1", - "@actions/exec": "1.1.1", - "@actions/github": "^5.0.0", - "@actions/glob": "0.6.1", - "@actions/io": "1.1.3", - "autocannon": "^8.0.0", - "eslint-plugin-regexp": "^1.15.0", - "markdown-table": "3.0.3", - "tree-kill": "1.2.2" - }, - "volta": { - "extends": "../../package.json" - } -} diff --git a/dev-packages/node-overhead-gh-action/run-local.mjs b/dev-packages/node-overhead-gh-action/run-local.mjs deleted file mode 100644 index fd890d559b5b..000000000000 --- a/dev-packages/node-overhead-gh-action/run-local.mjs +++ /dev/null @@ -1,11 +0,0 @@ -import { getAveragedOverheadMeasurements } from './lib/getOverheadMeasurements.mjs'; -import { formatResults } from './lib/markdown-table-formatter.mjs'; - -async function run() { - const measurements = await getAveragedOverheadMeasurements(); - - // eslint-disable-next-line no-console - console.log(formatResults(undefined, measurements)); -} - -run(); diff --git a/dev-packages/node-overhead-gh-action/src/app.mjs b/dev-packages/node-overhead-gh-action/src/app.mjs deleted file mode 100644 index 185340837aeb..000000000000 --- a/dev-packages/node-overhead-gh-action/src/app.mjs +++ /dev/null @@ -1,58 +0,0 @@ -import * as Sentry from '@sentry/node'; -import express from 'express'; -import mysql from 'mysql2/promise'; - -const app = express(); -const port = 3030; - -const pool = mysql.createPool({ - user: 'root', - password: 'password', - host: 'localhost', - database: 'mydb', - port: 3306, - waitForConnections: true, - connectionLimit: 10, - maxIdle: 10, // max idle connections, the default value is the same as `connectionLimit` - idleTimeout: 60000, // idle connections timeout, in milliseconds, the default value 60000 - queueLimit: 0, - enableKeepAlive: true, - keepAliveInitialDelay: 0, -}); - -app.use(express.json()); - -app.get('/test-get', function (req, res) { - res.send({ version: 'v1' }); -}); - -app.post('/test-post', function (req, res) { - const body = req.body; - res.send(generateResponse(body)); -}); - -app.get('/test-mysql', function (_req, res) { - pool.query('SELECT * from users').then(([users]) => { - res.send({ version: 'v1', users }); - }); -}); - -Sentry.setupExpressErrorHandler(app); - -app.listen(port, () => { - // eslint-disable-next-line no-console - console.log(`Example app listening on port ${port}`); -}); - -// This is complicated on purpose to simulate a real-world response -function generateResponse(body) { - const bodyStr = JSON.stringify(body); - const RES_BODY_SIZE = 10000; - - const bodyLen = bodyStr.length; - let resBody = ''; - for (let i = 0; i < RES_BODY_SIZE; i++) { - resBody += `${i}${bodyStr[i % bodyLen]}-`; - } - return { version: 'v1', length: bodyLen, resBody }; -} diff --git a/dev-packages/node-overhead-gh-action/src/instrument-error-only.mjs b/dev-packages/node-overhead-gh-action/src/instrument-error-only.mjs deleted file mode 100644 index 6476a071226a..000000000000 --- a/dev-packages/node-overhead-gh-action/src/instrument-error-only.mjs +++ /dev/null @@ -1,5 +0,0 @@ -import * as Sentry from '@sentry/node'; - -Sentry.init({ - dsn: process.env.E2E_TEST_DSN || 'https://1234567890@sentry.io/1234567890', -}); diff --git a/dev-packages/node-overhead-gh-action/src/instrument.mjs b/dev-packages/node-overhead-gh-action/src/instrument.mjs deleted file mode 100644 index 8a49ebb67a7e..000000000000 --- a/dev-packages/node-overhead-gh-action/src/instrument.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import * as Sentry from '@sentry/node'; - -Sentry.init({ - dsn: process.env.E2E_TEST_DSN || 'https://1234567890@sentry.io/1234567890', - tracesSampleRate: 1, -}); diff --git a/package.json b/package.json index d05b71e7fdbc..f07218aaf7a3 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,6 @@ "dev-packages/clear-cache-gh-action", "dev-packages/external-contributor-gh-action", "dev-packages/rollup-utils", - "dev-packages/node-overhead-gh-action", "dev-packages/bundler-tests" ], "devDependencies": {