Files
ayurishchevandClaude Sonnet 5.5 1366ecbdea Add the admin guide for manual database cleanup (SQL)
docs/ADMIN_CLEANUP.md: what is in the control-api database and what must
not be touched, preparation (stop, backup, checks), ready SQL for a full
reset before a new run, the event log, the check registry, single
addresses and compaction, verification after the cleanup, restore from a
backup, and what to do through the API instead. Every SQL block was run
on a copy of the production backup. Linked from the README.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-02 16:45:59 +03:00

14 KiB

Ручная очистка базы данных control-api (SQL)

Когда нужна: подготовка к новому полному прогону, разбор после инцидента, освобождение места, удаление отдельных адресов. Все примеры проверены 2026-10-02 на копии боевой БД (20 валидаторов, 4 площадки, 6445 адресов в реестре, 29 889 проверок, 19 897 событий): без ошибок, integrity_check = ok, нарушений внешних ключей нет.

Сначала API. Если в очереди есть адреса в работе, очищайте очередь штатно: кнопка «Очистить всё» на /ips или POST /api/v1/admin/ips/clear (см. USAGE.md). Только так control-api отвяжет Floating IP от портов валидаторов. SQL ниже работает с базой «в покое»: он не обращается к OpenStack.

1. Что в базе и что нельзя трогать

Группа Таблицы Можно чистить
Данные прогона ip_queue (очередь), ip_registry (реестр адресов), checks (реестр проверок), ip_site_checks (признаки площадок по адресам в работе), events (журнал событий) да
Настройки (не трогать) validators, sites, target_groups (цели), check_types, inbound_checks_settings, settings, auto_cycle нет
Служебное sqlite_sequence (нумерация записей), PRAGMA user_version (версия схемы) нумерацию можно сбросить, версию не менять

Связи (внешние ключи): checks, events, ip_site_checks ссылаются на ip_queue; checks, events, ip_queue — на ip_registry; validators.current_ip_id — на ip_queue. Поэтому порядок удаления всегда такой: сначала validators.current_ip_id в NULL, затем ip_site_checks, checks, events, ip_queue и в конце ip_registry. Времена в БД хранятся строками 2026-10-02T07:14:58.857Z (UTC).

2. Подготовка (всегда, перед любой очисткой)

2.1. Где лежит база и чем её открывать. Нужен клиент sqlite3 на хосте (apt install sqlite3).

Развёртывание Файл базы
rxprod-compose/ (боевой стенд) rxprod-compose/capi-db/control-api.db
Docker (deploy/docker) в томе cloud-ip-validator-db: docker volume inspect cloud-ip-validator-db --format '{{.Mountpoint}}', файл control-api.db в этом каталоге (нужен root)
systemd /var/lib/cloud-ip-validator/control-api.db (database.path в control-api.yaml)

Дальше в примерах DB=путь/к/control-api.db.

2.2. Проверить, что в очереди ничего не в работе:

sqlite3 -readonly "$DB" "
SELECT state, COUNT(*) FROM ip_queue
WHERE state NOT IN ('done', 'failed', 'occupied') GROUP BY state;"

Пустой вывод — всё завершено. Адреса в работе есть — сначала «Очистить всё» через API (см. выше). Затем проверьте в OpenStack, что на портах валидаторов нет лишних Floating IP; по базе видно только то, что control-api считает привязанным:

sqlite3 -readonly "$DB" "
SELECT ip_address, state, fip_id FROM ip_queue
WHERE fip_id <> '' AND state NOT IN ('done', 'failed', 'occupied');"

2.3. Остановить control-api. Он держит базу открытой и пишет в неё на каждом такте; ручные правки поверх работающего процесса ненадёжны.

cd rxprod-compose && docker compose stop control-api      # compose-развёртывание
sudo systemctl stop control-api                           # systemd

2.4. Сделать резервную копию (штатной командой SQLite, не cp: у базы есть журнал -wal):

TS=$(date -u +%Y-%m-%d_%H-%M)
sqlite3 "$DB" ".backup '$(dirname "$DB")/backup-$TS-before-cleanup.db'"
sqlite3 -readonly "$(dirname "$DB")/backup-$TS-before-cleanup.db" "PRAGMA integrity_check;"   # должно быть: ok

