docs: MAM baseline 0.8.4 — best-effort archival and offline repair

Document the 'archive redo baseline changed in place' failure, the runtime
session isolation, the offline storage-checkpoint repair, and the Prosody
native-format comparison (list_append in-place vs list_store atomic rename).
This commit is contained in:
wt committed 2026-10-21 18:57:51 +07:00
1 parent 100005379b
commit 75bb730d3c
3 files changed
+99 -27

No files matched your search

+32 -1
View File
@@ -235,7 +235,7 @@ bind2_archive_latency_network.py, archive_edges.rs, session_routing_isolation.rs
После изменений обновляйте точные границы совместимости и результаты проверки. После изменений обновляйте точные границы совместимости и результаты проверки.
Не перезаписывайте исторические отчёты как будто они относятся к новой сборке. Не перезаписывайте исторические отчёты как будто они относятся к новой сборке.
В передаваемый ZIP включайте AGENTS.md, workflow, vendor reference, исходники, В передаваемый ZIP включайте AGENTS.md, workflow, vendor reference, исходники,
документацию и scripts; исключайте target, demo data, bin, .git, __pycache__, pyc документацию и scripts; исключайте target, demo data, bin, .git, **pycache**, pyc
и секреты. Сохраняйте executable bits shell scripts и проверяйте содержимое ZIP. и секреты. Сохраняйте executable bits shell scripts и проверяйте содержимое ZIP.
## Durable message IO 0.8.3 ## Durable message IO 0.8.3
@@ -298,3 +298,34 @@ Final binary SHA-256: 8ceb8de3a2d02bc4399b8ae817dff7e3c3fe9bff83dc0c6669b7f19210
Подробные samples/границы — docs/VALIDATION-0.8.3.md. Checksum и JSON syntax всех frames Подробные samples/границы — docs/VALIDATION-0.8.3.md. Checksum и JSON syntax всех frames
проверяются при SM replay, но Record создаётся только для последнего проверяются при SM replay, но Record создаётся только для последнего
snapshot. Recovery WAL sync до публикации обязателен; EIO fixture защищает это. snapshot. Recovery WAL sync до публикации обязателен; EIO fixture защищает это.
## Monal MAM baseline 0.8.4
Баг: «archive redo baseline changed in place» на живом сервере — внешняя
перезапись native `.list` поверх зафиксированного redo-baseline (бакап/копия
data_path/Prosody/старая версия) делала каждый `archive_for_user` Err, `?` ронял
session, клиент (Monal-iOS) реконнектился и не доставлял сообщение. Причина не в
обычном append — baseline ломает только перезапись первых `base` байт в обход
`.rredo`; наш Rust-append пишет только в конец.
Фикс (commit 1000053): 1) MAM-архивация на личной доставке (отправитель,
получатель, входящая федерация) стала best-effort `archive_message_best_effort` —
storage-ошибка не роняет session и не роняет доставку, в лог `mam-archive-skipped`
(санитизированный `sanitized_label`, без payload); 2) offline `storage-checkpoint`
теперь ремонтирует непроверенный baseline: `prepare_rewrite` через
`is_untrusted_redo` считает текущий синхронизированный native авторитетным и
удаляет неприменимый журнал (`retire_redo`). Живой append остаётся fail-closed.
Новые tests: `archive.rs` `runtime_append_fails_closed_...`/
`offline_checkpoint_retires_...`, сетевой `tests/mam_broken_baseline_network.py`
(перезапись baseline не роняет доставку; checkpoint возвращает MAM).
Сравнение с Prosody: `util/datamanager.lua` `list_append` пишет в конец, а
`list_store` (перезапись) делает atomic rename — меняет первые bytes baseline;
`plugins/mod_storage_internal.lua` `archive:append` для MUC/`key` и trim/delete
делает `list_store` (полная перезапись). Prosody не понимает `.rredo` sidecar. Наш
native `.list` сохраняет Prosody-совместимый `item(...)` формат.
Операционный ремонт пользователя: остановить сервер, `./target/release/rust-xmpp
storage-checkpoint CONFIG`, запустить снова. `--experimental` не снимать. Полная
замена Prosody требует реального CI, внешней федерации, настоящих клиентов/push,
deployment/UID/TLS, rollback и длительной нагрузки — см. COMPATIBILITY.md.
+28 -17
View File
@@ -7,23 +7,23 @@
неизвестные явно включённые MUC-модули отклоняются при запуске. неизвестные явно включённые MUC-модули отклоняются при запуске.
Это не означает полного совпадения поведения с Prosody. Это не означает полного совпадения поведения с Prosody.
| Область | Что реализовано | Существенные пределы | | Область | Что реализовано | Существенные пределы |
|---|---|---| | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Конфигурация | Lua/JSON/YAML/YML/TOML, Include, глобальные и локальные области, наследование модулей | Lua VM ограничена; не все функции окружения конфигуратора Prosody доступны. Runtime обслуживает один VirtualHost и его компоненты | | Конфигурация | Lua/JSON/YAML/YML/TOML, Include, глобальные и локальные области, наследование модулей | Lua VM ограничена; не все функции окружения конфигуратора Prosody доступны. Runtime обслуживает один VirtualHost и его компоненты |
| Хранилище | internal `.dat`, `.list`, бинарные строки, смешанные ключи Lua, атомарные записи, потоковые архивы, инвалидирование `.lidx` | SQL, external storage остальных модулей и произвольные драйверы не реализованы; S3 uploads поддержаны. Каталог блокируется между процессами Rust; Prosody в блокировке не участвует | | Хранилище | internal `.dat`, `.list`, бинарные строки, смешанные ключи Lua, атомарные записи, потоковые архивы, инвалидирование `.lidx` | SQL, external storage остальных модулей и произвольные драйверы не реализованы; S3 uploads поддержаны. Каталог блокируется между процессами Rust; Prosody в блокировке не участвует |
| Аккаунты, saslauth | internal_hashed SHA-1/SHA-256, SASLprep, PLAIN внутри TLS, SCRAM и PLUS с tls-exporter/tls-server-end-point, SSDP, SASL2 | Только internal_hashed; опциональный sasl2_bind2 (alias bind2) поддерживает inline bind/SM/CSI/Carbons. SASL2 inline SM resume и takeover живого транспорта реализованы; Bind2/MAM metadata читает endpoints без полного scan; полные MAM queries всё ещё могут сканировать историю | | Аккаунты, saslauth | internal_hashed SHA-1/SHA-256, SASLprep, PLAIN внутри TLS, SCRAM и PLUS с tls-exporter/tls-server-end-point, SSDP, SASL2 | Только internal_hashed; опциональный sasl2_bind2 (alias bind2) поддерживает inline bind/SM/CSI/Carbons. SASL2 inline SM resume и takeover живого транспорта реализованы; Bind2/MAM metadata читает endpoints без полного scan; полные MAM queries всё ещё могут сканировать историю |
| Сообщения и presence | локальная маршрутизация, приоритеты ресурсов, контакты/подписки/probes, offline, carbons, блокировки | Полная совместимость всех пограничных сценариев маршрутизации ещё не подтверждена | | Сообщения и presence | локальная маршрутизация, приоритеты ресурсов, контакты/подписки/probes, offline, carbons, блокировки | Полная совместимость всех пограничных сценариев маршрутизации ещё не подтверждена |
| MAM | личный и комнатный MAM2, prefs, формы, RSM, фильтры времени/контакта/ID, extended queries, metadata, stanza-id, expiry | Нет доказательства совпадения каждой ошибки и порядка результатов при всех комбинациях запроса | | MAM | личный и комнатный MAM2, prefs, формы, RSM, фильтры времени/контакта/ID, extended queries, metadata, stanza-id, expiry | Нет доказательства совпадения каждой ошибки и порядка результатов при всех комбинациях запроса |
| MUC | комнаты, несколько ресурсов, роли/affiliations, owner/admin, конфигурация, приглашения, история без MAM и MAM, occupant-id/avatar, hats, PM/presence policies, регистрация, административные ad-hoc команды; журнал конфигурации, членства и истории до ответа, crash recovery persistent/temporary комнат | Rust-журнал авторитетен, native config/state — экспорт. Без SM восстановление требует новой аутентификации точного полного JID; grace по умолчанию 300 с, `muc_recovery_grace_period` 1..604800. Действующая приватная SM-запись восстанавливает логическое членство до listeners. Произвольные Lua-модули не запускаются; [MUC-RECOVERY.md](MUC-RECOVERY.md) | | MUC | комнаты, несколько ресурсов, роли/affiliations, owner/admin, конфигурация, приглашения, история без MAM и MAM, occupant-id/avatar, hats, PM/presence policies, регистрация, административные ad-hoc команды; журнал конфигурации, членства и истории до ответа, crash recovery persistent/temporary комнат | Rust-журнал авторитетен, native config/state — экспорт. Без SM восстановление требует новой аутентификации точного полного JID; grace по умолчанию 300 с, `muc_recovery_grace_period` 1..604800. Действующая приватная SM-запись восстанавливает логическое членство до listeners. Произвольные Lua-модули не запускаются; [MUC-RECOVERY.md](MUC-RECOVERY.md) |
| PEP/pubsub | persistent nodes/items, ACL, subscriptions, publish/retract/purge/delete, owner/configuration, caps, OMEMO XML как payload | Не все необязательные политики узлов Prosody реализованы; OMEMO шифруют клиенты, криптография OMEMO не часть сервера | | PEP/pubsub | persistent nodes/items, ACL, subscriptions, publish/retract/purge/delete, owner/configuration, caps, OMEMO XML как payload | Не все необязательные политики узлов Prosody реализованы; OMEMO шифруют клиенты, криптография OMEMO не часть сервера |
| private/bookmarks/vCard | private XML, старые и новые закладки, vCard/temp-vCard4 и avatar-переходы | Межклиентное поведение нужно дополнительно проверить настоящими клиентами | | private/bookmarks/vCard | private XML, старые и новые закладки, vCard/temp-vCard4 и avatar-переходы | Межклиентное поведение нужно дополнительно проверить настоящими клиентами |
| SM/push | acknowledgements после обработки, resumable XEP-0198 sessions, replay, ограниченные очереди, приватный durable snapshot, heartbeat и shutdown checkpoint; legacy SASL/SASL2 resume после restart; native регистрации push | Нужны новый TLS/SASL-транспорт и токен того же владельца; TCP/TLS-сокеты не сохраняются. SM-снимок не native Prosody: 256 MiB суммарно, 8 MiB/4096 stanza на сессию. При неопределённой отправке возможен replay. Есть coalescing и watchdog первого push; реальный APNs/FCM цикл не подтверждён | | SM/push | acknowledgements после обработки, resumable XEP-0198 sessions, replay, ограниченные очереди, приватный durable snapshot, heartbeat и shutdown checkpoint; legacy SASL/SASL2 resume после restart; native регистрации push | Нужны новый TLS/SASL-транспорт и токен того же владельца; TCP/TLS-сокеты не сохраняются. SM-снимок не native Prosody: 256 MiB суммарно, 8 MiB/4096 stanza на сессию. При неопределённой отправке возможен replay. Есть coalescing и watchdog первого push; реальный APNs/FCM цикл не подтверждён |
| CSI | active/inactive, ограниченный буфер, native importance function + runtime defaults для OMEMO/receipts/markers, delay, flush при клиентской активности и grace period | Defaults0.8.2 важнее по доставке, чем строгая native policy; явный csi_important_payloads заменяет defaults. Событие socket drain приближено завершением записи; необязательные расширенные XPath-фильтры поддержаны не полностью | | CSI | active/inactive, ограниченный буфер, native importance function + runtime defaults для OMEMO/receipts/markers, delay, flush при клиентской активности и grace period | Defaults0.8.2 важнее по доставке, чем строгая native policy; явный csi_important_payloads заменяет defaults. Событие socket drain приближено завершением записи; необязательные расширенные XPath-фильтры поддержаны не полностью |
| Федерация | входящие/исходящие STARTTLS, CA/DNS-ID, SASL EXTERNAL, DNS SRV, проверка домена каждого stanza, ограниченный пул соединений | Нет dialback/DANE/OtherName-only/SRV-ID/bidi/direct TLS | | Федерация | входящие/исходящие STARTTLS, CA/DNS-ID, SASL EXTERNAL, DNS SRV, проверка домена каждого stanza, ограниченный пул соединений | Нет dialback/DANE/OtherName-only/SRV-ID/bidi/direct TLS |
| HTTP upload | Несколько XEP-0363 components, Host/path isolation, local/S3 gateway, multipart/SigV4, streaming PUT, GET/HEAD/range/conditional, квоты/expiry, native metadata/JWT; offline local↔S3/S3↔S3 migration с SHA-256/resume; intent/exact-key recovery и quarantine; реальный 10 GiB PUT/GET через MinIO проверен | MinIO single-node с описанным netlink adapter тестовой среды; AWS/distributed deployment не проверены. Миграция требует остановки сервера и временного места под один файл; source сохраняется. Потерянные ownership metadata и неопределённый Complete требуют отдельного разбора; HTTP-UPLOAD.md | | HTTP upload | Несколько XEP-0363 components, Host/path isolation, local/S3 gateway, multipart/SigV4, streaming PUT, GET/HEAD/range/conditional, квоты/expiry, native metadata/JWT; offline local↔S3/S3↔S3 migration с SHA-256/resume; intent/exact-key recovery и quarantine; реальный 10 GiB PUT/GET через MinIO проверен | MinIO single-node с описанным netlink adapter тестовой среды; AWS/distributed deployment не проверены. Миграция требует остановки сервера и временного места под один файл; source сохраняется. Потерянные ownership metadata и неопределённый Complete требуют отдельного разбора; HTTP-UPLOAD.md |
| WebSocket/BOSH | WS/WSS и HTTP/HTTPS, общая XMPP-сессия, origin rules, BOSH RID/retry/hold/reorder/restart; http_altconnect XRD/JRD discovery; http_paths, HTTP Host/fallback, внешний URL, доверенные IP/CIDR и forwarded headers для TLS reverse proxy | Один VirtualHost с компонентами; нет PROXYv1/v2, произвольных Lua HTTP-приложений и горячей перенастройки. См. HTTP-PROXY.md | | WebSocket/BOSH | WS/WSS и HTTP/HTTPS, общая XMPP-сессия, origin rules, BOSH RID/retry/hold/reorder/restart; http_altconnect XRD/JRD discovery; http_paths, HTTP Host/fallback, внешний URL, доверенные IP/CIDR и forwarded headers для TLS reverse proxy | Один VirtualHost с компонентами; нет PROXYv1/v2, произвольных Lua HTTP-приложений и горячей перенастройки. См. HTTP-PROXY.md |
| admin_shell | протокол admin socket, status/version/uptime, списки, основные операции аккаунтов | Нет произвольного Lua, горячей загрузки модулей и полного набора prosodyctl | | admin_shell | протокол admin socket, status/version/uptime, списки, основные операции аккаунтов | Нет произвольного Lua, горячей загрузки модулей и полного набора prosodyctl |
| Остальные модули | disco, ping, time, version, uptime, register, TURN extdisco/HMAC, каталог seclabels | Нет встроенного TURN. Coturn работает отдельно. `pep_simple` пуст в предоставленном исходнике | | Остальные модули | disco, ping, time, version, uptime, register, TURN extdisco/HMAC, каталог seclabels | Нет встроенного TURN. Coturn работает отдельно. `pep_simple` пуст в предоставленном исходнике |
## Особенности именно вашего конфига ## Особенности именно вашего конфига
@@ -172,3 +172,14 @@ ad-hoc destroy/set-affiliation. Общие и host `modules_disabled` приме
MAM/SM используют authoritative redo sidecars. Перед сменой writer или откатом MAM/SM используют authoritative redo sidecars. Перед сменой writer или откатом
на старую версию требуется offline `storage-checkpoint CONFIG`; после аварии на старую версию требуется offline `storage-checkpoint CONFIG`; после аварии
нельзя просто удалить sidecars. Подробности — [DURABLE-MESSAGE-IO.md](DURABLE-MESSAGE-IO.md). нельзя просто удалить sidecars. Подробности — [DURABLE-MESSAGE-IO.md](DURABLE-MESSAGE-IO.md).
### Внешний перезапись MAM baseline
Внешняя перезапись native `.list` поверх зафиксированного redo-baseline (бакап,
копия data_path, Prosody, старая версия) давала «archive redo baseline changed
in place»: каждый MAM-append падал и через `?` ронял session, клиент
реконнектился, сообщение не доставлялось. В 0.8.4 MAM-архивация на личной
доставке стала best-effort (storage-ошибка не роняет session и не роняет доставку;
в лог — `mam-archive-skipped`), а `storage-checkpoint` получил ремонт непроверенного
baseline: текущий синхронизированный native считается авторитетным и неприменимый
журнал удаляется. Живой append остаётся fail-closed: журнал не удаляется молча.
+39 -9
View File
@@ -66,20 +66,50 @@ in-place append при активном redo считается незаверш
Редактирование baseline in place без handoff не поддержано. Anchor проверяет Редактирование baseline in place без handoff не поддержано. Anchor проверяет
последние 4 KiB baseline, не является checksum всего старого архива. последние 4 KiB baseline, не является checksum всего старого архива.
## Внешний перезапись baseline: «archive redo baseline changed in place»
Если какой-либо writer (бакап-восстановление, копия data_path, Prosody, старая
версия сервера) перезаписал native `.list` поверх зафиксированного baseline без
handoff, `.rredo` перестаёт совпадать со своим anchor. Каждый вызов
`archive_for_user` возвращал `Err`, и через `?` ошибка роняла session
(`connection-closed reason=protocol-or-transport-error`) — клиент (напр. Monal)
реконнектился по кругу, сообщение не доставлялось.
Начиная с 0.8.4 MAM-архивация на трёх местах личной доставки (отправитель,
получатель, входящая федерация) стала best-effort: при storage-ошибке исходная
stanza всё равно маршрутизируется, обе сессии остаются живы, в лог пишется
санитизированный `mam-archive-skipped`. Живой append остаётся fail-closed:
непроверенный журнал не удаляется молча, а сохраняется для явного handoff.
Ремонт выполняется той же командой offline handoff:
```bash
rust-xmpp storage-checkpoint /etc/prosody/prosody.cfg.lua
```
Теперь `prepare_rewrite` распознаёт непроверенный baseline (changed in place,
shorter, missing, journal exceeds bound), считает текущий durably-синхронизированный
native авторитетным, синхронизирует его и удаляет неприменимый журнал. После этого
MAM history снова читается, а новые append создадут свежий redo поверх нового
baseline. Сверку совместимости native-формата см. в исходниках Prosody
(`util/datamanager.lua` `list_append`/`list_store`, `plugins/mod_storage_internal.lua`
`archive:append`): Prosody пишет `item(...)` и при полной перезаписи применяет
atomic rename — что и меняет первые bytes baseline, оставляя Rust-журнал устаревшим.
## Диагностика ## Диагностика
`message-processing-slow` по-прежнему имеет threshold 250 мс, duration_ms, route `message-processing-slow` по-прежнему имеет threshold 250 мс, duration_ms, route
и connection. Новые поля в integer milliseconds: и connection. Новые поля в integer milliseconds:
|Поле|Измеряемый этап| | Поле | Измеряемый этап |
|---|---| | ---------------- | -------------------------------------------------------------------- |
|pre_routing_ms|Работа до личного MAM; для MUC/ранних выходов — обработка до handled| | pre_routing_ms | Работа до личного MAM; для MUC/ранних выходов — обработка до handled |
|sender_mam_ms|Политика и архивирование отправителя| | sender_mam_ms | Политика и архивирование отправителя |
|recipient_mam_ms|Проверка аккаунта, политика и архивирование получателя| | recipient_mam_ms | Проверка аккаунта, политика и архивирование получателя |
|delivery_ms|Маршрутизация, targets, mutex и transmit| | delivery_ms | Маршрутизация, targets, mutex и transmit |
|carbons_ms|Выбор ресурсов и отправка carbon| | carbons_ms | Выбор ресурсов и отправка carbon |
|offline_push_ms|Offline commit и подготовка/enqueue push| | offline_push_ms | Offline commit и подготовка/enqueue push |
|handled_ms|Session mutex и durable mark_handled| | handled_ms | Session mutex и durable mark_handled |
Неисполненные этапы равны 0. target_count — выбранные локальные ресурсы, Неисполненные этапы равны 0. target_count — выбранные локальные ресурсы,
carbon_count — успешные carbon transmit. Это серверные этапы, не время carbon_count — успешные carbon transmit. Это серверные этапы, не время