mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-24 10:27:44 -06:00
Recovery: a record lock held for a moment is waited out, not skipped
new_draft_restarts_restores_unsaved_and_explicitly_discards failed in a workspace run: the reopened session found no recovery record, because a process another test was starting held the record's lock for a moment (a forked child shares it until exec) and scan() skips locked records. The same can happen to a user when the app starts a helper process. lock() now tries again up to ten times, 10 ms apart, before treating the record as owned by a live instance. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e127a080a1
commit
18b680c9aa
@@ -14,6 +14,8 @@ const MAX_BYTES: u64 = 256 << 20;
|
||||
const MAX_RECORDS: usize = 128;
|
||||
const MAX_FILES: usize = 16;
|
||||
const DISK_BUDGET: u64 = 2 << 30;
|
||||
/// Times a record lock held for a moment (by a process being started) is tried again.
|
||||
const LOCK_RETRIES: u32 = 10;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -127,11 +129,21 @@ impl Store {
|
||||
paths.sort_by_key(|p| std::cmp::Reverse(generation(p)));
|
||||
Ok(paths)
|
||||
}
|
||||
/// Locks a record's `owner.lock`, retrying [`LOCK_RETRIES`] times 10 ms apart.
|
||||
fn lock(&self, id: &str) -> Result<File, String> {
|
||||
if !id_ok(id) {
|
||||
return Err("invalid recovery id".into());
|
||||
}
|
||||
let file = private_file(&self.root.join(id).join("owner.lock"), false).map_err(|e| e.to_string())?;
|
||||
// A process another part of the app (or a test) is starting holds the lock for a moment
|
||||
// (a forked child shares it until it execs): wait that out, but not a live owner.
|
||||
for _ in 0..LOCK_RETRIES {
|
||||
match file.try_lock() {
|
||||
Ok(()) => return Ok(file),
|
||||
Err(std::fs::TryLockError::WouldBlock) => std::thread::sleep(Duration::from_millis(10)),
|
||||
Err(e) => return Err(format!("recovery copy is in use or cannot be locked: {e}")),
|
||||
}
|
||||
}
|
||||
file.try_lock().map_err(|e| format!("recovery copy is in use or cannot be locked: {e}"))?;
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user