Кэш списка бакета, БД без блокировки event loop, один процесс на БД
Производительность и масштабирование, пункты 8–10 ревью (docs/changes/019): - кэш списка бакета и связей файлов с копиями (BACKUPS_CACHE_TTL, 60 с): sync_rows только при реальном чтении бакета; сброс после бэкапа и удаления, счётчик поколений против гонки; refresh=1 и «Обновить список»; - обработчики API/UI без await — обычные функции (пул потоков FastAPI), запуск задач остаётся async; запись статуса, задачи и sync_rows — через asyncio.to_thread; SQLite: WAL, synchronous=NORMAL, busy_timeout; - файловая блокировка <файл БД>.lock: второй процесс на той же БД не стартует; раздел «Ограничения» в README. Тесты: 24 из 24. Стенд (порт 8001): кэш 0,014 с против 0,138 с, второй uvicorn на боевой БД отклонён блокировкой, боевые данные не изменены. Ручная проверка UI пользователем на момент коммита не подтверждена. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
123b5abdfc
commit
ba8ac7be71
17 files changed
+395
-110
No files matched your search
@@ -55,6 +55,11 @@ Admin Dashboard ⇄ Control Server ⇄ RouterOS REST (на каждом устр
|
||||
- Пароли устройств шифруются ключом `SECRET_KEY` (Fernet); потеря ключа = потеря доступа к сохранённым паролям.
|
||||
- **Секреты в бэкапах:** `.rsc` создаётся с `show-sensitive`, а `.backup` — без шифрования, поэтому файлы содержат пароли и ключи открытым текстом. Ограничьте доступ к бакету и ссылкам скачивания.
|
||||
|
||||
## Ограничения
|
||||
|
||||
- **Один процесс на БД**: сервер держит файловую блокировку `<файл БД>.lock` рядом с БД (снимается при остановке); второй процесс на той же БД (`--workers 2+`, вторая копия контейнера на том же томе) не стартует — понятная ошибка вместо молчаливой порчи данных. В памяти процесса (не переживает перезапуск и не разделяется между процессами) — семафор фоновых задач, блокировка синхронизации бэкапов, счётчики неудачных попыток очистки журнала и кэш списка бакета.
|
||||
- **Кэш списка бакета**: страница «Бэкапы» и `GET /api/v1/backups` не перечитывают бакет на каждый просмотр — список живёт `BACKUPS_CACHE_TTL` секунд (по умолчанию 60; `0` — кэш выключен). Изменения бакета, сделанные не через это приложение, видны не позже TTL или сразу — кнопкой «Обновить список» (`refresh=1`). Собственные изменения (бэкап, удаление) сбрасывают кэш сами.
|
||||
|
||||
## Запуск
|
||||
|
||||
```bash
|
||||
@@ -76,6 +81,7 @@ docker compose up -d --build # UI: http://localhost:8000, OpenAPI: /docs
|
||||
| `API_TOKEN` | `change-me` | Bearer-токен API |
|
||||
| `DATABASE_URL` | `sqlite:///./data/ros_control.db` | база метаданных (в compose — том) |
|
||||
| `S3_ENDPOINT`, `S3_REGION`, `S3_BUCKET`, `S3_ACCESS_KEY`, `S3_SECRET_KEY`, `S3_PREFIX` | Yandex Object Storage, `backups` | бакет для резервных копий |
|
||||
| `BACKUPS_CACHE_TTL` | `60` | кэш списка бакета, с; `0` — выключить (см. «Ограничения») |
|
||||
| `MAX_CONCURRENCY` | `10` | одновременные обращения к устройствам |
|
||||
| `ROS_CONNECT_TIMEOUT` | `4` | секунд на установление соединения (скорость обнаружения недоступности) |
|
||||
| `ROS_TIMEOUT` | `30` | секунд на ответ устройства (долгие операции) |
|
||||
@@ -89,7 +95,7 @@ docker compose up -d --build # UI: http://localhost:8000, OpenAPI: /docs
|
||||
python3 -m venv venv && ./venv/bin/pip install -r requirements.txt
|
||||
set -a; . ./.env; set +a
|
||||
./venv/bin/uvicorn app.main:app --reload
|
||||
./venv/bin/python -m pytest # 22 теста, фоновый опрос в тестах выключен
|
||||
./venv/bin/python -m pytest # 24 теста, фоновый опрос в тестах выключен
|
||||
```
|
||||
|
||||
## API v1
|
||||
@@ -115,7 +121,7 @@ curl -s -H "Authorization: Bearer $API_TOKEN" http://localhost:8000/api/v1/devic
|
||||
| PATCH/DELETE | `/api/v1/groups/{id}` | переименовать / удалить (устройства остаются без группы) |
|
||||
| POST | `/api/v1/batch/{backup\|ros_update\|fw_update}` | `{"device_ids": [...]}` и/или `{"group_id": N}` — групповая операция (задача) |
|
||||
| PUT | `/api/v1/batch/channel` | `{"device_ids": [...] или "group_id": N, "channel": "..."}` — групповая смена канала (задача `set_channel`) → 202 `{"job_ids": [...]}` |
|
||||
| GET/DELETE | `/api/v1/backups`, `/backups/download?key=` | бэкапы в бакете (фильтры `device_id`, `group`, `kind`, `date_from`, `date_to`, `q`) |
|
||||
| GET/DELETE | `/api/v1/backups`, `/backups/download?key=` | бэкапы в бакете (фильтры `device_id`, `group`, `kind`, `date_from`, `date_to`, `q`; `refresh=1` — минуя кэш) |
|
||||
| POST | `/api/v1/backups/delete` | групповое удаление файлов: `{"keys": [...]}` → `{"deleted": N, "failed": M}` |
|
||||
| GET | `/api/v1/jobs`, `/jobs/{id}` | состояние задач |
|
||||
| GET | `/api/v1/events`, `/events/{id}` | журнал событий (фильтры `entity_id`, `type`, `device_id`, `job_id`, `actor`, `date_from`, `date_to`, `q`; постранично `before=<evt_…>`, `limit`) |
|
||||
@@ -154,3 +160,4 @@ curl -s -H "Authorization: Bearer $API_TOKEN" http://localhost:8000/api/v1/devic
|
||||
- `016-unique-ids-and-event-log` — глобально уникальные ID (префикс + UUIDv7) для всех сущностей, журнал событий в БД, миграция числовых ID.
|
||||
- `017-events-ui-rotation` — страница «Журнал» в UI, настройки ротации, очистка журнала с подтверждением паролем.
|
||||
- `018-correctness-consistency` — одиночное удаление бэкапа в UI через общий сервис `delete_many`, единая система миграций (колонки старой схемы — в `migrations.run` до миграции ID), групповая смена канала фоновыми задачами (`set_channel`).
|
||||
- `019-performance-scaling` — кэш списка бакета (`BACKUPS_CACHE_TTL`, «Обновить список»); обработчики без обращений к event loop — обычные функции (пул потоков FastAPI), запись статуса и тяжёлые операции с БД в фоне — через `asyncio.to_thread`; SQLite — WAL и `busy_timeout`; файловая блокировка БД — один процесс на БД.
|
||||
Reference in new issue
Block a user