0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-03 17:34:47 -05:00

perf: improve localStorage throughput (#11709)

This PR improves localStorage write throughput by around 150x by caching
the prepared statements for SQLite and adding some DB pragmas.

Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com>
This commit is contained in:
Luca Casonato 2021-08-14 22:39:35 +02:00 committed by GitHub
parent 370c5013c5
commit 4010b84675
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,5 +1,7 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
// NOTE to all: use **cached** prepared statements when interfacing with SQLite.
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::include_js_files; use deno_core::include_js_files;
use deno_core::op_sync; use deno_core::op_sync;
@ -64,11 +66,25 @@ fn get_webstorage(
})?; })?;
std::fs::create_dir_all(&path.0)?; std::fs::create_dir_all(&path.0)?;
let conn = Connection::open(path.0.join("local_storage"))?; let conn = Connection::open(path.0.join("local_storage"))?;
conn.execute( // Enable write-ahead-logging and tweak some other stuff.
"CREATE TABLE IF NOT EXISTS data (key VARCHAR UNIQUE, value VARCHAR)", let initial_pragmas = "
params![], -- enable write-ahead-logging mode
)?; PRAGMA journal_mode=WAL;
PRAGMA synchronous=NORMAL;
PRAGMA temp_store=memory;
PRAGMA page_size=4096;
PRAGMA mmap_size=6000000;
PRAGMA optimize;
";
conn.execute_batch(initial_pragmas)?;
conn.set_prepared_statement_cache_capacity(128);
{
let mut stmt = conn.prepare_cached(
"CREATE TABLE IF NOT EXISTS data (key VARCHAR UNIQUE, value VARCHAR)",
)?;
stmt.execute(params![])?;
}
state.put(LocalStorage(conn)); state.put(LocalStorage(conn));
} }
@ -76,11 +92,12 @@ fn get_webstorage(
} else { } else {
if state.try_borrow::<SessionStorage>().is_none() { if state.try_borrow::<SessionStorage>().is_none() {
let conn = Connection::open_in_memory()?; let conn = Connection::open_in_memory()?;
conn.execute( {
"CREATE TABLE data (key VARCHAR UNIQUE, value VARCHAR)", let mut stmt = conn.prepare_cached(
params![], "CREATE TABLE data (key VARCHAR UNIQUE, value VARCHAR)",
)?; )?;
stmt.execute(params![])?;
}
state.put(SessionStorage(conn)); state.put(SessionStorage(conn));
} }
@ -97,8 +114,7 @@ pub fn op_webstorage_length(
) -> Result<u32, AnyError> { ) -> Result<u32, AnyError> {
let conn = get_webstorage(state, persistent)?; let conn = get_webstorage(state, persistent)?;
let mut stmt = conn.prepare("SELECT COUNT(*) FROM data")?; let mut stmt = conn.prepare_cached("SELECT COUNT(*) FROM data")?;
let length: u32 = stmt.query_row(params![], |row| row.get(0))?; let length: u32 = stmt.query_row(params![], |row| row.get(0))?;
Ok(length) Ok(length)
@ -111,7 +127,8 @@ pub fn op_webstorage_key(
) -> Result<Option<String>, AnyError> { ) -> Result<Option<String>, AnyError> {
let conn = get_webstorage(state, persistent)?; let conn = get_webstorage(state, persistent)?;
let mut stmt = conn.prepare("SELECT key FROM data LIMIT 1 OFFSET ?")?; let mut stmt =
conn.prepare_cached("SELECT key FROM data LIMIT 1 OFFSET ?")?;
let key: Option<String> = stmt let key: Option<String> = stmt
.query_row(params![index], |row| row.get(0)) .query_row(params![index], |row| row.get(0))
@ -134,8 +151,8 @@ pub fn op_webstorage_set(
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let conn = get_webstorage(state, persistent)?; let conn = get_webstorage(state, persistent)?;
let mut stmt = let mut stmt = conn
conn.prepare("SELECT SUM(pgsize) FROM dbstat WHERE name = 'data'")?; .prepare_cached("SELECT SUM(pgsize) FROM dbstat WHERE name = 'data'")?;
let size: u32 = stmt.query_row(params![], |row| row.get(0))?; let size: u32 = stmt.query_row(params![], |row| row.get(0))?;
if size >= MAX_STORAGE_BYTES { if size >= MAX_STORAGE_BYTES {
@ -147,10 +164,9 @@ pub fn op_webstorage_set(
); );
} }
conn.execute( let mut stmt = conn
"INSERT OR REPLACE INTO data (key, value) VALUES (?, ?)", .prepare_cached("INSERT OR REPLACE INTO data (key, value) VALUES (?, ?)")?;
params![args.key_name, args.key_value], stmt.execute(params![args.key_name, args.key_value])?;
)?;
Ok(()) Ok(())
} }
@ -162,8 +178,7 @@ pub fn op_webstorage_get(
) -> Result<Option<String>, AnyError> { ) -> Result<Option<String>, AnyError> {
let conn = get_webstorage(state, persistent)?; let conn = get_webstorage(state, persistent)?;
let mut stmt = conn.prepare("SELECT value FROM data WHERE key = ?")?; let mut stmt = conn.prepare_cached("SELECT value FROM data WHERE key = ?")?;
let val = stmt let val = stmt
.query_row(params![key_name], |row| row.get(0)) .query_row(params![key_name], |row| row.get(0))
.optional()?; .optional()?;
@ -178,7 +193,8 @@ pub fn op_webstorage_remove(
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let conn = get_webstorage(state, persistent)?; let conn = get_webstorage(state, persistent)?;
conn.execute("DELETE FROM data WHERE key = ?", params![key_name])?; let mut stmt = conn.prepare_cached("DELETE FROM data WHERE key = ?")?;
stmt.execute(params![key_name])?;
Ok(()) Ok(())
} }
@ -190,11 +206,8 @@ pub fn op_webstorage_clear(
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let conn = get_webstorage(state, persistent)?; let conn = get_webstorage(state, persistent)?;
conn.execute("DROP TABLE data", params![])?; let mut stmt = conn.prepare_cached("DELETE FROM data")?;
conn.execute( stmt.execute(params![])?;
"CREATE TABLE data (key VARCHAR UNIQUE, value VARCHAR)",
params![],
)?;
Ok(()) Ok(())
} }
@ -206,8 +219,7 @@ pub fn op_webstorage_iterate_keys(
) -> Result<Vec<String>, AnyError> { ) -> Result<Vec<String>, AnyError> {
let conn = get_webstorage(state, persistent)?; let conn = get_webstorage(state, persistent)?;
let mut stmt = conn.prepare("SELECT key FROM data")?; let mut stmt = conn.prepare_cached("SELECT key FROM data")?;
let keys = stmt let keys = stmt
.query_map(params![], |row| row.get::<_, String>(0))? .query_map(params![], |row| row.get::<_, String>(0))?
.map(|r| r.unwrap()) .map(|r| r.unwrap())