fixtures/flight-ssr-bench/bench-server.js JAVASCRIPT 374 lines View on github.com → Search inside
1'use strict';23require('@babel/register')({4  presets: [['@babel/preset-react', {runtime: 'automatic'}]],5  plugins: ['@babel/plugin-transform-modules-commonjs'],6  only: [/\/src\//],7});89const http = require('http');10const {Readable} = require('stream');11const webpack = require('webpack');1213const {clientManifest, ssrManifest} = require('./webpack-mock');14const {15  renderFizzNode,16  renderFizzEdge,17  renderFlightFizzNode,18  renderFlightFizzEdge,19} = require('./render-helpers');20const {printGrid} = require('./print-helpers');21const fs = require('fs');22const JSON_OUT = (function () {23  const arg = process.argv.find(function (a) {24    return a.startsWith('--json-out=');25  });26  return arg ? arg.slice('--json-out='.length) : null;27})();28const jsonResults = [];2930// ---------------------------------------------------------------------------31// Build32// ---------------------------------------------------------------------------3334function build() {35  const config = require('./webpack.config');36  return new Promise(function (resolve, reject) {37    webpack(config, function (err, stats) {38      if (err) {39        reject(err);40        return;41      }42      if (stats.hasErrors()) {43        reject(new Error(stats.toString({errors: true})));44        return;45      }46      console.log(47        stats.toString({colors: true, modules: false, entrypoints: false})48      );49      resolve();50    });51  });52}5354// ---------------------------------------------------------------------------55// Server56// ---------------------------------------------------------------------------5758const ITEM_COUNT = 200;59const PORT = 3001;6061async function main() {62  console.log('Building RSC bundle...\n');63  await build();6465  const {66    renderRSCNode,67    renderRSCEdge,68    App: RSCApp,69    AppAsync: RSCAppAsync,70  } = require('./build/rsc-bundle.js');71  const App = require('./src/App.js').default;72  const AppAsync = require('./src/AppAsync.js').default;7374  function pipeStreamToRes(stream, res) {75    if (typeof stream.pipe === 'function') {76      // Node Readable stream77      stream.pipe(res);78    } else {79      // Web ReadableStream — convert to Node stream for HTTP response80      Readable.fromWeb(stream).pipe(res);81    }82  }8384  function pipeToRes(streamOrPromise, res) {85    if (typeof streamOrPromise.then === 'function') {86      streamOrPromise.then(87        function (stream) {88          pipeStreamToRes(stream, res);89        },90        function (err) {91          console.error(err);92          if (!res.headersSent) res.writeHead(500);93          res.end();94        }95      );96    } else {97      pipeStreamToRes(streamOrPromise, res);98    }99  }100101  const routes = {102    '/fizz-node-sync': function (res) {103      pipeToRes(renderFizzNode(App, ITEM_COUNT), res);104    },105    '/fizz-node-async': function (res) {106      pipeToRes(renderFizzNode(AppAsync, ITEM_COUNT), res);107    },108    '/fizz-edge-sync': function (res) {109      pipeToRes(renderFizzEdge(App, ITEM_COUNT), res);110    },111    '/fizz-edge-async': function (res) {112      pipeToRes(renderFizzEdge(AppAsync, ITEM_COUNT), res);113    },114    '/flight-node-sync': function (res) {115      pipeToRes(116        renderFlightFizzNode(117          renderRSCNode,118          RSCApp,119          ITEM_COUNT,120          clientManifest,121          ssrManifest122        ),123        res124      );125    },126    '/flight-node-sync.rsc': function (res) {127      pipeStreamToRes(renderRSCNode(clientManifest, RSCApp, ITEM_COUNT), res);128    },129    '/flight-node-async': function (res) {130      pipeToRes(131        renderFlightFizzNode(132          renderRSCNode,133          RSCAppAsync,134          ITEM_COUNT,135          clientManifest,136          ssrManifest137        ),138        res139      );140    },141    '/flight-node-async.rsc': function (res) {142      pipeStreamToRes(143        renderRSCNode(clientManifest, RSCAppAsync, ITEM_COUNT),144        res145      );146    },147    '/flight-edge-sync': function (res) {148      pipeToRes(149        renderFlightFizzEdge(150          renderRSCEdge,151          RSCApp,152          ITEM_COUNT,153          clientManifest,154          ssrManifest155        ),156        res157      );158    },159    '/flight-edge-sync.rsc': function (res) {160      pipeStreamToRes(renderRSCEdge(clientManifest, RSCApp, ITEM_COUNT), res);161    },162    '/flight-edge-async': function (res) {163      pipeToRes(164        renderFlightFizzEdge(165          renderRSCEdge,166          RSCAppAsync,167          ITEM_COUNT,168          clientManifest,169          ssrManifest170        ),171        res172      );173    },174    '/flight-edge-async.rsc': function (res) {175      pipeStreamToRes(176        renderRSCEdge(clientManifest, RSCAppAsync, ITEM_COUNT),177        res178      );179    },180  };181182  const server = http.createServer(function (req, res) {183    const handler = routes[req.url];184    if (!handler) {185      if (req.url === '/' || req.url === '') {186        res.writeHead(200, {'Content-Type': 'text/html'});187        res.end(188          '<html><body><h1>Flight SSR Bench</h1><ul>' +189            Object.keys(routes)190              .map(function (r) {191                return '<li><a href="' + r + '">' + r + '</a></li>';192              })193              .join('') +194            '</ul></body></html>'195        );196        return;197      }198      res.writeHead(404);199      res.end('Not found');200      return;201    }202    const contentType = req.url.endsWith('.rsc')203      ? 'text/x-component'204      : 'text/html';205    res.writeHead(200, {'Content-Type': contentType});206    handler(res);207  });208209  await new Promise(function (resolve) {210    server.listen(PORT, resolve);211  });212213  console.log('\nServer listening on http://localhost:%d', PORT);214  console.log('Endpoints:');215  for (const route of Object.keys(routes)) {216    console.log('  http://localhost:%d%s', PORT, route);217  }218219  if (!process.argv.includes('--bench')) {220    return;221  }222223  // Run autocannon against each endpoint.224  // Use a fixed request count (amount) instead of duration so that all225  // in-flight requests complete before autocannon closes connections.226  const autocannon = require('autocannon');227  const concurrencyLevels = [1, 10];228  const WARMUP_AMOUNT = 200;229  const BENCH_AMOUNT = 1000;230231  function runAutocannon(benchUrl, connections, amount) {232    return new Promise(function (resolve, reject) {233      const instance = autocannon({url: benchUrl, connections, amount});234      autocannon.track(instance, {235        renderProgressBar: false,236        renderResultsTable: false,237      });238      instance.on('done', resolve);239      instance.on('error', reject);240    });241  }242243  for (const c of concurrencyLevels) {244    console.log(245      '\n--- HTTP Benchmark (%d warmup, c=%d, %d requests) ---\n',246      WARMUP_AMOUNT,247      c,248      BENCH_AMOUNT249    );250251    const results = {};252    const benchRoutes = Object.keys(routes).filter(function (r) {253      return !r.endsWith('.rsc');254    });255    const labelWidth = Math.max(256      ...benchRoutes.map(function (r) {257        return r.length - 1;258      })259    );260261    const header =262      ''.padEnd(labelWidth) +263      '  ' +264      'req/s'.padStart(14) +265      '  ' +266      'p50'.padStart(8) +267      '  ' +268      'p99'.padStart(8);269    console.log('  ' + header);270    console.log('  ' + '-'.repeat(header.length));271272    for (const route of benchRoutes) {273      const label = route.slice(1);274      const benchUrl = 'http://localhost:' + PORT + route;275276      // Warmup277      await runAutocannon(benchUrl, c, WARMUP_AMOUNT);278279      const data = await runAutocannon(benchUrl, c, BENCH_AMOUNT);280      const reqPerSec = (1000 / data.latency.mean) * data.connections;281      const latencyMedian = data.latency.p50;282      const latencyP99 = data.latency.p99;283      const errors = data.errors + data.timeouts;284285      results[label] = {reqPerSec, latencyMedian, latencyP99};286      jsonResults.push({287        name: label,288        concurrency: c,289        reqPerSec,290        latencyMedian,291        latencyP99,292        errors,293      });294295      let line =296        '  ' +297        label.padEnd(labelWidth) +298        '  ' +299        String(reqPerSec.toFixed(1)).padStart(8) +300        ' req/s' +301        '  ' +302        String(latencyMedian).padStart(5) +303        ' ms' +304        '  ' +305        String(latencyP99).padStart(5) +306        ' ms';307      if (errors > 0) {308        line += '  (' + errors + ' errors)';309      }310      console.log(line);311    }312313    const rps = function (r) {314      return r.reqPerSec;315    };316317    console.log('\n--- Flight overhead (c=%d) ---\n', c);318    printGrid(319      ['Fizz', 'Flight+Fizz'],320      [321        ['Node sync', results['fizz-node-sync'], results['flight-node-sync']],322        [323          'Node async',324          results['fizz-node-async'],325          results['flight-node-async'],326        ],327        ['Edge sync', results['fizz-edge-sync'], results['flight-edge-sync']],328        [329          'Edge async',330          results['fizz-edge-async'],331          results['flight-edge-async'],332        ],333      ],334      rps,335      'req/s'336    );337338    console.log('\n--- Edge vs Node (c=%d) ---\n', c);339    printGrid(340      ['Node', 'Edge'],341      [342        ['Fizz sync', results['fizz-node-sync'], results['fizz-edge-sync']],343        ['Fizz async', results['fizz-node-async'], results['fizz-edge-async']],344        [345          'Flight+Fizz sync',346          results['flight-node-sync'],347          results['flight-edge-sync'],348        ],349        [350          'Flight+Fizz async',351          results['flight-node-async'],352          results['flight-edge-async'],353        ],354      ],355      rps,356      'req/s'357    );358  }359360  if (JSON_OUT) {361    fs.writeFileSync(362      JSON_OUT,363      JSON.stringify({mode: 'server', results: jsonResults})364    );365  }366367  server.close();368}369370main().catch(function (err) {371  console.error(err);372  process.exit(1);373});

Code quality findings 18

Remove debugging statements or use a logging library
info correctness console-log
console.log(
Ensure all async functions handle errors properly
info correctness async-without-catch
async function main() {
Remove debugging statements or use a logging library
info correctness console-log
console.log('Building RSC bundle...\n');
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof stream.pipe === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof stream.pipe === 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof streamOrPromise.then === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof streamOrPromise.then === 'function') {
Chain Promises properly to avoid callback hell
info correctness unresolved-promise
streamOrPromise.then(
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (req.url === '/' || req.url === '') {
Remove debugging statements or use a logging library
info correctness console-log
console.log('\nServer listening on http://localhost:%d', PORT);
Remove debugging statements or use a logging library
info correctness console-log
console.log('Endpoints:');
Remove debugging statements or use a logging library
info correctness console-log
console.log(' http://localhost:%d%s', PORT, route);
Remove debugging statements or use a logging library
info correctness console-log
console.log(
Remove debugging statements or use a logging library
info correctness console-log
console.log(' ' + header);
Remove debugging statements or use a logging library
info correctness console-log
console.log(' ' + '-'.repeat(header.length));
Remove debugging statements or use a logging library
info correctness console-log
console.log(line);
Remove debugging statements or use a logging library
info correctness console-log
console.log('\n--- Flight overhead (c=%d) ---\n', c);
Remove debugging statements or use a logging library
info correctness console-log
console.log('\n--- Edge vs Node (c=%d) ---\n', c);

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.