134 lines
14 KiB
Markdown
134 lines
14 KiB
Markdown
# Май 2020 — тройное ревью автономного пакета P27 «Фоновые задачи»
|
||
|
||
Статус: **принят в publication registry 31 июля 2026 года**. Он включает
|
||
ровно три revision для стабильных slug; registry сохраняет дату и автора
|
||
базового архива:
|
||
|
||
- <code>editorial-2020-05-practice-background-jobs</code>;
|
||
- <code>editorial-2020-05-mechanism-background-jobs</code>;
|
||
- <code>editorial-2020-05-field-background-jobs</code>.
|
||
|
||
В module export нет полей <code>date</code> и <code>author</code>;
|
||
<code>articles.json</code>, standard, очередь и package config не
|
||
перезаписывались. При вызове <code>--print-revisions</code> stdout
|
||
содержит только JSON, а <code>--verify-fixture</code> запускает отдельную
|
||
детерминированную учебную фикстуру.
|
||
|
||
## Проход 1. Факты, состояния и границы механизма — пройдено
|
||
|
||
| Утверждение | Первичный или официальный источник | Редакторская граница |
|
||
| --- | --- | --- |
|
||
| Delivery tag относится к доставке, а `basic.ack` и `basic.reject` относятся к этой доставке | [AMQP 0-9-1 specification](https://www.rabbitmq.com/resources/specs/amqp-xml-doc0-9-1.pdf) | Тексты не используют delivery tag как вечный идентификатор задачи и не приписывают абстрактному псевдокоду API конкретной Node-библиотеки. |
|
||
| Неподтверждённая ручная delivery возвращается после закрытия канала/соединения; requeue может образовать дорогой loop | [RabbitMQ: Consumer Acknowledgements and Publisher Confirms](https://www.rabbitmq.com/docs/3.13/confirms) | `redelivered` используется как сигнал в журнале, но идемпотентность опирается на `jobId` и terminal state, а не на один флаг. |
|
||
| Publisher/consumer неопределённость допускает duplicate, поэтому consumer должен быть идемпотентен либо делать deduplication | [RabbitMQ: Reliability guide](https://www.rabbitmq.com/docs/reliability) | Материалы утверждают только at-least-once контракт одной задачи; exactly-once не обещается. |
|
||
| `reject`/`nack` без requeue может направить сообщение в dead-letter route при настроенном DLX | [RabbitMQ: Dead Letter Exchanges](https://www.rabbitmq.com/docs/next/dlx) | Карантин описан как проверяемый маршрут конкретной конфигурации. Не заявлено, что он существует без настройки или одинаково работает у любого broker. |
|
||
|
||
Внутри статей разделены четыре факта, которые часто смешивают: запись
|
||
намерения в базе, outbox-публикация, delivery worker-у и сохранённый бизнес-
|
||
результат. Для одного <code>jobId</code> описаны состояния
|
||
<code>queued</code>, <code>running</code>, <code>retry_wait</code>,
|
||
<code>succeeded</code> и <code>quarantined</code>. Ack следует после
|
||
сохранения <code>resultKey</code> и terminal state. Повтор до этого момента
|
||
не считается дефектом: повторный worker должен увидеть завершённую задачу и
|
||
подтвердить новое delivery без второго effect.
|
||
|
||
Проверена и намеренно оставлена граница внешнего side effect. Детерминированный
|
||
путь <code>reports/{jobId}.csv</code> демонстрирует идемпотентность одного
|
||
экспорта; он не делает идемпотентными email, платёж, сторонний API или любое
|
||
другое необратимое действие. Для него нужен собственный стабильный ключ и
|
||
контракт принимающей стороны. Outbox также не выдан за распределённую
|
||
транзакцию с broker: он оставляет приложению восстанавливаемую запись на
|
||
публикацию, а реальный confirm зависит от выбранного клиента и стенда.
|
||
|
||
Историческая граница сохранена: это М3 / май 2020. Автор уже связывает backend
|
||
и delivery — состояние задачи, outbox, worker, retry и журнал — но не
|
||
приписывает себе платформу observability, современную оркестрацию или
|
||
универсальную реализацию задержанных очередей. Современная официальная
|
||
документация использована для сверки смысла протокольных операций, а не как
|
||
заявление о конкретно запущенной инфраструктуре мая 2020 года.
|
||
|
||
Вердикт прохода: **пройден**. Механизм и его технологические допущения
|
||
разделены; учебная фикстура не выдана за реальный broker.
|
||
|
||
## Проход 2. Редактура, глубина и голос М3 — пройдено
|
||
|
||
| Ревизия | Проблема и цена в первых двух абзацах | Практический вопрос | Объём по publication draft gate |
|
||
| --- | --- | --- | --- |
|
||
| Практика | Экспорт исчезает или создаёт дубль; цена — потерянная работа, неверный статус и ручной разбор | Как зафиксировать намерение, outbox, result и ack в одном коротком контракте | **9 385** знаков |
|
||
| Механизм | Команда смешивает job и delivery; цена — ранний ack, дубль effect или бесконечный requeue | Почему ack ставится после результата и как один `jobId` переживает повтор | **10 550** знаков |
|
||
| Полевой разбор | Один экспорт дублируется, невалидная задача крутится; цена — лишний effect, перегруженный worker и скрытая причина | Как расследовать один `jobId` и остановить poison message | **10 484** знака |
|
||
|
||
- Во всех трёх текстах первый абзац называет конкретный симптом и его цену, а
|
||
второй очерчивает учебную границу. Далее выдержан маршрут «симптом → причина
|
||
→ проверка → действие → ограничение», без общей техлид-риторики.
|
||
- У каждой статьи есть не менее пяти смысловых разделов, таблица с
|
||
<code>caption</code>/<code>thead</code>, рисунок с самостоятельными
|
||
<code>alt</code> и <code>figcaption</code>, код или JSON-журнал,
|
||
нумерованный маршрут и четыре первичных/официальных источника.
|
||
- Практика объясняет outbox и порядок результата/ack. Механизм отделяет
|
||
delivery tag от <code>jobId</code>, вводит retry-политику и quarantine.
|
||
Полевой текст не притворяется реальным incident: все ID, журналы и результат
|
||
маркированы как анонимизированная фикстура.
|
||
- Никакой текст не предлагает «повторить всё» или «увеличить timeout» без
|
||
классификации. Временная, неопределённая внешняя и постоянная ошибка имеют
|
||
разные проверки и разные следующие действия.
|
||
|
||
Отдельный редакторский контроль подтвердил, что исходные названия заметно
|
||
конкретизированы: в заголовках и excerpts есть job, worker, ack, retry,
|
||
идемпотентность и poison message, а не яркий общий заголовок с коротким
|
||
содержимым. Диапазон 5 000–15 000 соблюдён собственным guard модуля без
|
||
таблиц, code blocks и figures; строгий draft gate дополнительно измерил
|
||
видимое тело с этими артефактами.
|
||
|
||
Вердикт прохода: **пройден**. Глубина достигнута через разбор границ и
|
||
проверяемый маршрут, голос остаётся прагматичным уровнем М3.
|
||
|
||
## Проход 3. Визуал, фикстура и выпусковой preflight — пройдено в пределах пакета
|
||
|
||
- <code>background-job-lifecycle-2020.svg</code> показывает вертикальный
|
||
путь от записи job/outbox к worker, сохранённому result, ack, retry и
|
||
quarantine/DLX.
|
||
- <code>background-job-retry-boundary-2020.svg</code> отделяет delivery tag
|
||
от terminal state и показывает, что временный retry ограничен, а постоянная
|
||
ошибка останавливается без requeue.
|
||
- <code>background-job-diagnosis-2020.svg</code> ведёт расследование по одному
|
||
<code>jobId</code> через запись задачи, outbox, журнал worker, resultKey и
|
||
карантин.
|
||
- Все схемы имеют <code>title</code>, <code>desc</code>,
|
||
<code>role="img"</code>, вертикальный viewBox, короткие подписи и контрастные
|
||
карточки. В них нет JavaScript, <code>foreignObject</code>, внешних URL или
|
||
raster data URI.
|
||
- Независимый mobile preflight отрендерил каждый SVG через Sharp в PNG шириной
|
||
375 px и просмотрел результат. На первом рисунке длинная последняя подпись
|
||
была развёрнута в две строки, на втором короткая tag-подпись была сокращена;
|
||
после правки обрезания, наложения и горизонтального выхода в SVG не осталось.
|
||
Это статическая проверка visual asset, не browser-run и не тест screen reader.
|
||
- Основной редактор отдельно просмотрел финальные raster-версии на 375 px и
|
||
подтвердил вывод. После подключения registry strict audit и production build
|
||
пройдены; они не подменяют настоящий broker-интеграционный тест.
|
||
|
||
Учебная фикстура <code>--verify-fixture</code> выполнила две цепочки в памяти:
|
||
временный отказ → повторная delivery → result → ack и три неуспеха →
|
||
quarantined. Она вернула три истинных условия: <code>ackAfterResult</code>,
|
||
<code>poisonStopped</code> и <code>retryWasRedelivered</code>. Фикстура не
|
||
подключалась к RabbitMQ, не проверяла AMQP frames, DLX policy, сеть, несколько
|
||
consumer, скорость или production workload.
|
||
|
||
### Фактически выполненные проверки
|
||
|
||
| Проверка | Команда | Реальный результат |
|
||
| --- | --- | --- |
|
||
| Синтаксис модуля | <code>node --check scripts/upgrade-2020-05.mjs</code> из <code>web/</code> | PASS, код 0 |
|
||
| Import-safe export и draft gate | <code>npm run audit:draft -- scripts/upgrade-2020-05.mjs</code> из <code>web/</code> | PASS: 9 385 / 10 550 / 10 484 знаков body; три slug, таблицы, figures, code, routes, sources и assets найдены |
|
||
| Учебная фикстура состояний | <code>node scripts/upgrade-2020-05.mjs --verify-fixture</code> из <code>web/</code> | PASS: `ackAfterResult`, `poisonStopped`, `retryWasRedelivered` — true |
|
||
| XML трёх схем | <code>xmllint --noout public/assets/editorial/2020/background-job-lifecycle-2020.svg public/assets/editorial/2020/background-job-retry-boundary-2020.svg public/assets/editorial/2020/background-job-diagnosis-2020.svg</code> из <code>web/</code> | PASS, код 0 |
|
||
| Mobile visual preflight | Sharp render трёх SVG в PNG шириной 375 px и ручной просмотр | PASS после точечной правки двух коротких подписей; нет clipping, наложения или horizontal overflow внутри SVG |
|
||
| Strict audit после подключения registry | PASS: 9 385 / 10 550 / 10 484 знаков; 2 / 2 / 2 table и 1 / 2 / 2 code example |
|
||
| <code>npm run build</code> | PASS, code 0, 374 статические страницы |
|
||
| Scope/self-review | PASS: в revision нет date/author, а <code>articles.json</code> не перезаписан |
|
||
|
||
Выпусковой вердикт: **тройное ревью пройдено, пакет принят к публикации**.
|
||
Registry заменяет только редакционные поля по стабильному slug. Реальный
|
||
broker, DLX policy, browser и assistive-tech не запускались и не выданы за
|
||
результат in-memory fixture или статической сборки.
|