SQLite engine open: SqliteOpts, connection architecture, spawn_blocking seam posture (task sqlite-engine-open-opts)
- SqliteOpts (poll_interval: Option<Duration>, None = 1 ms shipping default ADR-023 §4; max_readers, DEFAULT_MAX_READERS = 8; opts exemption from non_exhaustive per ADR-017 §3) - open(path, opts) -> Box<dyn Store>: writer slot, reader pool, SharedUpdateWatcher with fallible spawn mapped W-2-style into Error::Database; plain-path posture (ADR-023 §3) pinned by test - Substrate delta D-30: open_conn_bootstrapped — every connection (writer and pooled readers) carries the full bootstrap surface (pragmas, notify, alkstore functions, schema) per engine-sqlite.md; lineage opened readers pragmas-only. Registered in PROVENANCE.md - spawn_blocking seam helper + error mappings (string/rusqlite -> Database, source chains preserved); helper cfg(test)-gated until sqlite-engine-seam-tx wires the trait impls - Store trait stubbed with Database errors (no panics); close()/Drop join the watcher, clear subscribers (death-guard), close pool+writer - 15 new tests (open boot, watcher fanout/death-close, cadence accounting, plain-path literal filename, :memory:, pool bounding, drop teardown, seam smoke incl. panic mapping); gates green
This commit is contained in:
1 parent
5474141f2b
commit
7d400906f5
11 files changed
+937
-13
No files matched your search
@@ -324,6 +324,27 @@ pub(crate) fn open_conn(path: &str, install_notify: bool) -> Result<Connection,
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
/// Open a connection carrying the engine's full per-connection
|
||||
/// bootstrap: pragmas, notify machinery, this substrate's function
|
||||
/// set, and schema bootstrap. The engine's `open` constructor runs
|
||||
/// this on the writer and each pooled reader (engine-sqlite.md's
|
||||
/// connection architecture: "each connection runs the substrate's
|
||||
/// bootstrap at open").
|
||||
///
|
||||
/// D-30: the lineage's pools open readers through bare `open_conn`
|
||||
/// (pragmas + notify only), the function set + schema living only on
|
||||
/// the writer. This substrate extends the pool open path with
|
||||
/// `attach_alkstore_functions` + `bootstrap_schema` — the engine's
|
||||
/// reader-slot operations (claim/ack, stream reads, lock ops,
|
||||
/// scheduler checks) run this substrate's SQL functions on pooled
|
||||
/// connections, so every pooled connection must carry them.
|
||||
pub(crate) fn open_conn_bootstrapped(path: &str) -> Result<Connection, Error> {
|
||||
let conn = open_conn(path, true)?;
|
||||
super::ops::attach_alkstore_functions(&conn)?;
|
||||
bootstrap_schema(&conn)?;
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
pub(crate) struct Writer {
|
||||
slot: Mutex<Option<Connection>>,
|
||||
available: Condvar,
|
||||
@@ -411,7 +432,7 @@ impl Readers {
|
||||
*out += 1;
|
||||
drop(out);
|
||||
drop(pool);
|
||||
match open_conn(&self.path, false) {
|
||||
match open_conn_bootstrapped(&self.path) {
|
||||
Ok(conn) => {
|
||||
if self.closed.load(Ordering::Acquire) {
|
||||
*self.outstanding.lock() -= 1;
|
||||
@@ -577,6 +598,55 @@ mod writer_reader_tests {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn open_conn_bootstrapped_carries_the_full_surface_on_every_connection() {
|
||||
let dir = std::env::temp_dir().join(format!(
|
||||
"alkstore-bootstrapped-{}-{:?}",
|
||||
std::process::id(),
|
||||
std::thread::current().id()
|
||||
));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join("bootstrapped.db");
|
||||
|
||||
let first = open_conn_bootstrapped(path.to_str().unwrap()).unwrap();
|
||||
drop(first);
|
||||
|
||||
for i in 0..3 {
|
||||
let conn = open_conn_bootstrapped(path.to_str().unwrap())
|
||||
.unwrap_or_else(|e| panic!("bootstrapped open {i} failed: {e}"));
|
||||
let mode: String = conn
|
||||
.pragma_query_value(None, "journal_mode", |r| r.get(0))
|
||||
.unwrap();
|
||||
assert!(mode.eq_ignore_ascii_case("wal"), "open {i}: not WAL");
|
||||
let notify_fn: i64 = conn
|
||||
.query_row("SELECT notify('__alkstore_probe', 'x')", [], |r| r.get(0))
|
||||
.unwrap_or(0);
|
||||
assert!(notify_fn >= 1, "open {i}: notify function missing");
|
||||
let tables: i64 = conn
|
||||
.query_row(
|
||||
"SELECT count(*) FROM sqlite_master
|
||||
WHERE type = 'table' AND name LIKE '__alkstore_%'",
|
||||
[],
|
||||
|r| r.get(0),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
tables >= 7,
|
||||
"open {i}: bootstrap schema missing (saw {tables} tables)"
|
||||
);
|
||||
let publish_ready: i64 = conn
|
||||
.query_row(
|
||||
"SELECT alkstore_stream_publish('__alkstore_probe', NULL, '{}')",
|
||||
[],
|
||||
|r| r.get(0),
|
||||
)
|
||||
.unwrap_or(0);
|
||||
assert!(publish_ready >= 1, "open {i}: function set missing");
|
||||
drop(conn);
|
||||
}
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concurrent_opens_never_return_database_is_locked() {
|
||||
const ROUNDS: usize = 8;
|
||||
|
||||
Reference in new issue
Block a user