brokerd: runbook pointers for startup failures, no thread panics, args_os
M3a review findings 3, 5 (the cast), 6, 7 (brokerd), 11. A config, directory or socket failure at start now ends with docs/runbook.md#brokerd-start-failed, and losing a listener with #brokerd-listener-lost; both entries are new. Threads start through thread::Builder, so a refused thread is reported instead of silently killing a listener; an aborted connection no longer stops the daemon. brokerd reads args_os and keeps the config path as a path. The "requester went away" result is recorded at the time it happens. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+56
-25
@@ -3,7 +3,7 @@
|
||||
use std::fs::{DirBuilder, Permissions};
|
||||
use std::io;
|
||||
use std::os::unix::fs::{DirBuilderExt, PermissionsExt};
|
||||
use std::os::unix::net::UnixListener;
|
||||
use std::os::unix::net::{UnixListener, UnixStream};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc;
|
||||
@@ -148,21 +148,24 @@ impl Started {
|
||||
} = self;
|
||||
|
||||
// The expiry thread: once a second, expire the approvals whose time has run out.
|
||||
// `Builder` rather than `spawn`, which panics when the system refuses a thread.
|
||||
{
|
||||
let broker = Arc::clone(&broker);
|
||||
std::thread::spawn(move || {
|
||||
loop {
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
admin::expire_due(&broker, Timestamp::now());
|
||||
}
|
||||
});
|
||||
std::thread::Builder::new()
|
||||
.name("expiry".to_string())
|
||||
.spawn(move || {
|
||||
loop {
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
admin::expire_due(&broker, Timestamp::now());
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
||||
// One accept thread per socket, each with its own clone of the broker.
|
||||
let (tx, rx) = mpsc::channel::<io::Error>();
|
||||
|
||||
accept_loop(tools, Arc::clone(&broker), tx.clone(), Which::Tools);
|
||||
accept_loop(admin, Arc::clone(&broker), tx, Which::Admin);
|
||||
accept_loop(tools, Arc::clone(&broker), tx.clone(), Which::Tools)?;
|
||||
accept_loop(admin, Arc::clone(&broker), tx, Which::Admin)?;
|
||||
|
||||
// The first listener error stops the daemon.
|
||||
match rx.recv() {
|
||||
@@ -172,28 +175,56 @@ impl Started {
|
||||
}
|
||||
}
|
||||
|
||||
/// Accept streams from one listener forever, spawning a handler thread per stream.
|
||||
/// Accept streams from one listener forever, starting a handler thread per stream. An error that
|
||||
/// only concerns one connection (the peer aborted it, a signal interrupted `accept`) is skipped;
|
||||
/// any other stops the daemon through `tx`.
|
||||
fn accept_loop(
|
||||
listener: UnixListener,
|
||||
broker: Arc<Broker>,
|
||||
tx: mpsc::Sender<io::Error>,
|
||||
which: Which,
|
||||
) {
|
||||
std::thread::spawn(move || {
|
||||
for stream in listener.incoming() {
|
||||
match stream {
|
||||
Ok(stream) => {
|
||||
let broker = Arc::clone(&broker);
|
||||
std::thread::spawn(move || match which {
|
||||
Which::Tools => broker::handle(stream, &broker),
|
||||
Which::Admin => admin::handle(stream, &broker),
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
// Once `run` has returned, nobody receives; dropping this is correct.
|
||||
let _ = tx.send(e);
|
||||
) -> io::Result<()> {
|
||||
let name = match which {
|
||||
Which::Tools => "accept-broker",
|
||||
Which::Admin => "accept-admin",
|
||||
};
|
||||
std::thread::Builder::new()
|
||||
.name(name.to_string())
|
||||
.spawn(move || {
|
||||
for stream in listener.incoming() {
|
||||
match stream {
|
||||
Ok(stream) => serve_one(stream, &broker, which),
|
||||
Err(e) if one_connection_only(&e) => {}
|
||||
Err(e) => {
|
||||
// Once `run` has returned, nobody receives; dropping this is correct.
|
||||
let _ = tx.send(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Hand one stream to its own thread. If the system refuses the thread, the stream is closed, which
|
||||
/// the peer sees as the broker being unavailable, and the refusal is printed.
|
||||
fn serve_one(stream: UnixStream, broker: &Arc<Broker>, which: Which) {
|
||||
let for_thread = Arc::clone(broker);
|
||||
let started = std::thread::Builder::new().spawn(move || match which {
|
||||
Which::Tools => broker::handle(stream, &for_thread),
|
||||
Which::Admin => admin::handle(stream, &for_thread),
|
||||
});
|
||||
if let Err(e) = started {
|
||||
broker.log(&format!(
|
||||
"brokerd: cannot start a thread for a connection, so it was closed: {e}\nsee docs/runbook.md#brokerd-listener-lost"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors from `accept` that end one connection, not the listener.
|
||||
fn one_connection_only(e: &io::Error) -> bool {
|
||||
matches!(
|
||||
e.kind(),
|
||||
io::ErrorKind::ConnectionAborted | io::ErrorKind::Interrupted | io::ErrorKind::WouldBlock
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user