A validator that failed the self-check of an address no longer gets that address
again in the current round (ClaimNextQueued skips it); the validator itself stays
in service and takes all other addresses. The verdict fail is set when the number
of failed self-checks of an address reaches settings.self_check_max_attempts
(1..50, default 5, independent of the number of validators); max_retries and
retry_count are no longer used for self-check. If every working validator has
already failed the address, a new round starts and the exclusions lapse.
Migration 0012: ip_self_check_failures (permanent history per registry address),
ip_queue.sc_failures and sc_round_start_cycle (cycle_id is used instead of
attempt_number, which restarts when a queue row is recreated), the setting.
db.FailSelfCheck does it in one transaction; re-submission starts a new series.
API: self_check_max_attempts in GET/PUT /admin/config/orchestrator,
self_check_failed_on in /admin/ips/{ip} and /admin/registry/{ip}. Dashboard: the
field on /settings and the line "Self-check не прошёл на: ..." on the address
pages. Docs, plan and summary in docs/changes/.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
16 KiB
План: повтор после сбоя self-check — на другом валидаторе
Статус: реализовано, см. итог.
1. Проблема
В запуске 2 два адреса получили fail: 89.208.220.90 и 89.208.221.74. Оба раза self-check провалился четыре раза подряд на одном валидаторе vkiplab-v17 (он выходил в сеть с общего адреса облака 109.120.182.224, а не с плавающего IP), с интервалом около 70 секунд.
Причина в логике повтора: после сбоя адрес возвращается в начало очереди, а освободившийся v17 берёт следующий адрес с наименьшим sequence, то есть тот же. Один временно неисправный валидатор расходует все попытки адреса. Сам адрес исправен: 89.208.216.141, попавший на v17 дважды, после перехода на v13 прошёл проверки.
2. Требования и принятые решения
- Адрес, не прошедший self-check на валидаторе N, при повторе не отдаётся валидатору N. Правило действует только для этого адреса.
- Валидатор N остаётся в системе как был: не блокируется, не помечается неисправным, продолжает брать и проверять все остальные адреса.
- Итог
failставится, когда число проваленных self-check у адреса достигло потолка. Потолок не зависит от числа валидаторов. Это настройкаself_check_max_attempts: она меняется в разделе настроек/settingsи через API; значение по умолчанию для текущего окружения — 5. Исключение валидаторов (п.1) гарантирует, что первые попытки идут на разных валидаторах: адрес получаетfailпосле 5 провалов на 5 разных валидаторах (а не на одном). - Исключение создаёт только проваленный self-check; на ошибки привязки плавающего IP, потерю валидатора и истечение аренды оно не распространяется.
- Сведения «Self-check не прошёл на: …» сохраняются и отдаются в API для последующего показа на странице аналитики.
3. Решение
3.1. Данные (миграция 0012_self_check_failures.sql)
- Таблица
ip_self_check_failures(registry_id, run_id, cycle_id, attempt_number, validator_id, failed_at, detail). Постоянная история сбоев self-check по адресу; не очищается при перепроверках, живёт вместе с реестром (не удаляется вместе со строкой очереди) и служит источником для аналитики. - Колонки
ip_queue.sc_failures(сколько self-check провалено в текущей серии попыток) иip_queue.sc_round_start_attempt(номер попытки, с которой действуют исключения; «раунд»). - Настройка
self_check_max_attemptsв таблицеsettings: миграция записывает 5 (допустимо 1…50), дальше значение меняется в/settingsили через API. Полеorchestrator.max_self_check_retriesв YAML для self-check больше не используется (остаётся в файле для совместимости, в документации помечается устаревшим).
Исключение для валидатора N на адресе действует, если в ip_self_check_failures есть сбой этого валидатора на этом адресе с attempt_number >= sc_round_start_attempt.
3.2. Обработка сбоя (Orchestrator.SelfCheckResult, ветка «не прошёл»)
После проверки, что адрес принадлежит этому валидатору и ждёт self-check, и отвязки плавающего IP (как сейчас):
- Записать сбой в
ip_self_check_failures, увеличитьsc_failures. - Решение:
sc_failures >= self_check_max_attempts→fail(причина «self-check не прошёл N раз», в событии перечислены валидаторы);- иначе → вернуть адрес в очередь. Если среди рабочих валидаторов (
idle,assigned,checking;unreachableиunregisteredне считаются) не осталось ни одного без сбоя в текущем раунде, начинается новый раунд (sc_round_start_attempt= следующая попытка): исключения теряют силу, повтор идёт по обычным правилам. Так при числе валидаторов меньше потолка (в том числе при единственном) повторы продолжаются до потолка, а адрес не застревает в очереди.
Лимит max_retries и счётчик retry_count для self-check больше не используются: возврат в очередь после сбоя self-check не увеличивает retry_count и не может привести к fail по нему. Для остальных причин возврата (ошибка привязки, истечение аренды) всё остаётся как сейчас.
3.3. Выдача адресов (DB.ClaimNextQueued)
Валидатор получает ближайший по sequence адрес, кроме тех, где он исключён (по 3.1). Исключённый адрес остаётся в очереди для остальных валидаторов и очередь не блокирует. Запрос по-прежнему одной транзакцией.
3.4. Жизненный цикл
- Ручная перепроверка или повторная постановка адреса (
SubmitIPs) начинает новую серию:sc_failures = 0,sc_round_start_attempt= текущая попытка. - История в
ip_self_check_failuresостаётся. - Удаление адреса и очистка очереди историю не трогают;
docs/ADMIN_CLEANUP.mdдополняется новой таблицей.
3.5. Данные для аналитики и видимость
- Событие
self_check_resultостаётся как сейчас; добавляется событиеvalidator_excluded({"validator_id": …, "failures": N}). GET /admin/ips/{ip}иGET /admin/registry/{ip}получают полеself_check_failed_on(список валидаторов, у которых self-check на этом адресе не прошёл; для реестра — по всем запускам, для запуска — фильтр поrun_id).- Строка «Self-check не прошёл на: v17, …» на странице адреса (
/ips/{ip},/registry/{ip}). - Отображение на странице аналитики не входит в эту доработку: данные и API будут готовы, показ (колонка в списках адресов, блок «Self-check по валидаторам») делается следующим шагом отдельным планом.
3.6. Настройка в интерфейсе (/settings)
Изменения отражаются в разделе настроек явно:
- В форме с
fip_settle_secondsиhistory_retention_cyclesдобавляется поле «Потолок провалов self-check на адрес» (self_check_max_attempts), целое число, по умолчанию 5, допустимо 1…50. - Под полем пояснение: «Сколько раз self-check может не пройти у одного адреса, прежде чем адрес получит итог
fail. Повторы идут на других валидаторах: валидатор, на котором адрес не прошёл self-check, этому адресу больше не выдаётся (на остальные адреса это не влияет)». - Значение сохраняется той же кнопкой «Сохранить», действует на следующих повторах без перезапуска; неверное значение (не число, вне 1…50) показывается предупреждением в баннере, форма остаётся прежней.
- В API:
GET/PUT /admin/config/orchestratorполучает полеself_check_max_attempts(ошибка 400 при неверном значении). - Описание поля добавляется в
docs/DASHBOARD.md(строка/settings) иdocs/USAGE.md.
4. Файлы
- БД:
internal/db/migrations/0012_self_check_failures.sql,db.go,queries_ipqueue.go(ClaimNextQueued, запись сбоя, новый возврат в очередь безretry_count, принудительныйfail, сброс серии вSubmitIPsAs),queries_settings.go(настройка), запрос истории для API. - Оркестратор:
orchestrator.go(SelfCheckResult),config.go(начальное значение настройки). - API и дашборд:
handlers_admin.go,handlers_config.go, DTO,handlers_settings.go,settings.html,ip_detail.html,registry_detail.html. - Документы:
USAGE.md(«Как читать итоговый результат», повторы self-check, настройка),API.md,ADMIN_CLEANUP.md,README.md, итогdocs/changes/…-summary.md.
5. Тесты
- БД: валидатор, на котором адрес исключён, пропускает его и берёт следующий; другой валидатор берёт исключённый адрес; исключения действуют только для этого адреса; новый раунд снимает исключения; перепостановка сбрасывает серию, но не историю; настройка (значения по умолчанию, границы).
- Оркестратор (сценарий инцидента на трёх валидаторах): v1 всегда проваливает self-check — адрес не возвращается на v1, проходит на v2, а v1 в это время берёт и проходит другие адреса; потолок: адрес, провалившийся
self_check_max_attemptsраз, получаетfail, раньше — нет; при потолке меньше числа валидаторовfailнаступает на потолке без перебора всех; при потолке больше числа валидаторов начинается новый раунд; единственный валидатор повторяет на себе до потолка;unreachableне считается валидатором; сбой привязки и истечение аренды исключений и счётчика self-check не создают и по-прежнему идут поmax_retries; изменение настройки действует на следующем повторе. - API и дашборд: поле
self_check_failed_on; поле «Потолок провалов self-check на адрес» отображается в/settingsсо значением 5, сохраняется, неверные значения показывают предупреждение; поле вGET/PUT /admin/config/orchestrator; строка «Self-check не прошёл на: …» на странице адреса. - Миграция
0012на копии боевой БД (настройка равна 5), повторный запуск безопасен. Полныйgo build ./... && go vet ./... && go test ./....
6. Выкладка и проверка
Пересборка и перезапуск control-api и admin-dashboard (меняется форма настроек и страница адреса); агенты и prober без изменений. Порядок: проверка пустой очереди, копия БД, тег отката образа, миграция 0012. Сбой на стенде воспроизвести нельзя, поэтому проверка там — штатный небольшой прогон без зависших queued; логику повторов покрывает тест на трёх валидаторах.
7. Риски и последствия принятого решения
- Время до
failу неисправного адреса ограничено потолком: 5 попыток по ~70 секунд, около 6 минут вместо ~5 минут сейчас (4 попытки). Очередь и другие валидаторы это не блокирует. - Потолок меньше числа валидаторов. Адрес, которому не повезло с 5 неисправными валидаторами подряд, получит
fail, хотя на остальных 15 прошёл бы. Это сознательный компромисс в пользу ограниченного времени; потолок меняется в настройках. - Адрес, исключённый на части валидаторов, может чуть дольше ждать подходящего валидатора.
- Если сбой общий (проблема облака для всех валидаторов), адрес в итоге получит
fail; это верное поведение. - Не решается: причина сбоя
v17в облаке (трансляция плавающего IP) — её нужно смотреть в OpenStack.
8. Решения по согласованию (все приняты)
- Потолок попыток задаётся независимо от числа валидаторов; значение меняется в меню настроек (
/settings) и через API. - Значение по умолчанию для текущего окружения — 5.
- Исключение действует только для проваленного self-check; на ошибки привязки не распространяется.
- «Self-check не прошёл на: …» сохраняется и отдаётся в API; показ на странице аналитики — следующим шагом.
- Изменения отражаются в разделе настроек UI (п.3.6).
Открытых вопросов нет. Жду команды начать реализацию.