Deep Engineering

MEASUREMENT

bench/bff/storage.mjs

The script that produced the numbers in the article, and the record of the run. The file is read from the repository at build time — this is the code that was run, not a copy of it.

Cited in
/en/system-design/api-design/bff
How to run it
node bench/bff/measure.mjs > bench/bff/measurements.json
node bench/bff/storage.mjs > bench/bff/storage.json
node bench/bff/latency.mjs > bench/bff/latency.json

The run below is recorded in Russian. It is a lab record, kept in the language it was written in; the numbers, the tables and the code read the same either way.

Record of the run

Замеры для статьи «Backend for Frontend»

Не симуляция. Каждое число статьи взято из прогона этих файлов против next start перед настоящим Go API перед PostgreSQL — кроме одной величины, которая задана намеренно (см. ниже).

Файл Что меряет
measure.mjs что скрипт из собственного источника страницы может и чего не может достать, когда сессия живёт в HttpOnly-cookie за BFF
storage.mjs две другие колонки той же таблицы: что достаёт тот же скрипт, когда токен лежит в localStorage и когда — в куке без HttpOnly
latency.mjs как цена экрана растёт с числом обходов, которые делает браузер
measurements.json, storage.json, latency.json своды, из которых взяты числа статьи
node bench/bff/measure.mjs > bench/bff/measurements.json
node bench/bff/storage.mjs > bench/bff/storage.json
node bench/bff/latency.mjs > bench/bff/latency.json

Одно число снято не этими скриптами. Поле serverLegLoopback в latency.json (0,71 мс) получено двадцатью запросами curl к http://localhost:8091/health с той же машины, а не из браузера, — это записано в самом файле, в поле tool. Статья обязана называть его так же: обращение к соседнему сервису с машины, а не «внутри датацентра».

Что здесь измерено, а что задано

Задержка на стороне браузера вводится намеренно (Network.emulateNetworkConditions через CDP), потому что замер по петле обратной связи сказал бы, что обход бесплатен, — а это ровно та иллюзия, которую замер призван сломать. Введённая величина названа в тексте статьи; измеряется не она, а то, как от неё растёт цена экрана.

Почему этот каталог появился поздно

Скрипты лежали в tmp/, а tmp/ стоит в .gitignore — в репозитории их не было, и числа статьи нельзя было перезапустить. Правило «числа в статьях берутся отсюда, а не из головы» для уроков выполнялось, для статей — нет.

Script

45 lines
/**
 * The other two columns of the same table: what an in-page script reaches when
 * the token is kept in localStorage, and when it is kept in a cookie without
 * HttpOnly. Measured the same way as the BFF column, so the comparison is
 * three measurements rather than one measurement and two assertions.
 *
 *   node bench/bff/storage.mjs > bench/bff/storage.json
 */
import http from "node:http";
import { chromium } from "@playwright/test";

const TOKEN = "tok_1f3a9c8e2b7d4a06";

const server = http.createServer((req, res) => {
  if (req.url.startsWith("/set")) {
    // A cookie without HttpOnly: exactly what a framework hands you when the
    // flag is left off.
    res.setHeader("Set-Cookie", [`readable_session=${TOKEN}; Path=/; SameSite=Lax`]);
  }
  res.setHeader("Content-Type", "text/html; charset=utf-8");
  res.end(`<!doctype html><meta charset="utf-8"><title>probe</title>
<script>localStorage.setItem("access_token", ${JSON.stringify(TOKEN)});</script>`);
});
await new Promise((r) => server.listen(0, "127.0.0.1", r));
const port = server.address().port;

const browser = await chromium.launch({
  executablePath: process.env.PLAYWRIGHT_CHROMIUM_PATH || undefined,
});
const page = await browser.newPage();
await page.goto(`http://127.0.0.1:${port}/set`);

// The one line an injected script needs in each case.
const reach = await page.evaluate(() => ({
  fromLocalStorage: localStorage.getItem("access_token"),
  fromDocumentCookie: document.cookie,
}));

await browser.close();
server.close();

process.stdout.write(
  JSON.stringify({ plantedToken: TOKEN, reach, recordedAt: new Date().toISOString() }, null, 2),
);