memoryd: a reported turn decided after loopd has its answer, from brokerd and the log
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
//! Serving: three sockets, one request per connection, each answered by one frame (M5a spec,
|
||||
//! section 7). One lock covers the files and the index, so writes never interleave.
|
||||
//! section 7). One lock covers the files and the index, so writes never interleave. With
|
||||
//! `[honcho]`, a reported turn is decided after `loopd` has its answer, under a lock of its own
|
||||
//! (M5c spec, section 5).
|
||||
|
||||
use std::os::unix::net::{UnixListener, UnixStream};
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
@@ -7,13 +9,16 @@ use std::time::Duration;
|
||||
|
||||
use bxcore::{
|
||||
DataClass, Empty, Envelope, ErrorCode, MemoryAdminReply, MemoryArgs, MemoryBlockReply,
|
||||
MemoryCall, MemoryCommand, Message, PROTOCOL_VERSION, RecallArgs, Reindexed, Timestamp,
|
||||
ToolResponse, WireError, read_frame, write_frame,
|
||||
MemoryCall, MemoryCommand, Message, PROTOCOL_VERSION, RecallArgs, Reindexed, SessionId,
|
||||
SessionUpdated, Timestamp, ToolResponse, WireError, read_frame, write_frame,
|
||||
};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::edit::{self, Context};
|
||||
use crate::files::Store;
|
||||
use crate::honcho::ledger::{self, Ledger, LedgerError};
|
||||
use crate::honcho::state;
|
||||
use crate::honcho::turns::{self, TurnSync};
|
||||
use crate::index::{self, Index};
|
||||
use crate::recall::Recalled;
|
||||
use crate::{journal, recall, review};
|
||||
@@ -32,9 +37,39 @@ pub struct Memoryd {
|
||||
config: Config,
|
||||
store: Store,
|
||||
state: Mutex<State>,
|
||||
/// With `[honcho]`: the turns reported and not yet decided, and the ledger. Never taken
|
||||
/// while `state` is held, and never held by a request that waits for its answer.
|
||||
honcho: Option<Mutex<TurnSync>>,
|
||||
log: Log,
|
||||
}
|
||||
|
||||
/// Why `start` failed. `main` prints a start failure with the pointer `memoryd-start-failed`, and
|
||||
/// a ledger that does not read through with its own.
|
||||
#[derive(Debug)]
|
||||
pub enum StartError {
|
||||
Failed(String),
|
||||
Ledger(LedgerError),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for StartError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
StartError::Failed(why) => write!(f, "{why}"),
|
||||
// `Damaged` names its pointer itself.
|
||||
StartError::Ledger(e @ LedgerError::Damaged { .. }) => write!(f, "{e}"),
|
||||
StartError::Ledger(e) => write!(f, "{e}; {}", ledger::RUNBOOK),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for StartError {}
|
||||
|
||||
impl From<String> for StartError {
|
||||
fn from(why: String) -> StartError {
|
||||
StartError::Failed(why)
|
||||
}
|
||||
}
|
||||
|
||||
/// A started `memoryd`: its three listeners, bound and waiting.
|
||||
pub struct Running {
|
||||
memoryd: Arc<Memoryd>,
|
||||
@@ -48,9 +83,30 @@ enum Which {
|
||||
Admin,
|
||||
}
|
||||
|
||||
/// Create what is missing under `memory/`, open the index and catch it up, then bind the three
|
||||
/// sockets. The error names what failed; the caller points at `memoryd-start-failed`.
|
||||
pub fn start(config: Config, log: Log) -> Result<Running, String> {
|
||||
/// With `[honcho]`, read the ledger through first. Then create what is missing under `memory/`,
|
||||
/// open the index and catch it up, and bind the three sockets. The error names what failed.
|
||||
pub fn start(config: Config, log: Log) -> Result<Running, StartError> {
|
||||
let honcho = match config.honcho_config()? {
|
||||
Some(honcho) => {
|
||||
let path = config.memory_dir().join(turns::LEDGER);
|
||||
let ledger = Ledger::open(&path).map_err(StartError::Ledger)?;
|
||||
if ledger.repaired() {
|
||||
(log)(
|
||||
format!(
|
||||
"memoryd: {}: a last line cut off by a crash was dropped",
|
||||
path.display()
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
Some(Mutex::new(TurnSync::new(
|
||||
ledger,
|
||||
&honcho.agent,
|
||||
&config.sessions_dir(),
|
||||
)))
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let store = Store::new(&config.memory_dir());
|
||||
store.init(&config.memory.soul_starter)?;
|
||||
let index = Index::open(&config.index_path(), config.index.max_class)?;
|
||||
@@ -71,6 +127,7 @@ pub fn start(config: Config, log: Log) -> Result<Running, String> {
|
||||
config,
|
||||
store,
|
||||
state: Mutex::new(State { index }),
|
||||
honcho,
|
||||
log,
|
||||
}),
|
||||
listeners,
|
||||
@@ -141,6 +198,10 @@ impl Memoryd {
|
||||
Ok(request) => request,
|
||||
Err(_) => return,
|
||||
};
|
||||
let update = match (which, &request.msg) {
|
||||
(Which::Loop, Message::SessionUpdated(update)) => Some(update.clone()),
|
||||
_ => None,
|
||||
};
|
||||
let reply = self.handle(request.msg, which);
|
||||
let env = Envelope {
|
||||
v: PROTOCOL_VERSION,
|
||||
@@ -149,6 +210,29 @@ impl Memoryd {
|
||||
msg: reply,
|
||||
};
|
||||
let _ = write_frame(&mut stream, &env);
|
||||
// `loopd` has its answer before `brokerd` is asked: it sends the turn's reply only after
|
||||
// this frame, and `brokerd` may take seconds.
|
||||
drop(stream);
|
||||
if let Some(update) = update {
|
||||
self.decide_turns(&update);
|
||||
}
|
||||
}
|
||||
|
||||
/// With `[honcho]`: decide the reported turn, and every other that waits (`TurnSync`).
|
||||
fn decide_turns(&self, update: &SessionUpdated) {
|
||||
let Some(honcho) = &self.honcho else {
|
||||
return;
|
||||
};
|
||||
let socket = self.config.state_socket();
|
||||
let ask = |session: &SessionId| state::ask(&socket, session, state::WAIT);
|
||||
let mut sync = honcho.lock().unwrap_or_else(|p| p.into_inner());
|
||||
sync.updated(
|
||||
&update.session,
|
||||
update.turn,
|
||||
&ask,
|
||||
Timestamp::now(),
|
||||
&*self.log,
|
||||
);
|
||||
}
|
||||
|
||||
/// The answer to one request on one socket. A message sent to the wrong socket is refused.
|
||||
|
||||
Reference in New Issue
Block a user