docs: описать транзакционный guard и взаимное исключение операций
- docs/07: полный порядок staged apply, инвариант снятия guard, объяснение почему окно 45 секунд не обязано покрывать smoke и почему guard не трогает nftables.service, семантическая проверка эффективного firewall; - docs/11: разделы A5e/A5f для новых unit-тестов и серверные сценарии D1e (guard доходит до дедлайна), D1f (конкурентные операции), D1g (успешная операция не оставляет следов транзакции); матрица и acceptance criteria дополнены; - docs/12: разбор отказов "уже выполняется другая операция" и firewall_guard_fired; - docs/13: строки журнала guard в таблице recovery, новый раздел 8a про замок операций; - docs/14 и purge-v0.sh: очистка /run/hy2xs, замка операций и candidate-файлов firewall — /run это tmpfs, но очистка не имеет права требовать перезагрузки; - README: защита от потери доступа при смене firewall и раздел "Одна операция за раз"; - CHANGELOG: шестой проход.
This commit is contained in:
@@ -81,6 +81,29 @@ sudo -u hy2xs-admin test ! -r /etc/hy2xs/hy2xs.env
|
||||
| `rollback finished with N failed stage(s); manual recovery may be required` | итог: перечисленные стадии требуют ручной проверки |
|
||||
| `rollback completed: N stage(s) succeeded` | восстановление отработало полностью |
|
||||
| `manual recovery data preserved at /run/hy2xs/rollback/<op>` | firewall восстановлен не полностью; прежние `nftables.conf` и `hy2xs.nft` лежат по этому пути |
|
||||
| `firewall rollback guard armed: … fires in 45s` | guard взведён; с этого момента операция обязана снять его до фиксации успеха |
|
||||
| `firewall rollback guard disarmed and proven inactive` | guard снят, и это подтверждено состоянием юнитов и отсутствием маркера срабатывания |
|
||||
| `automatic firewall rollback has already fired` | guard успел сработать; сервер работает на **прежнем** firewall, операция обязана завершиться отказом |
|
||||
| `firewall rollback guard <unit> is still in state "…"` | остановить guard не удалось; фиксация успеха запрещена, разбирайтесь с systemd |
|
||||
|
||||
Отдельно про сработавший guard. Окно 45 секунд намеренно короче худшего случая
|
||||
smoke и не обязано его покрывать: доказательством служит не время, а маркер
|
||||
|
||||
```text
|
||||
/run/hy2xs/rollback/<op-id>/auto-rollback-fired
|
||||
```
|
||||
|
||||
Если он есть — операция откатывается независимо от результата smoke, и в
|
||||
маркере установки появляется `phase: firewall_guard_fired`. Это значит: сервер
|
||||
жив и работает на прежнем firewall, а причину, по которой проход не уложился в
|
||||
окно, надо искать в journal — обычно это медленный старт одного из сервисов.
|
||||
|
||||
Юнит автоотката при частичном восстановлении уходит в `failed`, поэтому его
|
||||
стоит прочитать целиком:
|
||||
|
||||
```bash
|
||||
journalctl -u 'hy2xs-fw-rollback-*' --no-pager
|
||||
```
|
||||
|
||||
Резервные копии не удаляются, пока восстановление не подтверждено, и переживают
|
||||
долговечную запись `phase: installed`. Поэтому при разборе неудачи всегда
|
||||
@@ -92,6 +115,8 @@ ls -la /etc/hy2xs/backups/ # копия состояния до пос
|
||||
cat /etc/hy2xs/backups/*/manifest.json
|
||||
```
|
||||
|
||||
Имя каталога совпадает с полем `op_id` в `/var/lib/hy2xs/install-state.json`.
|
||||
|
||||
Манифест прямо говорит, какие файлы существовали до операции, а какие нет:
|
||||
запись `"present": false` означает, что откат обязан был файл **удалить**, а не
|
||||
восстановить.
|
||||
@@ -100,6 +125,47 @@ cat /etc/hy2xs/backups/*/manifest.json
|
||||
проблему внутри отката: последняя — это информация о том, что осталось не
|
||||
восстановленным, а не причина отказа.
|
||||
|
||||
## 8a. Одна операция за раз
|
||||
|
||||
`install`, `reconfigure`, `repair` и `doctor` сериализованы эксклюзивным
|
||||
замком:
|
||||
|
||||
```text
|
||||
/run/lock/hy2xs-orchestrator.lock
|
||||
```
|
||||
|
||||
Вторая операция отказывает сразу и **до первой мутации** — до снятия резервной
|
||||
копии, до записи конфигов, до firewall:
|
||||
|
||||
```text
|
||||
another HY2XS operation is already in progress: reconfigure (pid 4242, started at …)
|
||||
```
|
||||
|
||||
`doctor` тоже берёт замок: диагностика в середине транзакции описывает
|
||||
промежуточное состояние сервера и выдаёт бессмысленные ошибки по временным
|
||||
несоответствиям.
|
||||
|
||||
`status` и `diagnostics collect` замок **не** берут — они нужны в том числе во
|
||||
время долгой операции, — но сообщают о ней:
|
||||
|
||||
```bash
|
||||
hy2xs-orchestrator status --package-dir /usr/local/lib/hy2xs/package
|
||||
# "operation_in_progress": "reconfigure (pid 4242, started at …)"
|
||||
```
|
||||
|
||||
Замок снимается при любом завершении держателя: штатном, по `Ctrl+C`, по
|
||||
SIGTERM от systemd и при обрыве SSH. Если процесс был убит `kill -9`, следующая
|
||||
операция обнаружит мёртвого держателя и переиспользует замок сама:
|
||||
|
||||
```text
|
||||
operation lock … is held by install (pid 1234), which is no longer running; reclaiming it
|
||||
```
|
||||
|
||||
`/run/lock` — это tmpfs, поэтому перезагрузка снимает замок в любом случае.
|
||||
Удалять файл руками нужно только если в нём оказалось непонятное содержимое:
|
||||
такой замок сознательно не переиспользуется автоматически — непонятый файл не
|
||||
доказывает, что операции нет.
|
||||
|
||||
## 9. Reconfigure flow
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user