fix(auth): let profile locks reach stale reclaim

This commit is contained in:
aqilaziz
2026-05-20 16:12:06 +07:00
parent 65d92bf10a
commit c526748443
2 changed files with 17 additions and 3 deletions
+5 -3
View File
@@ -13,7 +13,6 @@ const CURRENT_SCHEMA_VERSION: u32 = 1;
const PROFILES_FILENAME: &str = "auth-profiles.json";
const LOCK_FILENAME: &str = "auth-profiles.lock";
const LOCK_WAIT_MS: u64 = 50;
const LOCK_TIMEOUT_MS: u64 = 10_000;
/// A lock file that has existed for longer than this is treated as leaked
/// (its owner crashed without unlinking it, or `fs::remove_file` in the
/// guard's `Drop` was rejected by Windows AV/indexer and the file got
@@ -23,6 +22,9 @@ const LOCK_TIMEOUT_MS: u64 = 10_000;
/// threshold is intentionally well above any realistic operation time
/// so we never reclaim under a slow-but-legitimate holder.
const STALE_LOCK_AGE_MS: u64 = 30_000;
/// Wait long enough for a fresh leaked lock to cross the stale threshold
/// and be reclaimed before surfacing a lock timeout to the caller.
const LOCK_TIMEOUT_MS: u64 = STALE_LOCK_AGE_MS + 5_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
@@ -595,7 +597,7 @@ impl AuthProfilesStore {
// Issue #1612 — a previous openhuman crash can leave a
// stale auth-profiles.lock behind, after which every RPC
// path that touches the auth profile store fails for the
// 10s LOCK_TIMEOUT_MS window and the user gets stuck in a
// `LOCK_TIMEOUT_MS` window and the user gets stuck in a
// retry storm. Before falling back to the busy-wait, try
// once to peek at the writer's recorded PID and remove
// the lock if that process is no longer alive. Flag is
@@ -654,7 +656,7 @@ impl AuthProfilesStore {
/// `AuthProfileLockGuard::drop` could not unlink the file (AV /
/// indexer briefly held a handle) and orphaned the lock with its
/// still-alive pid inside — every subsequent acquirer would
/// otherwise spin the full 10s `LOCK_TIMEOUT_MS` and bail. No
/// otherwise spin the full `LOCK_TIMEOUT_MS` and bail. No
/// legitimate auth-profile op holds the lock long enough to be
/// affected, so a too-old lock is unambiguously a leak.
///
@@ -495,6 +495,18 @@ fn clear_lock_if_stale_reclaims_aged_malformed_lock() {
assert!(!lock_path.exists());
}
#[test]
fn lock_timeout_allows_fresh_leaked_locks_to_age_into_stale_reclaim() {
assert!(
LOCK_TIMEOUT_MS > STALE_LOCK_AGE_MS,
"lock timeout must outlive stale-lock age so a fresh leaked lock can be reclaimed"
);
assert!(
LOCK_TIMEOUT_MS - STALE_LOCK_AGE_MS >= 1_000,
"timeout should leave at least one periodic stale recheck after the threshold"
);
}
/// Sentry OPENHUMAN-TAURI-H8: when `OpenOptions::create_new` fails with
/// anything other than `AlreadyExists`, the error surfaced to Sentry
/// must embed the underlying `io::ErrorKind` and `raw_os_error()` so we