coven_database/store/
store_reads.rs1use super::{host_sql_reads::HostSqlReads, SqlReadContext};
2use crate::{DbError, QueryDependencies};
3use coven_foundation::bounded_workers::BoundedWorkers;
4use rusqlite::{Connection, OpenFlags};
5use std::{num::NonZeroUsize, path::Path};
6
7const WORKERS: usize = 4;
8const QUEUED: NonZeroUsize = NonZeroUsize::new(64).expect("positive queue capacity");
9
10#[derive(Clone)]
13pub struct StoreReads {
14 connections: BoundedWorkers<Connection>,
15 processing: BoundedWorkers<()>,
16}
17
18impl StoreReads {
19 pub fn open(path: &Path) -> Result<Self, DbError> {
22 let connections = (0..WORKERS)
23 .map(|_| {
24 let connection = Connection::open_with_flags(
25 path,
26 OpenFlags::SQLITE_OPEN_READ_ONLY
27 | OpenFlags::SQLITE_OPEN_NO_MUTEX
28 | OpenFlags::SQLITE_OPEN_URI,
29 )?;
30 connection.pragma_update(None, "foreign_keys", "ON")?;
31 Ok(connection)
32 })
33 .collect::<rusqlite::Result<Vec<_>>>()
34 .map_err(DbError::from)?;
35 Ok(Self {
36 connections: BoundedWorkers::start(connections, QUEUED, "coven-read")
37 .map_err(|e| DbError::context("start read workers", e))?,
38 processing: BoundedWorkers::start(vec![(); WORKERS], QUEUED, "coven-read-processing")
39 .map_err(|e| DbError::context("start read processing workers", e))?,
40 })
41 }
42
43 pub async fn read<F, R, E>(&self, read: F) -> Result<Result<R, E>, DbError>
44 where
45 F: for<'connection> FnOnce(SqlReadContext<'connection>) -> Result<R, E> + Send + 'static,
46 R: Send + 'static,
47 E: Send + 'static,
48 {
49 self.connections
50 .call(move |connection| HostSqlReads::new(connection).read(read))
51 .await
52 }
53
54 pub async fn read_tracked<F, R, E>(
55 &self,
56 read: F,
57 ) -> Result<(Result<R, E>, QueryDependencies), DbError>
58 where
59 F: for<'connection> FnOnce(SqlReadContext<'connection>) -> Result<R, E> + Send + 'static,
60 R: Send + 'static,
61 E: Send + 'static,
62 {
63 self.connections
64 .call(move |connection| HostSqlReads::new(connection).read_tracked(read))
65 .await
66 }
67
68 pub async fn process<F, R>(&self, process: F) -> R
70 where
71 F: FnOnce() -> R + Send + 'static,
72 R: Send + 'static,
73 {
74 self.processing.call(move |()| process()).await
75 }
76}