refactor(lock): use kernel file locking instead of a hand-rolled PID lock [P2]
The previous implementation had every failure mode a home-grown PID lock has: - contention was detected by matching the substring "lock already exists" in an error message, so any change to the message would have turned a busy lock into a hard error; - stale-lock recovery removed the file and created it again as two separate operations, so two processes could each decide the lock was stale and each take it; - a reused PID made a live lock look stale, since the check was only whether /proc/<pid> exists; - Drop deleted the lock file, racing with a process that had already opened it and was sleeping on the retry loop - it would then lock a file that no longer has a directory entry, and the next arrival would create a fresh one and lock that instead. Replaced with advisory kernel locking through std::fs::File::try_lock, stabilised in Rust 1.89 and backed by flock on Linux. No new dependency. The kernel releases the lock when the holder exits by any means, including kill -9, so there is no stale state to reason about and no recovery heuristic to get wrong. The PID is still written to the file, but purely as diagnostics - nothing reads it back to make a decision. The lock file is now never deleted. Its presence carries no meaning; only the lock held on it does. That removes the race the old Drop introduced. Five tests: a second acquisition of a live lock fails, timeout reports lock busy timeout, the lock is free again once the guard drops, the file survives release, and a leftover file from a previous run does not block acquisition. Behaviour note for the four call sites (traffic, firewall, sysctl_tuning, swap): timeouts are unchanged, and a process killed mid-reconcile no longer leaves anything that needs cleaning up before the next run. cargo test --workspace: 158 + 4 passed. clippy -D warnings clean.
This commit is contained in:
@@ -494,12 +494,16 @@ Status обязан показывать наличие таблицы/chain/п
|
||||
| `/run/awg2-harness/firewall.lock` | 10 с | `apply_firewall` (host + anti-torrent) |
|
||||
| `/run/awg2-harness/sysctl_tuning.lock` | 30 с | `ensure_sysctl_tuning` |
|
||||
|
||||
Реализация — advisory-блокировка ядра (`flock` на Linux) через `std::fs::File`, а не PID-файл.
|
||||
|
||||
Поведение:
|
||||
|
||||
- ожидание захвата — с интервалом 100 мс до таймаута, затем ошибка `lock busy timeout`;
|
||||
- в файл записывается PID владельца;
|
||||
- если процесс-владелец мёртв (`/proc/<pid>` отсутствует) или содержимое файла не парсится как PID, блокировка считается устаревшей и перезахватывается;
|
||||
- освобождение автоматическое при выходе из области видимости, включая пути с ошибкой.
|
||||
- PID пишется в файл **только как диагностика**; ни одно решение на его основе не принимается;
|
||||
- освобождение выполняет ядро при завершении процесса — любым способом, включая `kill -9`. Понятия «устаревшая блокировка» не существует;
|
||||
- файл блокировки никогда не удаляется: сам факт его наличия ничего не значит, значение имеет только удерживаемая на нём блокировка.
|
||||
|
||||
Прежняя реализация на PID-файлах имела типичные для такого подхода дефекты: конфликт определялся по подстроке в тексте ошибки; «устаревшая» блокировка удалялась и создавалась заново двумя отдельными операциями, поэтому два процесса могли одновременно счесть её устаревшей и оба захватить; переиспользованный PID делал живую блокировку похожей на мёртвую; удаление файла в `Drop` гонялось с процессом, который уже открыл его и ждёт.
|
||||
|
||||
## 15. Контракт вывода status
|
||||
|
||||
|
||||
Reference in New Issue
Block a user