From 66a55b55b9e8ad7bea824c5c7db2214dc75de330 Mon Sep 17 00:00:00 2001 From: Greg Heartsfield Date: Fri, 6 Jan 2023 12:17:30 -0600 Subject: [PATCH] perf: new index, manually selected when appropriate --- src/db.rs | 24 +++++++++++++++++++++++- src/schema.rs | 27 ++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/db.rs b/src/db.rs index 291a4a7..6fbdbd5 100644 --- a/src/db.rs +++ b/src/db.rs @@ -501,6 +501,22 @@ fn repeat_vars(count: usize) -> String { s } +/// Decide if there is an index that should be used explicitly +fn override_index(f: &ReqFilter) -> Option { + // queries for multiple kinds default to kind_index, which is + // significantly slower than kind_created_at_index. + if let Some(ks) = &f.kinds { + if f.ids.is_none() && + ks.len() > 1 && + f.since.is_none() && + f.until.is_none() && + f.authors.is_none() { + return Some("kind_created_at_index".into()); + } + } + None +} + /// Create a dynamic SQL subquery and params from a subscription filter. fn query_from_filter(f: &ReqFilter) -> (String, Vec>) { // build a dynamic SQL query. all user-input is either an integer @@ -516,7 +532,13 @@ fn query_from_filter(f: &ReqFilter) -> (String, Vec>) { return (empty_query, empty_params); } - let mut query = "SELECT e.content, e.created_at FROM event e".to_owned(); + // check if the index needs to be overriden + let idx_name = override_index(&f); + if let Some(n) = &idx_name { + info!("using explicit index: {:?}", n); + } + let idx_stmt = idx_name.map_or_else(|| "".to_owned(), |i| format!("INDEXED BY {}",i)); + let mut query = format!("SELECT e.content, e.created_at FROM event e {}", idx_stmt); // query parameters for SQLite let mut params: Vec> = vec![]; diff --git a/src/schema.rs b/src/schema.rs index b5ab8ac..62c82e7 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -20,7 +20,7 @@ pragma mmap_size = 17179869184; -- cap mmap at 16GB "##; /// Latest database version -pub const DB_VERSION: usize = 13; +pub const DB_VERSION: usize = 14; /// Schema definition const INIT_SQL: &str = formatcp!( @@ -50,10 +50,12 @@ content TEXT NOT NULL -- serialized json of event object -- Event Indexes CREATE UNIQUE INDEX IF NOT EXISTS event_hash_index ON event(event_hash); CREATE INDEX IF NOT EXISTS author_index ON event(author); +CREATE INDEX IF NOT EXISTS kind_index ON event(kind); CREATE INDEX IF NOT EXISTS created_at_index ON event(created_at); CREATE INDEX IF NOT EXISTS delegated_by_index ON event(delegated_by); CREATE INDEX IF NOT EXISTS event_composite_index ON event(kind,created_at); CREATE INDEX IF NOT EXISTS kind_author_index ON event(kind,author); +CREATE INDEX IF NOT EXISTS kind_created_at_index ON event(kind,created_at); -- Tag Table -- Tag values are stored as either a BLOB (if they come in as a @@ -193,6 +195,9 @@ pub fn upgrade_db(conn: &mut PooledConnection) -> Result<()> { if curr_version == 12 { curr_version = mig_12_to_13(conn)?; } + if curr_version == 13 { + curr_version = mig_13_to_14(conn)?; + } if curr_version == DB_VERSION { info!( @@ -593,3 +598,23 @@ PRAGMA user_version = 13; } Ok(13) } + +fn mig_13_to_14(conn: &mut PooledConnection) -> Result { + info!("database schema needs update from 13->14"); + let upgrade_sql = r##" +CREATE INDEX IF NOT EXISTS kind_index ON event(kind); +CREATE INDEX IF NOT EXISTS kind_created_at_index ON event(kind,created_at); +pragma optimize; +PRAGMA user_version = 14; +"##; + match conn.execute_batch(upgrade_sql) { + Ok(()) => { + info!("database schema upgraded v13 -> v14"); + } + Err(err) => { + error!("update failed: {}", err); + panic!("database could not be upgraded"); + } + } + Ok(14) +}