Use write_all for ledger entries to improve concurrent-write safety
writeln!(f, ...) makes two syscalls (data + newline) which can interleave between concurrent processes even with O_APPEND. Serialise entry to bytes and append the newline before write_all() so the entire entry lands in a single write() syscall, which O_APPEND makes atomic on Linux local filesystems for typical entry sizes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
10
src/agent.rs
10
src/agent.rs
@@ -617,18 +617,22 @@ fn append_ledger_entry(ledger: &Path, result: &BacktestResult, strategy: &Value)
|
||||
.to_string(),
|
||||
strategy: strategy.clone(),
|
||||
};
|
||||
let line = match serde_json::to_string(&entry) {
|
||||
Ok(s) => s,
|
||||
// Append newline inside the serialised bytes so the entire write is a single
|
||||
// write_all() syscall — O_APPEND + single write() is atomic on Linux local
|
||||
// filesystems, making concurrent instances safe for typical entry sizes.
|
||||
let mut bytes = match serde_json::to_vec(&entry) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
warn!("could not serialize ledger entry: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
bytes.push(b'\n');
|
||||
if let Err(e) = std::fs::OpenOptions::new()
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(ledger)
|
||||
.and_then(|mut f| writeln!(f, "{}", line))
|
||||
.and_then(|mut f| f.write_all(&bytes))
|
||||
{
|
||||
warn!("could not write ledger entry: {e}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user