Deep Engineering

ЗАМЕР

bench/bff/storage.mjs

Скрипт, которым получены числа в статье, и запись прогона. Файл читается на сборке из репозитория — это тот самый код, который запускали, а не его копия.

Цитируется в статье
/ru/system-design/api-design/bff
Как запустить
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

Запись прогона

Замеры для статьи «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 — в репозитории их не было, и числа статьи нельзя было перезапустить. Правило «числа в статьях берутся отсюда, а не из головы» для уроков выполнялось, для статей — нет.

Скрипт

45 строк
/**
 * 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),
);