Registry: the "last result" column now also shows, per level (egress,
ingress), how many of the recorded checks of the latest cycle succeeded, split
by check family (tcp-22 and tcp-443 are both "tcp"). One grouped query per
chunk of addresses; new fields last_cycle_id, egress, ingress in
GET /admin/registry; the dashboard renders them under the verdict.
Verdict integrity (migration 0010):
- the prober is handed an address once per site and attempt, not on every
poll, so results are no longer overwritten by later probe rounds;
- UpsertCheckIfOpen refuses writes once the address is aggregating or has its
verdict, or for an older attempt; senders get {"ok":true,"ignored":N} and a
result_dropped event is recorded;
- the checking window counts from checking_started_at, not from assigned_at;
- checks.recorded_at (server clock) and checks.after_verdict (flag for rows
written after the verdict in existing data);
- the verdict rule is a pure function (computeVerdict) and the aggregated
event carries the egress/ingress check counts.
Rebuilt bin/control-api and bin/admin-dashboard to match. Plans and summaries
are in docs/changes; README, API, USAGE, DASHBOARD and DIAGRAMS are updated.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
42 lines
5.6 KiB
Markdown
42 lines
5.6 KiB
Markdown
# Вердикт без опоздавших результатов — итог
|
|
|
|
План: [2026-10-03_17-21_verdict-no-late-results-plan.md](2026-10-03_17-21_verdict-no-late-results-plan.md).
|
|
|
|
Статус: код и тесты готовы. На стенд не выкладывалось, контрольный прогон не проводился.
|
|
|
|
## Что сделано
|
|
|
|
1. **Один проход площадки на адрес.** `GET /probers/{site}/assignments` больше не отдаёт адрес, для которого эта площадка отметила `complete` в текущей попытке (`DB.ListCheckingForSite`). Другим площадкам адрес выдаётся, новая попытка выдаёт его снова. Prober не менялся.
|
|
2. **После вердикта запись закрыта.** `DB.UpsertCheckIfOpen` пишет проверку только если строка очереди существует, попытка совпадает с текущей и состояние не `aggregating`, `done`, `failed`, `occupied`. Проверка состояния и запись — один SQL-запрос. Отброшенный результат не создаёт и не меняет строк. Ответ `200 {"ok": true, "ignored": N}`; на каждый адрес пишется событие `result_dropped`. `UpsertCheck` и `Orchestrator.RecordCheck` оставлены как обёртки для существующих вызовов.
|
|
3. **Окно проверки с её начала.** Новая колонка `ip_queue.checking_started_at` (ставится в `SetChecking`, сбрасывается при повторе). `isReadyToAggregate` считает `checking_window_seconds` от неё, для старых строк — от `assigned_at`.
|
|
4. **Время записи по часам сервера.** Колонка `checks.recorded_at`, обновляется при каждой принятой записи.
|
|
5. **Вердикт воспроизводим.** Правило вынесено в чистую функцию `computeVerdict`; в событие `aggregated` добавлены `egress` и `ingress` (число проверок по уровням).
|
|
6. **Старые данные.** Миграция `0010_verdict_integrity.sql`: `recorded_at` = `created_at`, колонка `after_verdict` = 1 у строк, записанных после вердикта (`julianday(checked_at) > julianday(aggregated_at)`).
|
|
|
|
## Проверка на копии боевой БД (запуск 02.10, 141 005 проверок)
|
|
|
|
Миграция применилась за секунды, `user_version` = 10, `recorded_at` заполнен у всех строк. Помечено `after_verdict`:
|
|
|
|
| | всего | провалов | успешных |
|
|
|---|--:|--:|--:|
|
|
| ingress | 1430 | 844 | 586 |
|
|
| egress | 351 | 32 | 319 |
|
|
|
|
В плане было 1426 и 350: они считались сравнением строк времени, миграция сравнивает точнее.
|
|
|
|
## Тесты
|
|
|
|
- `internal/db/queries_verdict_integrity_test.go`: запись принимается, пока адрес проверяется, и идемпотентна; после начала агрегации и после вердикта не создаёт и не меняет строк; результат прежней попытки отбрасывается; `recorded_at` растёт, `created_at` нет; `ListCheckingForSite` (готовая площадка, другая площадка, новая попытка); `checking_started_at` ставится и сбрасывается; миграция 0010 на базе версии 9.
|
|
- `internal/orchestrator/verdict_integrity_test.go`: таблица `computeVerdict` (7 случаев); окно от начала проверки и запасной вариант `assigned_at`; полный цикл: после вердикта результат не принят, сохранённые проверки не изменились, вердикт равен пересчёту.
|
|
- `internal/httpapi/handlers_verdict_integrity_test.go`: площадка получает адрес до `complete`, после него нет, другая площадка получает; результаты prober и агента после вердикта дают `ignored`, строки не изменены, два события `result_dropped`.
|
|
- `go build ./... && go vet ./... && go test ./...` проходят.
|
|
|
|
## Что не проверено
|
|
|
|
- Работа на живом стенде и контрольный прогон. Гипотеза «часть прежних провалов ingress — проверка отвязанного Floating IP» подтвердится или нет только им: после изменения доля провалов ssh, tcp-22 и icmp должна упасть. Если не упадёт, причину нужно искать в другом месте.
|
|
- Нагрузка на сеть площадок изменится: prober теперь зондирует адрес один раз, а не при каждом опросе.
|
|
|
|
## Что дальше
|
|
|
|
Пересборка и перезапуск только `control-api` (prober и агенты без изменений), копия БД перед миграцией, контрольный прогон на небольшой партии (300 адресов). Раздел «Аналитика» строится после этого и получает миграцию `0011`.
|