Skip to content

Commit 0246394

Browse files
committed
Don't update last_used if the new value is the same
This reduces time by -67% in benchmarks for continuous reads.
1 parent b3a359b commit 0246394

File tree

5 files changed

+70
-12
lines changed

5 files changed

+70
-12
lines changed

src/c_api/ext_fns/handle.rs

+8
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,14 @@ pub extern "C" fn possum_single_read_at(
133133
Err(err) => return err.into(),
134134
Ok(ok) => ok,
135135
};
136+
// eprintln!(
137+
// "reading single {} bytes at {} from {}, read {}: {}",
138+
// read_buf.len(),
139+
// offset,
140+
// value.length(),
141+
// r_nbyte,
142+
// rust_key.escape_ascii(),
143+
// );
136144
buf.size = r_nbyte;
137145
NoError
138146
}

src/handle.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,8 @@ impl Handle {
257257
/// Begins a read transaction.
258258
pub fn read(&self) -> rusqlite::Result<Reader<OwnedTx>> {
259259
let reader = Reader {
260-
owned_tx: self.start_deferred_transaction()?,
260+
owned_tx: self
261+
.start_writable_transaction_with_behaviour(TransactionBehavior::Immediate)?,
261262
reads: Default::default(),
262263
};
263264
Ok(reader)

src/lib.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -480,10 +480,15 @@ impl Deref for Value {
480480

481481
impl Value {
482482
fn from_row(row: &rusqlite::Row) -> rusqlite::Result<Self> {
483-
let file_id: Option<FileId> = row.get(0)?;
484-
let file_offset: Option<u64> = row.get(1)?;
485-
let length = row.get(2)?;
486-
let last_used = row.get(3)?;
483+
Self::from_column_values(row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)
484+
}
485+
486+
fn from_column_values(
487+
file_id: Option<FileId>,
488+
file_offset: Option<u64>,
489+
length: ValueLength,
490+
last_used: Timestamp,
491+
) -> rusqlite::Result<Self> {
487492
let location = if length == 0 {
488493
assert_eq!(file_id, None);
489494
assert_eq!(file_offset, None);

src/tests.rs

+22
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::thread::*;
33
use std::time::*;
44

55
use anyhow::Result;
6+
use rusqlite::TransactionState;
67

78
use self::test;
89
use super::*;
@@ -150,3 +151,24 @@ fn test_torrent_storage_benchmark() -> anyhow::Result<()> {
150151
use testing::torrent_storage::*;
151152
BENCHMARK_OPTS.build()?.run()
152153
}
154+
155+
/// Show that update moves a transaction to write, even if nothing is changed. This was an
156+
/// investigation on how to optimize touch_for_read if last_used doesn't change.
157+
#[test]
158+
fn test_sqlite_update_same_value_txn_state() -> Result<()> {
159+
let mut conn = rusqlite::Connection::open_in_memory()?;
160+
conn.execute_batch(
161+
r"
162+
create table a(b);
163+
--insert into a values (1);
164+
",
165+
)?;
166+
let tx = conn.transaction()?;
167+
assert_eq!(tx.transaction_state(None)?, TransactionState::None);
168+
let changed = tx.execute("update a set b=1", [])?;
169+
// No rows were changed.
170+
assert_eq!(changed, 0);
171+
// But now we're a write transaction anyway.
172+
assert_eq!(tx.transaction_state(None)?, TransactionState::Write);
173+
Ok(())
174+
}

src/tx.rs

+29-7
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,37 @@ impl<T> ReadTransaction for T where T: ReadOnlyTransactionAccessor {}
185185

186186
impl<'h, H> Transaction<'h, H> {
187187
pub fn touch_for_read(&mut self, key: &[u8]) -> rusqlite::Result<Value> {
188-
self.tx
189-
.prepare_cached(&format!(
190-
"update keys \
191-
set last_used=cast(unixepoch('subsec')*1e3 as integer) \
192-
where key=? \
193-
returning {}",
188+
// Avoid modifying the manifest. We had to take a write lock already to ensure our data
189+
// isn't modified on us, but it still seems to be an improvement. (-67% on read times in
190+
// fact).
191+
let (file_id, file_offset, value_length, mut last_used, now) = self
192+
.tx
193+
.prepare_cached_readonly(&format!(
194+
"select {}, cast(unixepoch('subsec')*1e3 as integer) \
195+
from keys where key=?",
194196
value_columns_sql()
195197
))?
196-
.query_row([key], Value::from_row)
198+
.query_row([key], |row| row.try_into())?;
199+
let update_last_used = last_used != now;
200+
// eprintln!("updating last used: {}", update_last_used);
201+
if update_last_used {
202+
let (new_last_used,) = self
203+
.tx
204+
.prepare_cached(
205+
r"
206+
update keys
207+
set last_used=cast(unixepoch('subsec')*1e3 as integer)
208+
where key=?
209+
returning last_used
210+
",
211+
)?
212+
.query_row([key], |row| row.try_into())?;
213+
// This can in fact change between calls. Since we're updating now anyway, we don't
214+
// really care.
215+
//assert_eq!(new_last_used, now);
216+
last_used = new_last_used;
217+
}
218+
Value::from_column_values(file_id, file_offset, value_length, last_used)
197219
}
198220
}
199221

0 commit comments

Comments
 (0)