SQLite: Fix thread pool exhaustion

This commit is contained in:
mdecimus
2026-02-25 14:33:52 +01:00
parent d340e7d469
commit c3586d9af6
5 changed files with 38 additions and 32 deletions

View File

@@ -16,8 +16,9 @@ impl SqliteStore {
key: &[u8], key: &[u8],
range: Range<usize>, range: Range<usize>,
) -> trc::Result<Option<Vec<u8>>> { ) -> trc::Result<Option<Vec<u8>>> {
let conn = self.conn_pool.get().map_err(into_error)?; let manager = self.conn_pool.clone();
self.spawn_worker(move || { self.spawn_worker(move || {
let conn = manager.get().map_err(into_error)?;
let mut result = conn let mut result = conn
.prepare_cached("SELECT v FROM t WHERE k = ?") .prepare_cached("SELECT v FROM t WHERE k = ?")
.map_err(into_error)?; .map_err(into_error)?;
@@ -42,8 +43,9 @@ impl SqliteStore {
} }
pub(crate) async fn put_blob(&self, key: &[u8], data: &[u8]) -> trc::Result<()> { pub(crate) async fn put_blob(&self, key: &[u8], data: &[u8]) -> trc::Result<()> {
let conn = self.conn_pool.get().map_err(into_error)?; let manager = self.conn_pool.clone();
self.spawn_worker(move || { self.spawn_worker(move || {
let conn = manager.get().map_err(into_error)?;
conn.prepare_cached("INSERT OR REPLACE INTO t (k, v) VALUES (?, ?)") conn.prepare_cached("INSERT OR REPLACE INTO t (k, v) VALUES (?, ?)")
.map_err(into_error)? .map_err(into_error)?
.execute([key, data]) .execute([key, data])
@@ -54,8 +56,9 @@ impl SqliteStore {
} }
pub(crate) async fn delete_blob(&self, key: &[u8]) -> trc::Result<bool> { pub(crate) async fn delete_blob(&self, key: &[u8]) -> trc::Result<bool> {
let conn = self.conn_pool.get().map_err(into_error)?; let manager = self.conn_pool.clone();
self.spawn_worker(move || { self.spawn_worker(move || {
let conn = manager.get().map_err(into_error)?;
conn.prepare_cached("DELETE FROM t WHERE k = ?") conn.prepare_cached("DELETE FROM t WHERE k = ?")
.map_err(into_error)? .map_err(into_error)?
.execute([key]) .execute([key])

View File

@@ -16,8 +16,9 @@ impl SqliteStore {
query: &str, query: &str,
params_: &[Value<'_>], params_: &[Value<'_>],
) -> trc::Result<T> { ) -> trc::Result<T> {
let conn = self.conn_pool.get().map_err(into_error)?; let manager = self.conn_pool.clone();
self.spawn_worker(move || { self.spawn_worker(move || {
let conn = manager.get().map_err(into_error)?;
let mut s = conn.prepare_cached(query).map_err(into_error)?; let mut s = conn.prepare_cached(query).map_err(into_error)?;
let params = params_ let params = params_
.iter() .iter()

View File

@@ -79,6 +79,7 @@ impl SqliteStore {
SUBSPACE_TELEMETRY_SPAN, SUBSPACE_TELEMETRY_SPAN,
SUBSPACE_TELEMETRY_METRIC, SUBSPACE_TELEMETRY_METRIC,
SUBSPACE_SEARCH_INDEX, SUBSPACE_SEARCH_INDEX,
SUBSPACE_DIRECTORY,
] { ] {
let table = char::from(table); let table = char::from(table);
conn.execute( conn.execute(
@@ -93,16 +94,22 @@ impl SqliteStore {
.map_err(into_error)?; .map_err(into_error)?;
} }
let table = char::from(SUBSPACE_INDEXES); for table in [
conn.execute( SUBSPACE_INDEXES,
&format!( SUBSPACE_REGISTRY_IDX,
"CREATE TABLE IF NOT EXISTS {table} ( SUBSPACE_REGISTRY_IDX_GLOBAL,
] {
let table = char::from(table);
conn.execute(
&format!(
"CREATE TABLE IF NOT EXISTS {table} (
k BLOB PRIMARY KEY k BLOB PRIMARY KEY
)" )"
), ),
[], [],
) )
.map_err(into_error)?; .map_err(into_error)?;
}
for table in [SUBSPACE_COUNTER, SUBSPACE_QUOTA, SUBSPACE_IN_MEMORY_COUNTER] { for table in [SUBSPACE_COUNTER, SUBSPACE_QUOTA, SUBSPACE_IN_MEMORY_COUNTER] {
conn.execute( conn.execute(

View File

@@ -13,8 +13,9 @@ impl SqliteStore {
where where
U: Deserialize + 'static, U: Deserialize + 'static,
{ {
let conn = self.conn_pool.get().map_err(into_error)?; let manager = self.conn_pool.clone();
self.spawn_worker(move || { self.spawn_worker(move || {
let conn = manager.get().map_err(into_error)?;
let mut result = conn let mut result = conn
.prepare_cached(&format!( .prepare_cached(&format!(
"SELECT v FROM {} WHERE k = ?", "SELECT v FROM {} WHERE k = ?",
@@ -24,7 +25,7 @@ impl SqliteStore {
let key = key.serialize(0); let key = key.serialize(0);
result result
.query_row([&key], |row| { .query_row([&key], |row| {
U::deserialize(row.get_ref(0)?.as_bytes()?) U::deserialize_with_key(&key, row.get_ref(0)?.as_bytes()?)
.map_err(|err| rusqlite::Error::ToSqlConversionFailure(err.into())) .map_err(|err| rusqlite::Error::ToSqlConversionFailure(err.into()))
}) })
.optional() .optional()
@@ -38,9 +39,9 @@ impl SqliteStore {
params: IterateParams<T>, params: IterateParams<T>,
mut cb: impl for<'x> FnMut(&'x [u8], &'x [u8]) -> trc::Result<bool> + Sync + Send, mut cb: impl for<'x> FnMut(&'x [u8], &'x [u8]) -> trc::Result<bool> + Sync + Send,
) -> trc::Result<()> { ) -> trc::Result<()> {
let conn = self.conn_pool.get().map_err(into_error)?; let manager = self.conn_pool.clone();
self.spawn_worker(move || { self.spawn_worker(move || {
let conn = manager.get().map_err(into_error)?;
let table = char::from(params.begin.subspace()); let table = char::from(params.begin.subspace());
let begin = params.begin.serialize(0); let begin = params.begin.serialize(0);
let end = params.end.serialize(0); let end = params.end.serialize(0);
@@ -113,8 +114,9 @@ impl SqliteStore {
let key = key.into(); let key = key.into();
let table = char::from(key.subspace()); let table = char::from(key.subspace());
let key = key.serialize(0); let key = key.serialize(0);
let conn = self.conn_pool.get().map_err(into_error)?; let manager = self.conn_pool.clone();
self.spawn_worker(move || { self.spawn_worker(move || {
let conn = manager.get().map_err(into_error)?;
match conn match conn
.prepare_cached(&format!("SELECT v FROM {table} WHERE k = ?")) .prepare_cached(&format!("SELECT v FROM {table} WHERE k = ?"))
.map_err(into_error)? .map_err(into_error)?

View File

@@ -14,12 +14,10 @@ use trc::AddContext;
impl SqliteStore { impl SqliteStore {
pub(crate) async fn write(&self, batch: Batch<'_>) -> trc::Result<AssignedIds> { pub(crate) async fn write(&self, batch: Batch<'_>) -> trc::Result<AssignedIds> {
let mut conn = self let manager = self.conn_pool.clone();
.conn_pool
.get()
.map_err(into_error)
.caused_by(trc::location!())?;
self.spawn_worker(move || { self.spawn_worker(move || {
let mut conn = manager.get().map_err(into_error)?;
let mut account_id = u32::MAX; let mut account_id = u32::MAX;
let mut collection = u8::MAX; let mut collection = u8::MAX;
let mut document_id = u32::MAX; let mut document_id = u32::MAX;
@@ -271,12 +269,9 @@ impl SqliteStore {
} }
pub(crate) async fn purge_store(&self) -> trc::Result<()> { pub(crate) async fn purge_store(&self) -> trc::Result<()> {
let conn = self let manager = self.conn_pool.clone();
.conn_pool
.get()
.map_err(into_error)
.caused_by(trc::location!())?;
self.spawn_worker(move || { self.spawn_worker(move || {
let conn = manager.get().map_err(into_error)?;
for subspace in [SUBSPACE_QUOTA, SUBSPACE_COUNTER, SUBSPACE_IN_MEMORY_COUNTER] { for subspace in [SUBSPACE_QUOTA, SUBSPACE_COUNTER, SUBSPACE_IN_MEMORY_COUNTER] {
conn.prepare_cached(&format!("DELETE FROM {} WHERE v = 0", char::from(subspace),)) conn.prepare_cached(&format!("DELETE FROM {} WHERE v = 0", char::from(subspace),))
.map_err(into_error) .map_err(into_error)
@@ -292,12 +287,10 @@ impl SqliteStore {
} }
pub(crate) async fn delete_range(&self, from: impl Key, to: impl Key) -> trc::Result<()> { pub(crate) async fn delete_range(&self, from: impl Key, to: impl Key) -> trc::Result<()> {
let conn = self let manager = self.conn_pool.clone();
.conn_pool
.get()
.map_err(into_error)
.caused_by(trc::location!())?;
self.spawn_worker(move || { self.spawn_worker(move || {
let conn = manager.get().map_err(into_error)?;
conn.prepare_cached(&format!( conn.prepare_cached(&format!(
"DELETE FROM {} WHERE k >= ? AND k < ?", "DELETE FROM {} WHERE k >= ? AND k < ?",
char::from(from.subspace()), char::from(from.subspace()),