sqlite: the listen bridge takes the stream bridge's std-thread shape (review 004 item 8) — one dedicated named thread per subscription doing blocking_send, not a spawn_blocking task (past ~500 listeners the blocking-pool ceiling queued the whole engine's seams); spawn failure unsubscribes and fails closed with Error::Database, matching the stream bridge's posture; notify.rs + engine-sqlite.md docs aligned; task -> completed
This commit is contained in:
1 parent
0fadf0f224
commit
acdef94058
3 files changed
+67
-12
No files matched your search
@@ -9,9 +9,12 @@
|
||||
//! never produces [`Error::PayloadTooLarge`](alkstore::Error) (ADR-016
|
||||
//! §5); the payload crosses the notify table as the serde_json
|
||||
//! serialization's UTF-8 text, the byte form ADR-020 §4 pins.
|
||||
//! - `listen` — the watcher-fanout bridge: one `spawn_blocking` thread
|
||||
//! per subscription (`engine-sqlite.md`'s mapping row) looping
|
||||
//! `recv()` on the substrate's sync feed (1-slot — bursts coalesce)
|
||||
//! - `listen` — the watcher-fanout bridge: one dedicated `std::thread`
|
||||
//! per subscription (the stream bridge's aligned shape — review 004
|
||||
//! item 8; not a tokio `spawn_blocking` per subscription, which
|
||||
//! would queue the engine's seams behind parked bridges at tokio's
|
||||
//! blocking-pool ceiling) looping `recv()` on the substrate's sync
|
||||
//! feed (1-slot — bursts coalesce)
|
||||
//! and `blocking_send`-ing [`Wake`] values into a tokio channel the
|
||||
//! [`WakeReceiver`] owns. Wakes are coalesced hints (overtrigger on
|
||||
//! purpose, ADR-006): the watcher fires on *any* commit, so a wake
|
||||
@@ -88,6 +91,10 @@ pub(crate) fn notify(
|
||||
/// against a store whose watcher has *died* (the db file was replaced)
|
||||
/// fails closed the same way — a new subscription never enrolls onto a
|
||||
/// dead feed to park forever (review 004 item 2's new-subscriber arm).
|
||||
/// The bridge runs on a dedicated `std::thread` (the stream bridge's
|
||||
/// shape — review 004 item 8); a thread-spawn failure unsubscribes and
|
||||
/// fails closed with [`Error::Database`](alkstore::Error) (the stream
|
||||
/// bridge's posture).
|
||||
pub(crate) fn listen(
|
||||
watcher: Arc<SharedUpdateWatcher>,
|
||||
closed: Arc<AtomicBool>,
|
||||
@@ -102,13 +109,21 @@ pub(crate) fn listen(
|
||||
let (id, rx) = watcher.subscribe().map_err(database_error)?;
|
||||
let (wake_tx, wake_rx) = tokio::sync::mpsc::channel(WAKE_CHANNEL_CAPACITY);
|
||||
let channel = channel.to_string();
|
||||
let _bridge = tokio::task::spawn_blocking(move || {
|
||||
while let Ok(()) = rx.recv() {
|
||||
if wake_tx.blocking_send(Wake::new(channel.clone())).is_err() {
|
||||
break;
|
||||
let spawned = std::thread::Builder::new()
|
||||
.name(format!("alkstore-listen-bridge-{channel}"))
|
||||
.spawn(move || {
|
||||
while let Ok(()) = rx.recv() {
|
||||
if wake_tx.blocking_send(Wake::new(channel.clone())).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if let Err(e) = spawned {
|
||||
watcher.unsubscribe(id);
|
||||
return Err(Error::database(std::io::Error::other(format!(
|
||||
"failed to spawn listen bridge thread: {e}"
|
||||
))));
|
||||
}
|
||||
Ok(Box::new(SqliteWakeReceiver {
|
||||
wake_rx,
|
||||
watcher,
|
||||
|
||||
Reference in new issue
Block a user