146 lines
13 KiB
Markdown
146 lines
13 KiB
Markdown
# Июнь 2020 — тройное ревью автономного пакета P28 «Повторы запросов и идемпотентность»
|
||
|
||
Статус: **принят в publication registry 31 июля 2026 года**. Ревизии
|
||
накладываются по стабильным slug, сохраняют дату и автора базового архива и
|
||
не перезаписывают <code>articles.json</code>.
|
||
|
||
Проверенные revision:
|
||
|
||
- <code>editorial-2020-06-practice-retry-idempotency</code>;
|
||
- <code>editorial-2020-06-mechanism-retry-idempotency</code>;
|
||
- <code>editorial-2020-06-field-retry-idempotency</code>.
|
||
|
||
Модуль экспортирует ровно три revision. В объектах нет <code>date</code> и
|
||
<code>author</code>, поэтому будущий publication layer должен сохранить
|
||
стабильные дату и автора из базового архива. При запуске
|
||
<code>--print-revisions</code> stdout содержит только JSON; отдельная
|
||
<code>--verify-fixture</code> печатает результат учебной in-memory проверки.
|
||
|
||
## Проход 1. Факты, механизм и ограничения — пройдено
|
||
|
||
| Утверждение или решение | Первичный / официальный источник | Проверенная граница |
|
||
| --- | --- | --- |
|
||
| HTTP различает идемпотентные методы и <code>POST</code>; <code>409</code>, <code>503</code> и <code>Retry-After</code> не создают idempotency сами по себе | [RFC 7231](https://www.rfc-editor.org/rfc/rfc7231.html) | Статьи не называют любой POST безопасным для автоповтора и не приписывают одному status универсальную retry-policy. |
|
||
| Идея key, fingerprint, replay завершённой операции и конфликта параллельного повтора уже была описана в начале 2020 года | [draft-idempotency-header-01](https://datatracker.ietf.org/doc/html/draft-idempotency-header-01) | Документ назван историческим work in progress, не готовым стандартом и не причиной считать header универсально поддержанным. |
|
||
| Timeout клиентского HTTP-request не отменяет серверный business effect автоматически | [Node.js v12: ClientRequest.setTimeout](https://nodejs.org/dist/latest-v12.x/docs/api/http.html#http_request_settimeout_timeout_callback) | Все timeout в статьях описаны как неизвестный исход для наблюдающей стороны, а не как rollback. |
|
||
| Уникальный scope и атомарный захват записи должны жить в устойчивом хранилище | [PostgreSQL 12: Constraints](https://www.postgresql.org/docs/12/ddl-constraints.html), [INSERT / ON CONFLICT](https://www.postgresql.org/docs/12/sql-insert.html) | Учебная SQL-схема не выдаёт процессную память за защиту от двух процессов и отделяет внутреннюю транзакцию от внешнего effect. |
|
||
|
||
Технический разбор проверен повторно после редакторских правок:
|
||
|
||
- key имеет scope <code>actor + operation + key</code>, а fingerprint
|
||
отделяет retry того же payload от другой команды;
|
||
- <code>completed</code> хранит минимальный terminal response для replay;
|
||
<code>in_progress</code> и mismatch payload останавливают второй handler до
|
||
business effect;
|
||
- для эффекта внутри одной базы учебный пример сохраняет объект и terminal
|
||
запись в одной транзакции;
|
||
- для внешнего вызова текст прямо требует отдельный устойчивый key либо status
|
||
route и не обещает exactly once локальной таблицей;
|
||
- retention key должен быть длиннее документированного retry-budget. Удаление
|
||
<code>in_progress</code> не показано как средство исправить сбой.
|
||
|
||
Выполнена автономная фикстура <code>--verify-fixture</code>. Реальный вывод
|
||
подтверждает: первая доставка имеет <code>response_lost</code>, создан ровно
|
||
один effect, retry возвращает сохранённый <code>demo-request-42</code>, а тот
|
||
же key с другой hash получает <code>409</code>. Фикстура не открывает сеть,
|
||
базу, proxy или внешний API.
|
||
|
||
Все адреса, ключи, payload, result ID, журналы и тайминги учебные:
|
||
<code>api.training.invalid</code>, <code>127.0.0.1:48080</code>,
|
||
<code>demo-key-retry-2020-06-a7f1</code> и <code>demo-request-42</code>. Нет
|
||
реальных платёжных данных, credentials, endpoint или утверждения о production
|
||
инциденте.
|
||
|
||
Вердикт прохода: **пройден**. Исторические источники и пример согласованы;
|
||
механика ограничена тем, что пакет действительно проверяет.
|
||
|
||
## Проход 2. Редактура, голос М3 и объём — пройдено
|
||
|
||
| Revision | Проблема и цена в первых двух абзацах | Главный вопрос | Голос и следующее действие |
|
||
| --- | --- | --- | --- |
|
||
| Практика | Потерянный ответ приводит к двойной заявке и ручной чистке | Как удержать один intent, key и конечный retry-budget на стороне клиента | М3 связывает UI с API-contract; маршрут вводит key до первой попытки и проверяет «response lost → replay». |
|
||
| Механизм | Два POST конкурируют, а boolean не объясняет состояние результата | Как сохранить scope, fingerprint и terminal response в устойчивом хранилище | М3 связывает HTTP с SQL-границей, но не выдаёт локальную таблицу за глобальную exactly-once гарантию. |
|
||
| Полевой разбор | Один timeout скрывает три разных пути и провоцирует blind retry | Как собрать requestId, key, журнал и временной budget в одну проверку | М3 связывает frontend, edge и backend через учебную трассу без современного observability stack. |
|
||
|
||
- Draft gate после финальной правки измерил основной текст без источников:
|
||
практика — **10 899** знаков, механизм — **11 577**, полевой разбор —
|
||
**11 331**. Все три текста лежат в диапазоне 5 000–15 000 знаков.
|
||
- В каждом материале проблема и цена названы в первых двух абзацах. Затем
|
||
сохранена короткая последовательность «симптом → причина → проверка →
|
||
действие», а не общая речь о надёжности.
|
||
- У каждой revision есть не менее пяти смысловых разделов, таблица с
|
||
<code>caption</code>/<code>thead</code>, figure с осмысленными
|
||
<code>alt</code>/<code>figcaption</code>, HTTP/code/log example,
|
||
нумерованный маршрут и минимум две первичные или официальные ссылки.
|
||
- Удалены поздние для автора 2020 года обобщения: нет payment API как основы
|
||
примера, event streaming, SLO, распределённой трассировки или фальшивого
|
||
опыта крупного production-инцидента. Внешний effect, proxy и база названы
|
||
границами, которые ещё нужно проверить в конкретном проекте.
|
||
|
||
Вердикт прохода: **пройден**. Тексты плотные, прагматичные и соответствуют
|
||
М3 / июню 2020 года.
|
||
|
||
## Проход 3. Визуал и выпуск автономного пакета — пройдено в заданных границах
|
||
|
||
- <code>retry-idempotency-request-path-2020.svg</code> показывает один intent,
|
||
первую доставку, потерянный ответ и replay. Он объясняет, почему timeout не
|
||
создаёт право послать новый key.
|
||
- <code>retry-idempotency-state-contract-2020.svg</code> показывает scope,
|
||
атомарный захват записи, ветки <code>completed</code>/<code>conflict</code>
|
||
и отдельную границу внешнего effect.
|
||
- <code>retry-idempotency-diagnosis-2020.svg</code> показывает разные
|
||
<code>requestId</code> попыток и общий idempotency key на временной шкале.
|
||
- У SVG есть <code>title</code>, <code>desc</code>, <code>role="img"</code>,
|
||
вертикальные viewBox, контрастные карточки и короткие подписи. Нет
|
||
JavaScript, <code>foreignObject</code>, внешних URL или растровых data URI.
|
||
- Диаграммы отрендерены через Sharp при ширине **375 px**. Независимый
|
||
визуальный проход обнаружил слишком длинные нижние подписи во второй и
|
||
третьей схеме; подписи сокращены и разбиты на строки. Финальные 375 px PNG
|
||
прочитаны повторно: обрезания, наложения и горизонтальный overflow внутри
|
||
SVG не обнаружены.
|
||
- Основной редактор отдельно просмотрел финальные raster-версии на 375 px и
|
||
подтвердил вывод. После подключения registry strict audit и production build
|
||
пройдены; они не подменяют реальный HTTP, PostgreSQL или внешний effect.
|
||
|
||
### Фактические команды и результаты
|
||
|
||
<pre><code>cd web
|
||
node --check scripts/upgrade-2020-06.mjs
|
||
npm run audit:draft -- scripts/upgrade-2020-06.mjs
|
||
node scripts/upgrade-2020-06.mjs --verify-fixture
|
||
xmllint --noout \
|
||
public/assets/editorial/2020/retry-idempotency-request-path-2020.svg \
|
||
public/assets/editorial/2020/retry-idempotency-state-contract-2020.svg \
|
||
public/assets/editorial/2020/retry-idempotency-diagnosis-2020.svg</code></pre>
|
||
|
||
| Проверка | Реальный результат |
|
||
| --- | --- |
|
||
| <code>node --check</code> | код завершения 0 |
|
||
| Draft audit | PASS: 10 899 / 11 577 / 11 331 знаков тела |
|
||
| In-memory fixture | <code>effects = 1</code>; четыре логические проверки вернули <code>true</code> |
|
||
| XML | все три SVG валидны, код завершения 0 |
|
||
| 375 px visual preflight | выполнен после финальной правки; читаемость и границы подтверждены локальным Sharp-рендером |
|
||
| Strict audit после подключения registry | PASS: 10 899 / 11 577 / 11 331 знаков; 2 / 2 / 2 table и 3 / 2 / 3 code example |
|
||
| <code>npm run build</code> | PASS, code 0, 374 статические страницы |
|
||
| Scope/self-review | PASS: в revision нет <code>date</code>/<code>author</code>, <code>articles.json</code> не перезаписан |
|
||
|
||
Не запускались browser-review опубликенной страницы, proxy, PostgreSQL,
|
||
curl-команды, интеграционный стенд или production deployment. Учебный маршрут
|
||
не выдан за опубликованную HTTP-функциональность.
|
||
Проверка assistive technology также не заявляется; у рисунков есть
|
||
структурные alt-тексты и подписи, но это не заменяет реальную проверку
|
||
скринридером.
|
||
|
||
## Итог
|
||
|
||
Статус: **тройное ревью пройдено, пакет принят к публикации**.
|
||
|
||
Изменения после трёх проходов:
|
||
|
||
1. SQL-псевдокод механизма приведён к тексту: внутренний effect и terminal
|
||
idempotency record показаны внутри одной учебной транзакции.
|
||
2. Две длинные мобильные подписи SVG сокращены и разбиты так, чтобы сохранить
|
||
крупный шрифт в 375 px.
|
||
3. Registry заменяет только редакционные поля по стабильному slug; архив,
|
||
standard, очередь и package configuration не перезаписаны.
|