review: a session log that cannot be read now is waited out
The worker marked a turn failed on any error reading its log. A missing log, a damaged line or a turn not found still fail it; any other I/O error is now waited out with the outage's doubling wait, logged once when it starts and once when the log reads again (pointer session-log-damaged, whose runbook entry now covers it), shown by bxctl memory sync, and the turn stays waiting. read_records tells an unreadable file from a damaged line (read_log is unchanged). The message helpers move from worker.rs to honcho/messages.rs, re-exported, to keep worker.rs under 500 lines. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -57,14 +57,38 @@ pub fn log_path(sessions: &Path, session: &SessionId) -> PathBuf {
|
||||
sessions.join(session.as_str()).join("0.jsonl")
|
||||
}
|
||||
|
||||
/// Why a session log did not read. Each names the file; `Display` adds the runbook pointer.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum LogError {
|
||||
/// The file exists but could not be read (a permission, an I/O error): it may read later.
|
||||
Io(String),
|
||||
/// A complete line that does not parse, and its number: it will not read later.
|
||||
Damaged(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LogError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
LogError::Io(why) | LogError::Damaged(why) => write!(f, "{why}; {LOG_RUNBOOK}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for LogError {}
|
||||
|
||||
/// A session log's complete lines as records. A missing file has none. A last line without its
|
||||
/// newline is one `loopd` is still writing, and is left out. A complete line that does not parse
|
||||
/// is an error naming the file and the line, with the runbook pointer.
|
||||
pub fn read_log(path: &Path) -> Result<Vec<LogRecord>, String> {
|
||||
read_records(path).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// `read_log`, telling a file that could not be read from a damaged line.
|
||||
pub fn read_records(path: &Path) -> Result<Vec<LogRecord>, LogError> {
|
||||
let text = match std::fs::read(path) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()),
|
||||
Err(e) => return Err(format!("{}: {e}; {LOG_RUNBOOK}", path.display())),
|
||||
Err(e) => return Err(LogError::Io(format!("{}: {e}", path.display()))),
|
||||
};
|
||||
let whole = match text.iter().rposition(|b| *b == b'\n') {
|
||||
Some(last) => last + 1,
|
||||
@@ -80,11 +104,11 @@ pub fn read_log(path: &Path) -> Result<Vec<LogRecord>, String> {
|
||||
match serde_json::from_slice::<LogRecord>(line) {
|
||||
Ok(record) => records.push(record),
|
||||
Err(e) => {
|
||||
return Err(format!(
|
||||
"{}:{}: {e}; {LOG_RUNBOOK}",
|
||||
return Err(LogError::Damaged(format!(
|
||||
"{}:{}: {e}",
|
||||
path.display(),
|
||||
index + 1
|
||||
));
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user