ЗАМЕР
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),
);