- рядом с воркером свёртки живёт горутина, раз в минуту разбирающая журнал пассивным чекпойнтом; «журнал не разбирается» видно строкой владельцу, а не только по `df`. Признак — пара чисел, а не флаг занятости: тот молчит под удерживаемым читателем (`busy=0` при 6256 страницах и пяти перенесённых), а при занятой блокировке отдаёт `-1` вместо ответа, и `-1 >= -1` читалось бы как «разобрано целиком» - каталог отвечает `304` на `If-None-Match`, не открывая снимок витрины. Метка собрана из всего, от чего зависит ответ: версии витрины (`data_version` с закреплённого соединения плюс поколение — значение локально для соединения и не переживает переоткрытия), горизонта измерения и области действия ресурса. Версия снимается до и после сборки: снятая после пометила бы устаревший снимок свежим номером - предел и дедлайн ответа отложены в задачу Read API точек вместе с измеренной ценой первого запроса; попутно починен флаки-тест чужой задачи, искавший значение точки в сыром буфере записи лога
5.4 KiB
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