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>
This commit is contained in:
ayurishchevandClaude Sonnet 5.5 committed 2026-10-02 16:45:59 +03:00
1 parent 0532baff09
commit 1366ecbdea
2 files changed
+249

No files matched your search

+248
View File
@@ -0,0 +1,248 @@
# Ручная очистка базы данных 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](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. Проверить, что в очереди ничего не в работе:**
```bash
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 считает привязанным:
```bash
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.** Он держит базу открытой и пишет в неё на каждом такте; ручные правки поверх работающего процесса
ненадёжны.
```bash
cd rxprod-compose && docker compose stop control-api # compose-развёртывание
sudo systemctl stop control-api # systemd
```
**2.4. Сделать резервную копию** (штатной командой SQLite, не `cp`: у базы есть журнал `-wal`):
```bash
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. Посмотреть, что и сколько лежит** (до и после чистки):
```bash
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. Полный сброс данных прогона (перед новым полным прогоном)
Очищает очередь, реестр адресов, реестр проверок, журнал событий. Валидаторы, площадки, цели, типы проверок и все настройки остаются.
```sql
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;
```
Затем освободите место (отдельной командой, не внутри транзакции):
```sql
PRAGMA wal_checkpoint(TRUNCATE);
VACUUM;
```
Файл сжимается до сотен килобайт (на проверочной копии: 20 МБ → 128 КБ).
### 3.2. Только журнал событий
Очередь, реестр и проверки не затрагиваются.
Весь журнал:
```sql
DELETE FROM events;
DELETE FROM sqlite_sequence WHERE name = 'events';
```
Только старше 7 дней (число дней меняйте в `'-7 days'`):
```sql
DELETE FROM events
WHERE occurred_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-7 days');
```
### 3.3. Только реестр проверок (история проверок)
Адреса и их итоги в очереди остаются; пропадает подробная история проверок (на странице «Реестр» исчезнут результаты).
Вся история:
```sql
DELETE FROM checks;
DELETE FROM sqlite_sequence WHERE name = 'checks';
```
Оставить последние 3 цикла каждого адреса (число `3` меняйте):
```sql
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).
```sql
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`):
```sql
PRAGMA wal_checkpoint(TRUNCATE);
VACUUM;
```
Размер и свободные страницы:
```sql
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. После очистки: запуск и проверка
```bash
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).
Проверка базы (до запуска или на копии):
```bash
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. Восстановление из копии
```bash
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 сверяет их на каждом такте и исправит расхождение,
но до этого результат непредсказуем.