Skip to content

Commit 5fc6a0a

Browse files
authored
feat: support read-only sqlite databases (#16)
1 parent 99f8241 commit 5fc6a0a

File tree

2 files changed

+38
-13
lines changed

2 files changed

+38
-13
lines changed

sqlite/backend.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use denokv_proto::VALUE_ENCODING_V8;
2222
use rand::Rng;
2323
use rand::RngCore;
2424
use rusqlite::params;
25+
use rusqlite::DatabaseName;
2526
use rusqlite::OptionalExtension;
2627
use rusqlite::Transaction;
2728
use uuid::Uuid;
@@ -139,6 +140,7 @@ pub struct SqliteBackend {
139140
rng: Box<dyn RngCore + Send>,
140141
pub dequeue_notify: Arc<tokio::sync::Notify>,
141142
pub messages_running: HashSet<QueueMessageId>,
143+
pub readonly: bool,
142144
}
143145

144146
impl SqliteBackend {
@@ -164,15 +166,20 @@ impl SqliteBackend {
164166
dequeue_notify: Arc<tokio::sync::Notify>,
165167
rng: Box<dyn RngCore + Send>,
166168
) -> Result<Self, anyhow::Error> {
167-
conn.pragma_update(None, "journal_mode", "wal")?;
168-
169+
let readonly = conn.is_readonly(DatabaseName::Main)?;
169170
let mut this = Self {
170171
conn,
171172
rng,
172173
dequeue_notify,
173174
messages_running: HashSet::new(),
175+
readonly,
174176
};
175177

178+
if readonly {
179+
return Ok(this);
180+
}
181+
182+
this.conn.pragma_update(None, "journal_mode", "wal")?;
176183
this.run_tx(|tx, _| {
177184
tx.execute(STATEMENT_CREATE_MIGRATION_TABLE, [])?;
178185

@@ -246,6 +253,12 @@ impl SqliteBackend {
246253
&mut self,
247254
write: AtomicWrite,
248255
) -> Result<Option<CommitResult>, anyhow::Error> {
256+
if self.readonly {
257+
return Err(
258+
TypeError("Cannot write to a read-only database".to_string()).into(),
259+
);
260+
}
261+
249262
let (has_enqueues, commit_result) = self.run_tx(|tx, rng| {
250263
for check in &write.checks {
251264
let real_versionstamp = tx

sqlite/lib.rs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,20 @@ async fn sqlite_thread(
114114
mut request_rx: tokio::sync::mpsc::Receiver<SqliteRequest>,
115115
) {
116116
let start = utc_now();
117-
if let Err(err) = backend.queue_cleanup() {
118-
panic!("KV queue cleanup failed: {err}");
117+
if !backend.readonly {
118+
if let Err(err) = backend.queue_cleanup() {
119+
panic!("KV queue cleanup failed: {err}");
120+
}
119121
}
120122
let mut dequeue_channels: VecDeque<oneshot::Sender<DequeuedMessage>> =
121123
VecDeque::with_capacity(4);
122-
let queue_next_ready = match backend.queue_next_ready() {
123-
Ok(queue_next_ready) => queue_next_ready,
124-
Err(err) => panic!("KV queue_next_ready failed: {err}"),
124+
let queue_next_ready = if backend.readonly {
125+
None
126+
} else {
127+
match backend.queue_next_ready() {
128+
Ok(queue_next_ready) => queue_next_ready,
129+
Err(err) => panic!("KV queue_next_ready failed: {err}"),
130+
}
125131
};
126132
let mut queue_dequeue_deadline = compute_deadline_with_max_and_jitter(
127133
queue_next_ready,
@@ -132,9 +138,13 @@ async fn sqlite_thread(
132138
start + duration_with_jitter(QUEUE_RUNNING_CLEANUP_INTERVAL);
133139
let mut queue_keepalive_deadline =
134140
start + duration_with_jitter(QUEUE_MESSAGE_DEADLINE_UPDATE_INTERVAL);
135-
let expiry_next_ready = match backend.collect_expired() {
136-
Ok(expiry_next_ready) => expiry_next_ready,
137-
Err(err) => panic!("KV collect expired failed: {err}"),
141+
let expiry_next_ready = if backend.readonly {
142+
None
143+
} else {
144+
match backend.collect_expired() {
145+
Ok(expiry_next_ready) => expiry_next_ready,
146+
Err(err) => panic!("KV collect expired failed: {err}"),
147+
}
138148
};
139149
let mut expiry_deadline = compute_deadline_with_max_and_jitter(
140150
expiry_next_ready,
@@ -149,12 +159,14 @@ async fn sqlite_thread(
149159
if !dequeue_channels.is_empty() {
150160
closest_deadline = closest_deadline.min(queue_dequeue_deadline);
151161
}
152-
let timer = if let Ok(time_to_closest_deadline) =
162+
let timer = if backend.readonly {
163+
Either::Left(futures::future::pending::<()>())
164+
} else if let Ok(time_to_closest_deadline) =
153165
closest_deadline.signed_duration_since(now).to_std()
154166
{
155-
Either::Left(tokio::time::sleep(time_to_closest_deadline))
167+
Either::Right(Either::Left(tokio::time::sleep(time_to_closest_deadline)))
156168
} else {
157-
Either::Right(futures::future::ready(()))
169+
Either::Right(Either::Right(futures::future::ready(())))
158170
};
159171
select! {
160172
biased;

0 commit comments

Comments
 (0)