Лог контейнера до чистки при необходимости сохраните отдельно: docker logs <контейнер> > backup-$TS.container.log 2>&1.

2.5. Посмотреть, что и сколько лежит (до и после чистки):

sqlite3 -readonly "$DB" "
SELECT 'ip_queue' AS tbl, COUNT(*) AS n FROM ip_queue
UNION ALL SELECT 'ip_registry', COUNT(*) FROM ip_registry
UNION ALL SELECT 'checks', COUNT(*) FROM checks
UNION ALL SELECT 'ip_site_checks', COUNT(*) FROM ip_site_checks
UNION ALL SELECT 'events', COUNT(*) FROM events
UNION ALL SELECT 'validators', COUNT(*) FROM validators
UNION ALL SELECT 'sites', COUNT(*) FROM sites;"

3. Сценарии

Команды выполняются так: sqlite3 "$DB" и вставить блок, либо сохранить блок в файл и выполнить sqlite3 "$DB" < файл.sql.

3.1. Полный сброс данных прогона (перед новым полным прогоном)

Очищает очередь, реестр адресов, реестр проверок, журнал событий. Валидаторы, площадки, цели, типы проверок и все настройки остаются.

PRAGMA foreign_keys = ON;
BEGIN;
-- валидаторы больше не ссылаются на адреса очереди
UPDATE validators SET current_ip_id = NULL WHERE current_ip_id IS NOT NULL;
UPDATE validators SET state = 'idle' WHERE state = 'assigned';
-- порядок важен: сначала зависимые таблицы
DELETE FROM ip_site_checks;
DELETE FROM checks;
DELETE FROM events;
DELETE FROM ip_queue;
DELETE FROM ip_registry;
-- нумерация снова с 1 (необязательно)
DELETE FROM sqlite_sequence WHERE name IN ('ip_registry', 'checks', 'ip_queue', 'events');
COMMIT;

Затем освободите место (отдельной командой, не внутри транзакции):

PRAGMA wal_checkpoint(TRUNCATE);
VACUUM;

Файл сжимается до сотен килобайт (на проверочной копии: 20 МБ → 128 КБ).

3.2. Только журнал событий

Очередь, реестр и проверки не затрагиваются.

Весь журнал:

DELETE FROM events;
DELETE FROM sqlite_sequence WHERE name = 'events';

Только старше 7 дней (число дней меняйте в '-7 days'):

DELETE FROM events
WHERE occurred_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-7 days');

3.3. Только реестр проверок (история проверок)

Адреса и их итоги в очереди остаются; пропадает подробная история проверок (на странице «Реестр» исчезнут результаты).

Вся история:

DELETE FROM checks;
DELETE FROM sqlite_sequence WHERE name = 'checks';

Оставить последние 3 цикла каждого адреса (число 3 меняйте):

DELETE FROM checks
WHERE cycle_id <= (SELECT MAX(c2.cycle_id) FROM checks c2 WHERE c2.registry_id = checks.registry_id) - 3;

Постоянное ограничение глубины истории лучше задать настройкой history_retention_cycles (страница /settings или PUT /api/v1/admin/config/orchestrator): control-api сам подрезает историю при завершении каждого адреса. SQL выше нужен для разовой чистки.

3.4. Удалить конкретные адреса целиком

Удаляет адрес из очереди и реестра вместе со всей его историей (проверки и события). Список адресов подставьте в первую команду CREATE TEMP TABLE doomed_reg. Адрес, который сейчас проверяется, удалять этим способом нельзя: используйте API (раздел 5).

PRAGMA foreign_keys = ON;
BEGIN;
CREATE TEMP TABLE doomed_reg AS
  SELECT id FROM ip_registry WHERE ip_address IN ('5.188.140.6', '5.188.140.62');   -- ваши адреса
CREATE TEMP TABLE doomed_ip AS
  SELECT id FROM ip_queue WHERE registry_id IN (SELECT id FROM doomed_reg);
