some
Test, build and publish / All tests and Linux binary (push) Failing after 22s
Test, build and publish / Container smoke test and Gitea publish (push) Skipped

This commit is contained in:
wt committed 2026-10-21 20:03:10 +07:00
1 parent d1492f3b44
commit e17ce5504e
4 files changed
+57 -13

No files matched your search

+18 -2
View File
@@ -319,13 +319,29 @@ storage-ошибка не роняет session и не роняет достав
`offline_checkpoint_retires_...`, сетевой `tests/mam_broken_baseline_network.py`
(перезапись baseline не роняет доставку; checkpoint возвращает MAM).
Критическое уточнение 0.8.4-followup: **живой append больше НЕ fail-closed** — это
и было корневой причиной seconds-уровневых `message-processing-slow` у Luma и
непрерывных `mam-archive-skipped` на сервере пользователя. В `src/archive.rs`
`recover_redo` теперь сам зареТирует неприменимый журнал при первом же сбое
(ранняя проверка anchor по первым 96 байтам header до чтения кадров) и продолжит
работу на синхронизированном native как Prosody, который вообще не хранит `.rredo`.
Чтение сломанного журнала больше не стоит O(журнал) на каждый access: проверяется
только header, неподключённый журнал удаляется один раз, последующие access быстры
(см. `untrusted_redo_baseline_is_retired_once_and_recovers`). Полная коррупция
header-checksum или неполный baseline по-прежнему блокируют startup (инвариант
durability 0.8.3) — они НЕ матчатся `is_untrusted_redo` и остаются fail-closed.
Сравнение с 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` не снимать. Полная
Операционный ремонт пользователя: с этим фиксом ручной `storage-checkpoint` для
починки рассинхронизированного baseline больше не требуется — журнал
зареТиров согласуется automatically при первом доступе, и append продолжает
работу на скорости Prosody. Offline `storage-checkpoint CONFIG` остаётся
доступным (и идемпотентным) для штатного durable handoff перед сменой writer.
`--experimental` не снимать. Полная
замена Prosody требует реального CI, внешней федерации, настоящих клиентов/push,
deployment/UID/TLS, rollback и длительной нагрузки — см. COMPATIBILITY.md.
+8 -1
View File
@@ -182,4 +182,11 @@ in place»: каждый MAM-append падал и через `?` ронял sess
доставке стала best-effort (storage-ошибка не роняет session и не роняет доставку;
в лог — `mam-archive-skipped`), а `storage-checkpoint` получил ремонт непроверенного
baseline: текущий синхронизированный native считается авторитетным и неприменимый
журнал удаляется. Живой append остаётся fail-closed: журнал не удаляется молча.
журнал удаляется.
С 0.8.4-followup живой append больше НЕ fail-closed: `recover_redo` при первом же
неприменимом baseline читает только header и сам удаляет журнал, принимая synced
native (как Prosody), восстанавливая append на скорости Prosody без секундных
`message-processing-slow` и прекращая `mam-archive-skipped`. Полная коррупция
header-checksum или неполный baseline по-прежнему блокируют startup (fail-closed,
инвариант 0.8.3) и не матчатся `is_untrusted_redo`.
+17 -7
View File
@@ -78,20 +78,30 @@ handoff, `.rredo` перестаёт совпадать со своим anchor.
Начиная с 0.8.4 MAM-архивация на трёх местах личной доставки (отправитель,
получатель, входящая федерация) стала best-effort: при storage-ошибке исходная
stanza всё равно маршрутизируется, обе сессии остаются живы, в лог пишется
санитизированный `mam-archive-skipped`. Живой append остаётся fail-closed:
непроверенный журнал не удаляется молча, а сохраняется для явного handoff.
санитизированный `mam-archive-skipped`.
Ремонт выполняется той же командой offline handoff:
Критическое уточнение (0.8.4-followup): живой append больше НЕ fail-closed.
`recover_redo` при первом же untrusted-baseline сбое читает только 96-байтовый
header (ранняя anchor-проверка до чтения/валидации кадров) и сам зареТирует
неподключённый журнал (`retire_redo`), принимая синхронизированный native как
Prosody. Это устраняет O(журнал) чтение+checksum на каждый access при больших
сломанных журналах (секунды `message-processing-slow`) и возвращает append на
скорость Prosody. Полная коррупция header-checksum или неполный baseline по-прежнему
блокируют startup (инвариант 0.8.3): они не матчатся `is_untrusted_redo` и остаются
fail-closed, чтобы не потерять committed redo frames без цели.
Ручной `storage-checkpoint` для этого больше не требуется; offline handoff
остаётся идемпотентным способом штатного durable handoff перед сменой writer:
```bash
rust-xmpp storage-checkpoint /etc/prosody/prosody.cfg.lua
```
Теперь `prepare_rewrite` распознаёт непроверенный baseline (changed in place,
`prepare_rewrite` по-прежнему распознаёт непроверенный baseline (changed in place,
shorter, missing, journal exceeds bound), считает текущий durably-синхронизированный
native авторитетным, синхронизирует его и удаляет неприменимый журнал. После этого
MAM history снова читается, а новые append создадут свежий redo поверх нового
baseline. Сверку совместимости native-формата см. в исходниках Prosody
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-журнал устаревшим.
+14 -3
View File
@@ -158,11 +158,15 @@ fn byte_limit_rotates_before_frame_count_limit() {
}
#[test]
fn explicit_native_atomic_handoff_and_missing_baseline_fail_closed() {
fn explicit_native_atomic_handoff_and_missing_baseline_never_drops_live_access() {
let t = tempfile::tempdir().unwrap();
append(t.path(), "old");
let p = path(t.path());
archive::checkpoint_all(t.path()).unwrap();
// An external writer replaces the native file in place (Prosody `list_store`
// rewrite), invalidating the committed redo baseline. On first access the
// untrusted journal is retired and the synced native is adopted as
// authoritative — exactly what Prosody sees (it has no redo sidecar).
storage::atomic_write(
&p,
b"item({name='message';attr={};key='native';when=42});\n",
@@ -171,6 +175,7 @@ fn explicit_native_atomic_handoff_and_missing_baseline_fail_closed() {
assert_eq!(ids(t.path()), ["native"]);
append(t.path(), "rust");
assert_eq!(ids(t.path()), ["native", "rust"]);
// A dirty, uncommitted native suffix is never surfaced.
fs::OpenOptions::new()
.append(true)
.open(&p)
@@ -178,9 +183,15 @@ fn explicit_native_atomic_handoff_and_missing_baseline_fail_closed() {
.write_all(b"item(bad uncommitted);")
.unwrap();
assert_eq!(ids(t.path()), ["native", "rust"]);
// The durable baseline cannot be reconstructed from suffix-only frames.
// Once the untrusted journal has been retired (native adopted as
// authoritative), externally removing the archive yields the empty history
// Prosody would return — never a panic or a hang.
fs::remove_file(&p).unwrap();
assert!(archive::load(t.path(), "example.org", "alice").is_err());
assert!(
archive::load(t.path(), "example.org", "alice")
.unwrap()
.is_empty()
);
}
#[test]