Files
healthlog/openspec/changes/archive/2026-08-02-cena-chitayushchego-marshruta/tasks.md
T
av 8db2ec7ff4 Цена читающего маршрута: чекпойнт WAL по таймеру и условный запрос
- рядом с воркером свёртки живёт горутина, раз в минуту разбирающая журнал
  пассивным чекпойнтом; «журнал не разбирается» видно строкой владельцу, а не
  только по `df`. Признак — пара чисел, а не флаг занятости: тот молчит под
  удерживаемым читателем (`busy=0` при 6256 страницах и пяти перенесённых), а
  при занятой блокировке отдаёт `-1` вместо ответа, и `-1 >= -1` читалось бы как
  «разобрано целиком»
- каталог отвечает `304` на `If-None-Match`, не открывая снимок витрины. Метка
  собрана из всего, от чего зависит ответ: версии витрины (`data_version` с
  закреплённого соединения плюс поколение — значение локально для соединения и
  не переживает переоткрытия), горизонта измерения и области действия ресурса.
  Версия снимается до и после сборки: снятая после пометила бы устаревший снимок
  свежим номером
- предел и дедлайн ответа отложены в задачу Read API точек вместе с измеренной
  ценой первого запроса; попутно починен флаки-тест чужой задачи, искавший
  значение точки в сыром буфере записи лога
2026-08-02 20:42:22 +03:00

5.4 KiB

1. Версия витрины в хранилище

  • 1.1 Соединение-щуп: sql.Conn, взятый по первому запросу версии, поколение (ULID через internal/ident), пересоздание с новым поколением только при sql.ErrConnDone — обстоятельства поколение не меняют
  • 1.2 Store.StateVersion(ctx)PRAGMA data_version со щупа, метка вида <поколение>-<счётчик>; Store.VersionedRead — двойная проба вокруг чтения; щуп закрывается раньше пула в Close и не воскресает после него
  • 1.3 Тесты: неизменившаяся база даёт ту же версию; запись из пула её двигает; переоткрытие базы даёт другую версию; щуп пересоздаётся с новым поколением; после Close версия отказывает и -wal рядом не остаётся; щуп не удерживает читающий снимок

2. Обслуживание WAL

  • 2.1 journal_size_limit в DSN рабочего подключения, с причиной в комментарии (пассивный чекпойнт файл не укорачивает)
  • 2.2 Store.CheckpointWAL(ctx)PRAGMA wal_checkpoint(PASSIVE), наружу тройка чисел (busy, log, checkpointed) без интерпретации
  • 2.3 Цикл чекпойнта в cmd/healthlog: тик в минуту, WARN при «страниц больше порога и перенесено меньше», отказ не убивает цикл
  • 2.4 Запуск и дренирование в serve: горутина ждётся в общем бюджете остановки, отдельного чекпойнта на выходе нет
  • 2.5 Тесты: чекпойнт переносит страницы в тишине; удерживаемый читатель даёт checkpointed < log без ошибки; порог молчит на малом журнале; цикл выходит по отмене

3. Условный запрос в транспорте

  • 3.1 Помощник httpapi: разбор If-None-Match (список, W/, *), слабое сравнение, 304 без тела — общий для будущих читающих маршрутов
  • 3.2 Источник версии передаётся транспорту функцией (как worker.Notify), проверка токена чтения остаётся раньше условия
  • 3.3 Тесты помощника на формах заголовка: пусто, список, W/, *, мусор

4. Каталог отдаёт версию

  • 4.1 catalog.Service.Metrics возвращает снимок вместе с версией: проба до, сборка, проба после; расхождение — версии нет
  • 4.2 handleMetrics: ETag из версии, 304 по If-None-Match без открытия снимка, ответ без ETag при расхождении проб
  • 4.3 Тесты: два ответа подряд — одна метка и одинаковые байты; после свёртки метка другая; 304 не открывает снимок; 401 раньше 304

5. Приёмочные критерии (рубрика ревью дизайна)

  • 5.1 Равная метка ⟹ побайтово равный ответ (кроме горизонта — назван в дизайне); обратное направление ошибок не допускается ни в одном тесте
  • 5.2 Ни один новый лог не несёт значений точек, имён метрик без обрезки и секретов; уровень выбран по адресату
  • 5.3 task gate зелёный; task verify:archive сходится (обслуживание WAL и версия не меняют витрину)
  • 5.4 Поведенческая проверка на своём стенде из исходников (отдельный каталог данных, рабочий контейнер не трогаем): curl дважды даёт 304, после доставки — 200

6. Документация

  • 6.1 docs/architecture.md: версия витрины, условный запрос, обслуживание WAL, отвергнутые чужие решения с причинами
  • 6.2 README.md: строка про условный запрос в примерах чтения
  • 6.3 docs/backlog: задача снята, остаток (предел ответа, измеренная цена первого запроса, готовая машинерия условного запроса) перенесён в read-api-tochki.md; наблюдаемость — в stats-nablyudaemost.md, цена ветки исчерпанного бюджета — в ostanovka-i-migraciya-sledy.md