Skip to content

Commit 1f80ec9

Browse files
fix: harden cross-platform lock and test behavior
1 parent 68237cd commit 1f80ec9

9 files changed

Lines changed: 546 additions & 267 deletions

File tree

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11

22
# Use bd merge for beads JSONL files
33
.beads/beads.jsonl merge=beads
4+
5+
# Integrity fixtures embed byte sizes and hashes; keep checkout bytes stable.
6+
tests/fixtures/pages_verify/** text eol=lf

src/indexer/mod.rs

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2382,6 +2382,7 @@ impl Drop for IndexRunLockGuard {
23822382
let _ = self.file.set_len(0);
23832383
let _ = self.file.rewind();
23842384
let _ = self.file.flush();
2385+
let _ = crate::search::asset_state::clear_index_run_lock_metadata_sidecar(&self._path);
23852386
let _ = self.file.unlock();
23862387
}
23872388
}
@@ -2416,8 +2417,7 @@ impl IndexRunLockGuard {
24162417
self._path.display()
24172418
)
24182419
})?;
2419-
writeln!(
2420-
self.file,
2420+
let metadata = format!(
24212421
"pid={}\nstarted_at_ms={}\nupdated_at_ms={}\nlast_progress_at_ms={}\ndb_path={}\nmode={}\njob_id={}\njob_kind={}\nphase={}",
24222422
std::process::id(),
24232423
self.started_at_ms,
@@ -2428,14 +2428,19 @@ impl IndexRunLockGuard {
24282428
self.job_id,
24292429
self.job_kind.as_lock_value(),
24302430
mode.as_lock_value()
2431-
)
2432-
.with_context(|| format!("writing index-run metadata to {}", self._path.display()))?;
2431+
);
2432+
writeln!(self.file, "{metadata}")
2433+
.with_context(|| format!("writing index-run metadata to {}", self._path.display()))?;
24332434
self.file
24342435
.flush()
24352436
.with_context(|| format!("flushing index-run lock file {}", self._path.display()))?;
24362437
self.file
24372438
.sync_all()
24382439
.with_context(|| format!("syncing index-run lock file {}", self._path.display()))?;
2440+
crate::search::asset_state::write_index_run_lock_metadata_sidecar(
2441+
&self._path,
2442+
&format!("{metadata}\n"),
2443+
)?;
24392444
Ok(())
24402445
}
24412446

@@ -2565,11 +2570,28 @@ fn heartbeat_index_run_lock_with_lock_and_progress(
25652570
.map_err(|_| anyhow::anyhow!("index-run metadata write lock poisoned"))
25662571
})
25672572
.transpose()?;
2568-
let lock_path = data_dir.join("index-run.lock");
2573+
let lock_path = crate::search::asset_state::index_run_lock_path(data_dir);
25692574
let existing = match fs::read_to_string(&lock_path) {
25702575
Ok(contents) if !contents.is_empty() => contents,
25712576
Ok(_) => return Ok(()),
25722577
Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(()),
2578+
Err(err) if crate::search::asset_state::windows_lock_conflict(&err) => {
2579+
let sidecar_path =
2580+
crate::search::asset_state::index_run_lock_metadata_sidecar_path(&lock_path);
2581+
match fs::read_to_string(&sidecar_path) {
2582+
Ok(contents) if !contents.is_empty() => contents,
2583+
Ok(_) => return Ok(()),
2584+
Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(()),
2585+
Err(err) => {
2586+
return Err(err).with_context(|| {
2587+
format!(
2588+
"reading index-run lock heartbeat sidecar {}",
2589+
sidecar_path.display()
2590+
)
2591+
});
2592+
}
2593+
}
2594+
}
25732595
Err(err) => {
25742596
return Err(err).with_context(|| {
25752597
format!("reading index-run lock heartbeat {}", lock_path.display())
@@ -2636,6 +2658,28 @@ fn heartbeat_index_run_lock_with_lock_and_progress(
26362658
}
26372659

26382660
fn write_index_run_lock_heartbeat_in_place(lock_path: &Path, refreshed: &str) -> Result<()> {
2661+
match write_index_run_lock_file_in_place(lock_path, refreshed) {
2662+
Ok(()) => {
2663+
crate::search::asset_state::write_index_run_lock_metadata_sidecar(lock_path, refreshed)
2664+
}
2665+
Err(err) => {
2666+
let lock_conflict = err.chain().any(|cause| {
2667+
cause
2668+
.downcast_ref::<std::io::Error>()
2669+
.is_some_and(crate::search::asset_state::windows_lock_conflict)
2670+
});
2671+
if lock_conflict {
2672+
crate::search::asset_state::write_index_run_lock_metadata_sidecar(
2673+
lock_path, refreshed,
2674+
)
2675+
} else {
2676+
Err(err)
2677+
}
2678+
}
2679+
}
2680+
}
2681+
2682+
fn write_index_run_lock_file_in_place(lock_path: &Path, refreshed: &str) -> Result<()> {
26392683
// Do not temp-file+rename `index-run.lock`: POSIX advisory locks attach
26402684
// to the existing file inode/open handle, and replacing the path would
26412685
// let another process lock a fresh inode while this process still holds

0 commit comments

Comments
 (0)