Версия 0.8.3 удерживала глобальный Store.slots mutex на весь durable
commit (включая fsync/append). fsync одного канала задерживал SM-коммиты всех остальных сессий; при медленном диске и нескольких пользователях это выглядело как «сервер перестал отвечать на запросы» и секундная доставка сообщений. - Глобальная блокировка разделена: короткий lock `slots` теперь только валидирует token/limits и резервирует слот; сам append+fsync идёт под per-session `wal` lock. Один медленный канал больше не блокирует остальных. - Ротация/compaction/GC сериализуются отдельным `maintenance` lock вне любого per-session lock — очереди append и compaction для одного WAL не гонки, дедлоков нет, инварианты durable и crash-тесты сохранены. - Все существующие crash/latency/recovery regressions зелёные без изменений. Для genuinely медленного диска (VPS с медленным fsync) добавлена опция как в Prosody, где исходящие SM-stanza живого клиента идут в in-memory smqueue без fsync, а durable пишутся счётчик/h и переход в hibernation. - `smacks_online_durable = false` (strict — default): live-сессии не fsync на каждый outbound/ack, а помечают `dirty`; фоновый проход `smacks_flush_interval` (default1с) и offline-переход фиксируют durable. - Offline/hibernating цели в любом режиме фиксируются durable ДО доставки — сохраняются критичные инварианты («Alice отправила, Bob ещё не авторизован») и crash-регрессии; отзыв токенов/expiry остаётся durable. - Pre-ACK durable гарантию даёт строгий режим; deferred — компромисс Prosody «низкая задержка» в обмен на ограниченное окно потери для online-пиров. - `cargo test --locked --release`: 54 binary, 336 passed, 11 ignored (0 fail). - Strict crash (`slow_storage_crash_network`), latency (`slow_storage_latency`), `sm_restart`, `journal_recovery_sync`, личная доставка — все зелёные. - Новые регрессии: `tests/deferred_sm_online_network.py` (wire, deferred режим: offline durable до ACK, online доставка + flush persist), два Rust unit-теста в `tests/sm_journal.rs` (deferred online commit/flush, offline durable). - `push_latency_network.py` в этой среде падает на TLS CA `keyUsage` из-за более строгого TLS в Python 3.14 (до SM-кода, не связано с этим изменением). Документация: [docs/DURABLE-MESSAGE-IO.md](docs/DURABLE-MESSAGE-IO.md), [docs/COMPATIBILITY.md](docs/COMPATIBILITY.md).
This commit is contained in:
1 parent
bff609097b
commit
821e1c3e2f
11 files changed
+656
-88
No files matched your search
@@ -892,6 +892,12 @@ pub async fn serve(path: &Path) -> Result<()> {
|
||||
} else {
|
||||
Hub::default()
|
||||
};
|
||||
// Streaming durability policy. Defaults to the strict commit-before-ACK
|
||||
// model; set `smacks_online_durable = false` to defer per-stanza fsync for
|
||||
// live sessions (Prosody's streaming behavior), flushing on offline
|
||||
// transitions and a periodic background pass. Offline/hibernating targets
|
||||
// always commit durably before delivery regardless of this flag.
|
||||
hub.set_defer_online(!boolean(&c, &domain, "smacks_online_durable", true));
|
||||
// A persisted, unexpired SM session is still a logical room occupant while
|
||||
// its transport is offline. Restore routing before accepting any clients so
|
||||
// room traffic queues durably even before that occupant reauthenticates.
|
||||
@@ -1242,6 +1248,9 @@ pub async fn serve(path: &Path) -> Result<()> {
|
||||
let heartbeat_seconds = (number(&c, &domain, "smacks_hibernation_time", 300) / 4).clamp(1, 10);
|
||||
let mut heartbeat = tokio::time::interval(Duration::from_secs(heartbeat_seconds));
|
||||
let mut heartbeat_task: Option<tokio::task::JoinHandle<()>> = None;
|
||||
let flush_seconds = number(&c, &domain, "smacks_flush_interval", 1).clamp(1, 3600);
|
||||
let mut flush = tokio::time::interval(Duration::from_secs(flush_seconds));
|
||||
let mut flush_task: Option<tokio::task::JoinHandle<()>> = None;
|
||||
let mut terminate = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())?;
|
||||
let mut hangup = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::hangup())?;
|
||||
loop {
|
||||
@@ -1277,6 +1286,17 @@ pub async fn serve(path: &Path) -> Result<()> {
|
||||
}));
|
||||
}
|
||||
},
|
||||
_ = flush.tick() => {
|
||||
if flush_task.as_ref().is_none_or(|task| task.is_finished()) {
|
||||
let rt = runtime.clone();
|
||||
flush_task = Some(tokio::task::spawn_blocking(move || {
|
||||
if let Err(_error) = rt.hub.flush_dirty() {
|
||||
rt.logger.event(Level::Error, "smacks", None,
|
||||
"deferred-flush-failed", &[("reason", "storage-error")]);
|
||||
}
|
||||
}));
|
||||
}
|
||||
},
|
||||
_ = tick.tick() => {
|
||||
for jid in runtime.hub.expired() {
|
||||
logger.event(Level::Info, "smacks", None, "session-expired", &[("jid", &jid)]);
|
||||
|
||||
Reference in new issue
Block a user