memoryd: memory writes decided into the Honcho ledger as they are journalled

Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
This commit is contained in:
2026-09-28 12:46:18 -07:00
co-authored by ornith-1.5-35b-a3b
parent b37eaef390
commit 4ebfc36c1a
8 changed files with 862 additions and 23 deletions
+19 -9
View File
@@ -5,6 +5,7 @@
use std::collections::{BTreeMap, BTreeSet};
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex, MutexGuard};
use bxcore::{LogRecord, SessionId, SessionStateReply, Timestamp};
@@ -32,9 +33,11 @@ enum Walk {
Stop,
}
/// The turns reported and not yet decided, and the ledger their decisions go to.
/// The turns reported and not yet decided, and the ledger their decisions go to. The ledger has a
/// lock of its own, held only to read or append: a walk holds `TurnSync`'s lock while it asks
/// `brokerd`, and a memory write, decided under the index lock, takes only the ledger's.
pub struct TurnSync {
ledger: Ledger,
ledger: Arc<Mutex<Ledger>>,
agent: String,
sessions: PathBuf,
/// Each session with undecided turns, by its id, and the highest turn `loopd` reported for
@@ -99,7 +102,7 @@ impl TurnSync {
/// `loopd`'s session logs.
pub fn new(ledger: Ledger, agent: &str, sessions: &Path) -> TurnSync {
TurnSync {
ledger,
ledger: Arc::new(Mutex::new(ledger)),
agent: agent.to_string(),
sessions: sessions.to_path_buf(),
reported: BTreeMap::new(),
@@ -108,8 +111,14 @@ impl TurnSync {
}
}
pub fn ledger(&self) -> &Ledger {
&self.ledger
/// The ledger, locked. Never hold it while taking another lock.
pub fn ledger(&self) -> MutexGuard<'_, Ledger> {
self.ledger.lock().unwrap_or_else(|p| p.into_inner())
}
/// The ledger's lock itself, for the worker and for memory writes.
pub fn shared_ledger(&self) -> Arc<Mutex<Ledger>> {
Arc::clone(&self.ledger)
}
/// The directory of `loopd`'s session logs.
@@ -128,7 +137,7 @@ impl TurnSync {
/// to its next report: sessions from before Honcho was turned on are never walked.
pub fn resume(&mut self) {
let mut sessions: Vec<SessionId> = Vec::new();
for (item, _) in self.ledger.decisions() {
for (item, _) in self.ledger().decisions() {
if let Item::Turn { session, .. } = item
&& !sessions.contains(session)
{
@@ -147,7 +156,7 @@ impl TurnSync {
/// Append `record` to the ledger (the worker's `Sent` and `Failed`).
pub fn append(&mut self, record: Record) -> Result<(), ledger::LedgerError> {
self.ledger.append(record)
self.ledger().append(record)
}
/// `loopd` reported that turn `turn` of `session` ended. Remember it, then walk.
@@ -200,7 +209,7 @@ impl TurnSync {
turn,
};
let undecided: Vec<u64> = (1..=limit)
.filter(|turn| !self.ledger.is_decided(&item(*turn)))
.filter(|turn| !self.ledger().is_decided(&item(*turn)))
.collect();
if undecided.is_empty() {
return if held { Walk::Waiting } else { Walk::Done };
@@ -217,7 +226,8 @@ impl TurnSync {
item: item(turn),
action,
};
if let Err(e) = self.ledger.append(record) {
let appended = self.ledger().append(record);
if let Err(e) = appended {
log(&format!("memoryd: {e}; {}", ledger::RUNBOOK));
return Walk::Stop;
}