UPDATE validators SET current_ip_id = NULL WHERE current_ip_id IN (SELECT id FROM doomed_ip);
DELETE FROM ip_site_checks WHERE ip_id IN (SELECT id FROM doomed_ip);
DELETE FROM checks WHERE registry_id IN (SELECT id FROM doomed_reg);
DELETE FROM events WHERE registry_id IN (SELECT id FROM doomed_reg) OR ip_id IN (SELECT id FROM doomed_ip);
DELETE FROM ip_queue WHERE id IN (SELECT id FROM doomed_ip);
DELETE FROM ip_registry WHERE id IN (SELECT id FROM doomed_reg);
DROP TABLE doomed_ip;
DROP TABLE doomed_reg;
COMMIT;

3.5. Только освободить место

Если удалили много, а файл не уменьшился (SQLite не отдаёт место ОС до VACUUM):

PRAGMA wal_checkpoint(TRUNCATE);
VACUUM;

Размер и свободные страницы:

SELECT page_count * page_size / 1024 AS size_kb, freelist_count * page_size / 1024 AS free_kb
FROM pragma_page_count(), pragma_page_size(), pragma_freelist_count();

4. После очистки: запуск и проверка

cd rxprod-compose && docker compose up -d --no-deps control-api      # или: sudo systemctl start control-api

Если нужно, чтобы и лог контейнера начался с нуля, пересоздайте контейнер: docker compose up -d --force-recreate --no-deps control-api (старый лог сохраните заранее, п. 2.4).

Проверка базы (до запуска или на копии):

sqlite3 -readonly "$DB" "
PRAGMA integrity_check;
PRAGMA foreign_key_check;
SELECT state, COUNT(*) FROM validators GROUP BY state;
SELECT COUNT(*) AS validators_with_address FROM validators WHERE current_ip_id IS NOT NULL;
SELECT COUNT(*) AS queue_rows FROM ip_queue;"

Ожидается: ok; пустой результат foreign_key_check; у валидаторов состояние idle; validators_with_address = 0; для полного сброса queue_rows = 0. Через API: GET /api/v1/admin/status (total_ips = 0, total_validators = число валидаторов) и GET /api/v1/admin/validators (через 10–15 секунд после запуска у всех свежий last_heartbeat_at).

5. Что делать через API, а не через SQL

Задача Как
Остановить проверку адреса, удалить адрес в работе POST /api/v1/admin/ips/{ip}/cancel, DELETE /api/v1/admin/ips/{ip}
Очистить всю очередь с отвязкой Floating IP POST /api/v1/admin/ips/clear
Перепроверить завершённые адреса POST /api/v1/admin/ips со списком адресов
Валидатор «завис» с адресом ничего не править: лизинг истечёт, адрес вернётся в очередь, валидатор освободится сам
Добавить/убрать валидатор, площадку, цель /api/v1/admin/config/* или страницы дашборда

6. Восстановление из копии

cd rxprod-compose && docker compose stop control-api
cp capi-db/backup-<метка>-before-cleanup.db capi-db/control-api.db
rm -f capi-db/control-api.db-wal capi-db/control-api.db-shm       # старый журнал к новой копии не относится
docker compose up -d --no-deps control-api

7. Ловушки

  • Не выполняйте DELETE при работающем control-api: он пишет в ту же базу.
  • Не удаляйте строки настроек (validators, sites, target_groups, check_types, inbound_checks_settings, settings, auto_cycle): после этого control-api либо не стартует, либо работает без площадок и целей.
  • Не нарушайте порядок удаления (раздел 1) и не отключайте PRAGMA foreign_keys = ON в блоках выше: база сама остановит ошибочное удаление.
  • VACUUM нельзя вызывать внутри транзакции и пока control-api запущен.
  • Не копируйте файл базы командой cp при работающем процессе: журнал -wal останется в неконсистентном состоянии. Используйте .backup.
  • Не меняйте PRAGMA user_version: по нему control-api применяет миграции схемы.
  • Не правьте состояние адресов и валидаторов вручную (state, owner_validator_id, lease_expires_at) вместо API: control-api сверяет их на каждом такте и исправит расхождение, но до этого результат непредсказуем.