-
Notifications
You must be signed in to change notification settings - Fork 759
perf: sqlite prepare cached #7414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
30926da
031d08c
9b323ef
442cfe2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Cache prepared statements on hot SQLite read paths (Clarity side-store reads, MARF block-id/hash lookups, and the generic `query_*` helpers) instead of re-parsing the SQL on every call, and raise the per-connection statement-cache capacity so the cache is not LRU-thrashed | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,7 +54,10 @@ pub struct SqliteConnection { | |
|
|
||
| fn sqlite_put(conn: &Connection, key: &str, value: &str) -> Result<(), VmExecutionError> { | ||
| let params = params![key, value]; | ||
| match conn.execute("REPLACE INTO data_table (key, value) VALUES (?, ?)", params) { | ||
| match conn | ||
| .prepare_cached("REPLACE INTO data_table (key, value) VALUES (?, ?)") | ||
| .and_then(|mut stmt| stmt.execute(params)) | ||
| { | ||
| Ok(_) => Ok(()), | ||
| Err(e) => { | ||
| error!("Failed to insert/replace ({key},{value}): {e:?}"); | ||
|
|
@@ -67,11 +70,8 @@ fn sqlite_get(conn: &Connection, key: &str) -> Result<Option<String>, VmExecutio | |
| trace!("sqlite_get {key}"); | ||
| let params = params![key]; | ||
| let res = match conn | ||
| .query_row( | ||
| "SELECT value FROM data_table WHERE key = ?", | ||
| params, | ||
| |row| row.get(0), | ||
| ) | ||
| .prepare_cached("SELECT value FROM data_table WHERE key = ?") | ||
| .and_then(|mut stmt| stmt.query_row(params, |row| row.get(0))) | ||
| .optional() | ||
| { | ||
| Ok(x) => Ok(x), | ||
|
|
@@ -178,10 +178,10 @@ impl SqliteConnection { | |
| let key = Self::make_metadata_key(contract_id, key); | ||
| let params = params![bhh, key, value]; | ||
|
|
||
| if let Err(e) = conn.execute( | ||
| "INSERT INTO metadata_table (blockhash, key, value) VALUES (?, ?, ?)", | ||
| params, | ||
| ) { | ||
| if let Err(e) = conn | ||
| .prepare_cached("INSERT INTO metadata_table (blockhash, key, value) VALUES (?, ?, ?)") | ||
| .and_then(|mut stmt| stmt.execute(params)) | ||
| { | ||
| error!("Failed to insert ({bhh},{key},{value}): {e:?}"); | ||
| return Err(VmInternalError::DBError(SQL_FAIL_MESSAGE.into()).into()); | ||
| } | ||
|
|
@@ -282,11 +282,8 @@ impl SqliteConnection { | |
| let params = params![bhh, key]; | ||
|
|
||
| match conn | ||
| .query_row( | ||
| "SELECT value FROM metadata_table WHERE blockhash = ? AND key = ?", | ||
| params, | ||
| |row| row.get(0), | ||
| ) | ||
| .prepare_cached("SELECT value FROM metadata_table WHERE blockhash = ? AND key = ?") | ||
| .and_then(|mut stmt| stmt.query_row(params, |row| row.get(0))) | ||
| .optional() | ||
| { | ||
| Ok(x) => Ok(x), | ||
|
|
@@ -360,6 +357,10 @@ impl SqliteConnection { | |
| conn.busy_handler(Some(tx_busy_handler)) | ||
| .map_err(|x| VmInternalError::SqliteError(IncomparableError { err: x }))?; | ||
|
|
||
| // rusqlite's default statement cache holds only 16 entries; bump it so the | ||
| // per-read `prepare_cached` statements in this module are never LRU-evicted. | ||
| conn.set_prepared_statement_cache_capacity(32); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: even if used in one place we could define maybe we could even use just one constants (the bigger), but I'm fine keeping them separated due to the working set. |
||
|
|
||
| Ok(conn) | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -697,11 +697,13 @@ where | |
| "block_headers" | ||
| }; | ||
|
|
||
| conn.query_row( | ||
| &format!("SELECT {column_name} FROM {table_name} WHERE index_block_hash = ?",), | ||
| args, | ||
| |x| Ok(loader(x)), | ||
| ) | ||
| // `column_name`/`table_name` come from small fixed sets (7 as of this | ||
| // writing) times the two header tables, far below the connection's | ||
| // 200-entry statement-cache capacity. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: this comment looks like can easily drift if we change the cache configuration |
||
| conn.prepare_cached(&format!( | ||
| "SELECT {column_name} FROM {table_name} WHERE index_block_hash = ?" | ||
| )) | ||
| .and_then(|mut stmt| stmt.query_row(args, |x| Ok(loader(x)))) | ||
| .optional() | ||
| .unwrap_or_else(|_| { | ||
| panic!( | ||
|
|
@@ -822,14 +824,15 @@ fn get_matured_reward<GTS: GetTenureStartId>( | |
| }; | ||
| let parent_id_bhh = conn | ||
| .conn() | ||
| .query_row( | ||
| &format!("SELECT parent_block_id FROM {table_name} WHERE index_block_hash = ?"), | ||
| params![child_id_bhh.0], | ||
| |x| { | ||
| .prepare_cached(&format!( | ||
| "SELECT parent_block_id FROM {table_name} WHERE index_block_hash = ?" | ||
| )) | ||
| .and_then(|mut stmt| { | ||
| stmt.query_row(params![child_id_bhh.0], |x| { | ||
| Ok(StacksBlockId::from_column(x, "parent_block_id") | ||
| .expect("Bad parent_block_id in database")) | ||
| }, | ||
| ) | ||
| }) | ||
| }) | ||
| .optional() | ||
| .expect("Unexpected SQL failure querying parent block ID"); | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.