Восстановление хранилища сессий

Все разговоры Hermes хранит в одном SQLite-файле на профиль — state.db. Рядом лежат два служебных файла, которыми SQLite управляет сам: state.db-wal (журнал упреждающей записи, write-ahead log) и state.db-shm. Работать с этим файлом одновременно могут несколько процессов Hermes — шлюз, десктопное приложение, дашборд, cron и команды CLI пишут в него через собственные блокировки SQLite, и это безопасно.

Небезопасно одно: переписывать хранилище, пока в него пишет другой процесс. Если так случилось, процессы, у которых осталась старая копия журнала, намеренно перестают писать, и на каждый ход отвечают сообщением примерно такого вида:

another Hermes process still holds an old copy of the session database’s write-ahead log, so Hermes stopped writing to keep the file safe …

По-русски: другой процесс Hermes всё ещё держит старую копию журнала базы сессий, и Hermes перестал писать, чтобы не повредить файл. Ссылка из этого сообщения ведёт как раз сюда. Данные при этом не теряются — отказ и нужен для того, чтобы ничего не пропало.

Починка в три шага

  1. Завершите все процессы Hermes этого профиля. Десктопное приложение, шлюз, дашборд, cron:

    hermes gateway stop          # для именованного профиля добавьте -p <profile>

    затем закройте десктопное приложение через его меню и остановите дашборд (hermes dashboard --stop) или свой сервис, если он у вас есть. Перезапустить один из них мало: пока хоть один процесс держит старый журнал, все новые будут отказывать.

  2. Спросите doctor, кто ещё держит журнал.

    hermes doctor                # для именованного профиля добавьте -p <profile>

    Пока кто-то держит выведенный из работы журнал, doctor печатает каждого такого держателя в виде PID N (command) с той же рекомендацией и пропускает свои проверки здоровья и любую работу --fix, чтобы самому не стать ещё одним писателем. Остановите перечисленные процессы и запускайте doctor снова, пока строка не исчезнет.

  3. Запустите Hermes заново (сначала один процесс — шлюз или десктопное приложение) и отправьте сообщение ещё раз. Разговор продолжится с того места, где остановился.

Чего не делать

  • Не запускайте hermes doctor --fix, пока процессы работают. Doctor отказывается делать чекпоинт, если видит процесс со старым журналом, но на хосте, где он не может заглянуть в список процессов, путь --fix окажется ровно тем вторым писателем, из-за которого всё и началось.
  • Не удаляйте state.db-wal и state.db-shm. В журнале лежат закоммиченные разговоры, которых ещё нет в state.db. Удаление — единственное действие, которое превращает отказ в настоящую потерю данных.
  • Не копируйте один state.db. Три файла — это единый образ. Пользуйтесь снимком (hermes backup) или hermes sessions recover, но никогда не cp state.db somewhere/.
  • Не просите агента это починить. Его собственная сессия лежит в том же хранилище, и он упрётся в тот же отказ.

Команды обслуживания отказываются работать, пока кто-то пишет

hermes sessions optimize, hermes sessions optimize-storage и hermes sessions prune переписывают хранилище (VACUUM, перестроение полнотекстового индекса, массовое удаление). Запуск любой из них под живым шлюзом — как раз тот путь, которым целый парк агентов приходит к описанному выше отказу. Поэтому теперь они сначала проверяют и отказываются работать, если базу держит другой процесс:

Refusing `hermes sessions optimize-storage`: another process is using ~/.hermes/state.db.
  PID 41230 (hermes gateway run): state.db, state.db-shm, state.db-wal
  PID 41355 (hermes serve --profile work): state.db-wal
Rewriting the database under a live writer is how every agent ends up refusing turns with the
retired state.db-wal error. Nothing is lost.
Stop them first (`hermes gateway stop`, quit the Desktop app, pause cron), then re-run.
Override with --force if you accept the risk.

Предпросмотр через --dry-run не блокируется никогда. --force запускает команду в любом случае — используйте его, только если точно знаете, что перечисленные процессы простаивают (например, это читатель, которого вы запустили сами). Та же проверка срабатывает, когда вы вводите sessions optimize в консоли десктопного приложения.

Какие файлы бывают рядом со state.db

Файл или каталогЧто этоЧто делать
state.db-wal, state.db-shmЖивой журнал упреждающей записи SQLite и его индекс в разделяемой памяти. Большой -wal — норма, пока работает шлюз или десктопное приложение.Не трогайте. На следующем чекпоинте они уменьшатся сами.
state.db.retired-wal-<timestamp>-<pid>/Снимок копии журнала, которую держал процесс в момент отказа от записи, плюс manifest.json с её описанием. Это материал для разбора, а не бэкап, который можно восстанавливать вслепую.Сохраните. Если после восстановления пропали разговоры прямо перед инцидентом, приложите каталог к баг-репорту: по manifest.json мейнтейнер определит, ложатся ли эти кадры поверх текущего файла.
state.db.pre-update-emergency-<timestamp>.bakСнимок, который делает программа обновления десктопного приложения, прежде чем трогать хранилище.Держите, пока какое-то время не поработаете с обновлённым приложением. Восстанавливайте только при остановленных процессах Hermes, и сначала — hermes sessions recover --source <file> --inspect-only.
state.db.corrupt.<timestamp>.bak, *.malformed-backupКопии файла, который Hermes нашёл повреждённым перед починкой или карантином.Не восстанавливайте их поверх state.db — в них то же повреждение. Сохраните для отчёта; когда всё придёт в норму, их можно удалить.
state-snapshots/Быстрые снимки, которые делают hermes update и hermes backup.Восстанавливайте при остановленных процессах Hermes; см. hermes backup.

Если три шага не помогли

Все процессы Hermes остановлены, hermes doctor больше никого не показывает, а шлюз после запуска всё равно отказывается писать? Тогда, возможно, повреждён сам файл. Снова остановите всё и осмотрите его без записи:

hermes sessions recover --source ~/.hermes/state.db --inspect-only

--inspect-only никогда не изменяет файл. Если команда сообщит, что хранилище можно восстановить, выполните напечатанную ею команду или восстановите самый свежий снимок из state-snapshots/. Как всё это устроено внутри, описано в руководстве разработчика: Восстановление базы состояния и Хранилище сессий.

перевод и редактура:

обновлено

⌕ ESC