guard_empty в deploy/backup/fs-backup.sh:180-194 должен не давать пустым данным (новый Pi до восстановления) стать «последним снимком». Работает он на счётчиках из db_counts:
db_counts(){# → «игроков|партий»
sqlite3 "file:$1?immutable=1""SELECT (SELECT count(*) FROM users ...), (SELECT count(*) FROM matches);" 2>/dev/null \
||echo"?|?"}
guard_empty(){if["$1" !=0]||["$2" !=0];thenreturn 0;fi# «?» != 0 → считается НЕпустой
...
Если в БД ещё нет таблиц users/matches, запрос падает, счётчики становятся ?|?, и проверка пропускает снимок как непустой. PRAGMA integrity_check у такой БД проходит. В результате в оба репозитория уходит снимок с метками players:?/matches:?, и он становится latest.
Метка ? при следующих проверках читается как 0 (tonumber? // 0). После такого снимка защита перестаёт срабатывать и для последующих пустых БД с таблицами.
Когда реально случается
Сценарий «Катастрофа: Pi умер» (deploy/backup/README.md, раздел 9). На новом Pi docker compose up -d запускает app и backup одновременно. backup сразу делает первый бэкап, потому что локальных снимков нет. Если он попадёт в момент, когда alembic upgrade head уже создал файл БД, но ещё не создал таблицы, получится снимок без данных. В VPS-репозитории он станет latest, и шаг README fs-backup restore latest --repo vps упадёт на verify_staging («нет ключевых таблиц»). Данные при этом не теряются, но в самый стрессовый момент восстановление ломается.
Чаще первый бэкап просто падает раньше с «БД … не найдена» (файл ещё не создан) — это безопасно. Окно гонки узкое, но оно ровно в том сценарии, ради которого защита написана.
Что предлагается
В guard_empty считать неизвестные счётчики (?) не «непустыми», а поводом отказаться (как при пустой БД, с подсказкой про --allow-empty), либо отдельно проверять наличие ключевых таблиц, как делает verify_staging.
В entrypoint.sh перед первым бэкапом дождаться готовности приложения (например, wget app:8000/api/health) или в compose сделать backup зависимым от здоровья app (с оглядкой на то, что restore требует остановленного app).
В resolve_snapshot latest / README раздела 9 подсказать выбирать последний снимок с ненулевыми счётчиками.
Найдено при сверке документации с кодом.
## Проблема
`guard_empty` в `deploy/backup/fs-backup.sh:180-194` должен не давать пустым данным (новый Pi до восстановления) стать «последним снимком». Работает он на счётчиках из `db_counts`:
```sh
db_counts() { # → «игроков|партий»
sqlite3 "file:$1?immutable=1" "SELECT (SELECT count(*) FROM users ...), (SELECT count(*) FROM matches);" 2>/dev/null \
|| echo "?|?"
}
guard_empty() {
if [ "$1" != 0 ] || [ "$2" != 0 ]; then return 0; fi # «?» != 0 → считается НЕпустой
...
```
Если в БД ещё нет таблиц `users`/`matches`, запрос падает, счётчики становятся `?|?`, и проверка **пропускает** снимок как непустой. `PRAGMA integrity_check` у такой БД проходит. В результате в оба репозитория уходит снимок с метками `players:?`/`matches:?`, и он становится `latest`.
Метка `?` при следующих проверках читается как 0 (`tonumber? // 0`). После такого снимка защита перестаёт срабатывать и для последующих пустых БД с таблицами.
## Когда реально случается
Сценарий «Катастрофа: Pi умер» (`deploy/backup/README.md`, раздел 9). На новом Pi `docker compose up -d` запускает `app` и `backup` одновременно. `backup` сразу делает первый бэкап, потому что локальных снимков нет. Если он попадёт в момент, когда `alembic upgrade head` уже создал файл БД, но ещё не создал таблицы, получится снимок без данных. В VPS-репозитории он станет `latest`, и шаг README `fs-backup restore latest --repo vps` упадёт на `verify_staging` («нет ключевых таблиц»). Данные при этом не теряются, но в самый стрессовый момент восстановление ломается.
Чаще первый бэкап просто падает раньше с «БД … не найдена» (файл ещё не создан) — это безопасно. Окно гонки узкое, но оно ровно в том сценарии, ради которого защита написана.
## Что предлагается
- [ ] В `guard_empty` считать неизвестные счётчики (`?`) не «непустыми», а поводом отказаться (как при пустой БД, с подсказкой про `--allow-empty`), либо отдельно проверять наличие ключевых таблиц, как делает `verify_staging`.
- [ ] В `entrypoint.sh` перед первым бэкапом дождаться готовности приложения (например, `wget app:8000/api/health`) или в compose сделать `backup` зависимым от здоровья `app` (с оглядкой на то, что restore требует остановленного app).
- [ ] В `resolve_snapshot latest` / README раздела 9 подсказать выбирать последний снимок с ненулевыми счётчиками.
Найдено при сверке документации с кодом.
guard_empty: неизвестные счётчики (?, в БД нет таблиц users/matches) — отказ, как при пустой БД, с понятной ошибкой и подсказкой про --allow-empty.
entrypoint.sh: перед самым первым бэкапом ждать /api/health приложения (до 10 минут; health отвечает только после миграций и bootstrap). Не дождались — попытка всё равно, защита из п. 1 не пропустит БД без таблиц. Зависимость backup → app healthy в compose не добавляю: при восстановлении app остановлен.
README, раздел 9: обновить ожидаемые строки журнала; совет выбирать снимок с числами оставить. Снимки с ? больше не создаются, но могли остаться. Семантику restore latest не меняю.
Критерии готовности
run на БД без таблиц отказывает, снимок не создаётся; run --allow-empty проходит.
Первый бэкап ждёт здоровья app, но не дольше 10 минут; образ собирается и проверен локально.
Ветка:issue-74-backup-guard от dev
## План выполнения
1. `guard_empty`: неизвестные счётчики (`?`, в БД нет таблиц `users`/`matches`) — отказ, как при пустой БД, с понятной ошибкой и подсказкой про `--allow-empty`.
2. `entrypoint.sh`: перед самым первым бэкапом ждать `/api/health` приложения (до 10 минут; health отвечает только после миграций и bootstrap). Не дождались — попытка всё равно, защита из п. 1 не пропустит БД без таблиц. Зависимость `backup → app healthy` в compose не добавляю: при восстановлении `app` остановлен.
3. README, раздел 9: обновить ожидаемые строки журнала; совет выбирать снимок с числами оставить. Снимки с `?` больше не создаются, но могли остаться. Семантику `restore latest` не меняю.
**Критерии готовности**
- `run` на БД без таблиц отказывает, снимок не создаётся; `run --allow-empty` проходит.
- Первый бэкап ждёт здоровья `app`, но не дольше 10 минут; образ собирается и проверен локально.
**Ветка:** `issue-74-backup-guard` от `dev`
Итог: БД без таблиц (счётчики ?) теперь даёт отказ бэкапа, как пустая БД (обход — --allow-empty); первый бэкап нового контейнера ждёт /api/health приложения до 10 минут; README раздела 9 обновлён. Проверки: образ собран локально — отказ на БД без таблиц (репозиторий не создан), обход --allow-empty, нормальный снимок, ожидание приложения с укороченным таймаутом. Статус:Status/In Review Осталось за вами: ревью и мёрж PR; после — пересборка образа backup (scripts/build-push.ps1).
Работа выполнена, открыт PR: https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/pulls/100
**Итог:** БД без таблиц (счётчики `?`) теперь даёт отказ бэкапа, как пустая БД (обход — `--allow-empty`); первый бэкап нового контейнера ждёт `/api/health` приложения до 10 минут; README раздела 9 обновлён.
**Проверки:** образ собран локально — отказ на БД без таблиц (репозиторий не создан), обход `--allow-empty`, нормальный снимок, ожидание приложения с укороченным таймаутом.
**Статус:** `Status/In Review`
**Осталось за вами:** ревью и мёрж PR; после — пересборка образа `backup` (`scripts/build-push.ps1`).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Проблема
guard_emptyвdeploy/backup/fs-backup.sh:180-194должен не давать пустым данным (новый Pi до восстановления) стать «последним снимком». Работает он на счётчиках изdb_counts:Если в БД ещё нет таблиц
users/matches, запрос падает, счётчики становятся?|?, и проверка пропускает снимок как непустой.PRAGMA integrity_checkу такой БД проходит. В результате в оба репозитория уходит снимок с меткамиplayers:?/matches:?, и он становитсяlatest.Метка
?при следующих проверках читается как 0 (tonumber? // 0). После такого снимка защита перестаёт срабатывать и для последующих пустых БД с таблицами.Когда реально случается
Сценарий «Катастрофа: Pi умер» (
deploy/backup/README.md, раздел 9). На новом Pidocker compose up -dзапускаетappиbackupодновременно.backupсразу делает первый бэкап, потому что локальных снимков нет. Если он попадёт в момент, когдаalembic upgrade headуже создал файл БД, но ещё не создал таблицы, получится снимок без данных. В VPS-репозитории он станетlatest, и шаг READMEfs-backup restore latest --repo vpsупадёт наverify_staging(«нет ключевых таблиц»). Данные при этом не теряются, но в самый стрессовый момент восстановление ломается.Чаще первый бэкап просто падает раньше с «БД … не найдена» (файл ещё не создан) — это безопасно. Окно гонки узкое, но оно ровно в том сценарии, ради которого защита написана.
Что предлагается
guard_emptyсчитать неизвестные счётчики (?) не «непустыми», а поводом отказаться (как при пустой БД, с подсказкой про--allow-empty), либо отдельно проверять наличие ключевых таблиц, как делаетverify_staging.entrypoint.shперед первым бэкапом дождаться готовности приложения (например,wget app:8000/api/health) или в compose сделатьbackupзависимым от здоровьяapp(с оглядкой на то, что restore требует остановленного app).resolve_snapshot latest/ README раздела 9 подсказать выбирать последний снимок с ненулевыми счётчиками.Найдено при сверке документации с кодом.
План выполнения
guard_empty: неизвестные счётчики (?, в БД нет таблицusers/matches) — отказ, как при пустой БД, с понятной ошибкой и подсказкой про--allow-empty.entrypoint.sh: перед самым первым бэкапом ждать/api/healthприложения (до 10 минут; health отвечает только после миграций и bootstrap). Не дождались — попытка всё равно, защита из п. 1 не пропустит БД без таблиц. Зависимостьbackup → app healthyв compose не добавляю: при восстановленииappостановлен.?больше не создаются, но могли остаться. Семантикуrestore latestне меняю.Критерии готовности
runна БД без таблиц отказывает, снимок не создаётся;run --allow-emptyпроходит.app, но не дольше 10 минут; образ собирается и проверен локально.Ветка:
issue-74-backup-guardотdevРабота выполнена, открыт PR: #100
Итог: БД без таблиц (счётчики
?) теперь даёт отказ бэкапа, как пустая БД (обход —--allow-empty); первый бэкап нового контейнера ждёт/api/healthприложения до 10 минут; README раздела 9 обновлён.Проверки: образ собран локально — отказ на БД без таблиц (репозиторий не создан), обход
--allow-empty, нормальный снимок, ожидание приложения с укороченным таймаутом.Статус:
Status/In ReviewОсталось за вами: ревью и мёрж PR; после — пересборка образа
backup(scripts/build-push.ps1).