Don't lock database operations to a single thread

This commit is contained in:
Ethan O'Brien
2025-11-30 10:48:16 -06:00
parent b9b344b50c
commit fd47262f52
5 changed files with 80 additions and 98 deletions

View File

@@ -25,8 +25,8 @@ pub struct Live {
pub master_pass: i64, pub master_pass: i64,
} }
fn setup_tables(conn: &SQLite) { fn setup_tables(conn: &rusqlite::Connection) {
conn.lock_and_exec("CREATE TABLE IF NOT EXISTS lives ( conn.execute_batch("CREATE TABLE IF NOT EXISTS lives (
live_id INT NOT NULL PRIMARY KEY, live_id INT NOT NULL PRIMARY KEY,
normal_failed BIGINT NOT NULL, normal_failed BIGINT NOT NULL,
normal_pass BIGINT NOT NULL, normal_pass BIGINT NOT NULL,
@@ -36,11 +36,11 @@ fn setup_tables(conn: &SQLite) {
expert_pass BIGINT NOT NULL, expert_pass BIGINT NOT NULL,
master_failed BIGINT NOT NULL, master_failed BIGINT NOT NULL,
master_pass BIGINT NOT NULL master_pass BIGINT NOT NULL
)", params!()); );
conn.lock_and_exec("CREATE TABLE IF NOT EXISTS scores ( CREATE TABLE IF NOT EXISTS scores (
live_id INT NOT NULL PRIMARY KEY, live_id INT NOT NULL PRIMARY KEY,
score_data TEXT NOT NULL score_data TEXT NOT NULL
)", params!()); );").unwrap();
} }
fn update_live_score(id: i64, uid: i64, score: i64) { fn update_live_score(id: i64, uid: i64, score: i64) {

View File

@@ -11,11 +11,11 @@ lazy_static! {
static ref CACHED_DATA: Mutex<Option<JsonValue>> = Mutex::new(None); static ref CACHED_DATA: Mutex<Option<JsonValue>> = Mutex::new(None);
} }
fn setup_tables(conn: &SQLite) { fn setup_tables(conn: &rusqlite::Connection) {
conn.lock_and_exec("CREATE TABLE IF NOT EXISTS scores ( conn.execute_batch("CREATE TABLE IF NOT EXISTS scores (
event_id INT NOT NULL PRIMARY KEY, event_id INT NOT NULL PRIMARY KEY,
score_data TEXT NOT NULL score_data TEXT NOT NULL
)", params!()); );").unwrap();
} }
pub fn live_completed(event_id: u32, uid: i64, score: i64, star_level: i64) { pub fn live_completed(event_id: u32, uid: i64, score: i64, star_level: i64) {

View File

@@ -22,12 +22,12 @@ lazy_static! {
static ref DATABASE: SQLite = SQLite::new("gree.db", setup_tables); static ref DATABASE: SQLite = SQLite::new("gree.db", setup_tables);
} }
fn setup_tables(conn: &SQLite) { fn setup_tables(conn: &rusqlite::Connection) {
conn.create_store_v2("CREATE TABLE IF NOT EXISTS users ( conn.execute_batch("CREATE TABLE IF NOT EXISTS users (
cert TEXT NOT NULL, cert TEXT NOT NULL,
uuid TEXT NOT NULL, uuid TEXT NOT NULL,
user_id BIGINT NOT NULL PRIMARY KEY user_id BIGINT NOT NULL PRIMARY KEY
)"); );").unwrap();
} }
fn update_cert(uid: i64, cert: &str) { fn update_cert(uid: i64, cert: &str) {

View File

@@ -17,62 +17,63 @@ lazy_static! {
}; };
} }
fn setup_tables(conn: &SQLite) { fn setup_tables(conn: &rusqlite::Connection) {
conn.create_store_v2("CREATE TABLE IF NOT EXISTS tokens ( conn.execute_batch("
user_id BIGINT NOT NULL PRIMARY KEY, CREATE TABLE IF NOT EXISTS tokens (
token TEXT NOT NULL user_id BIGINT NOT NULL PRIMARY KEY,
)"); token TEXT NOT NULL
conn.create_store_v2("CREATE TABLE IF NOT EXISTS migration ( );
token TEXT NOT NULL PRIMARY KEY, CREATE TABLE IF NOT EXISTS migration (
password TEXT NOT NULL token TEXT NOT NULL PRIMARY KEY,
)"); password TEXT NOT NULL
);
conn.create_store_v2("CREATE TABLE IF NOT EXISTS userdata ( CREATE TABLE IF NOT EXISTS userdata (
user_id BIGINT NOT NULL PRIMARY KEY, user_id BIGINT NOT NULL PRIMARY KEY,
userdata TEXT NOT NULL, userdata TEXT NOT NULL,
friend_request_disabled INT NOT NULL friend_request_disabled INT NOT NULL
)"); );
conn.create_store_v2("CREATE TABLE IF NOT EXISTS userhome ( CREATE TABLE IF NOT EXISTS userhome (
user_id BIGINT NOT NULL PRIMARY KEY, user_id BIGINT NOT NULL PRIMARY KEY,
userhome TEXT NOT NULL userhome TEXT NOT NULL
)"); );
conn.create_store_v2("CREATE TABLE IF NOT EXISTS missions ( CREATE TABLE IF NOT EXISTS missions (
user_id BIGINT NOT NULL PRIMARY KEY, user_id BIGINT NOT NULL PRIMARY KEY,
missions TEXT NOT NULL missions TEXT NOT NULL
)"); );
conn.create_store_v2("CREATE TABLE IF NOT EXISTS loginbonus ( CREATE TABLE IF NOT EXISTS loginbonus (
user_id BIGINT NOT NULL PRIMARY KEY, user_id BIGINT NOT NULL PRIMARY KEY,
loginbonus TEXT NOT NULL loginbonus TEXT NOT NULL
)"); );
conn.create_store_v2("CREATE TABLE IF NOT EXISTS sifcards ( CREATE TABLE IF NOT EXISTS sifcards (
user_id BIGINT NOT NULL PRIMARY KEY, user_id BIGINT NOT NULL PRIMARY KEY,
sifcards TEXT NOT NULL sifcards TEXT NOT NULL
)"); );
conn.create_store_v2("CREATE TABLE IF NOT EXISTS friends ( CREATE TABLE IF NOT EXISTS friends (
user_id BIGINT NOT NULL PRIMARY KEY, user_id BIGINT NOT NULL PRIMARY KEY,
friends TEXT NOT NULL friends TEXT NOT NULL
)"); );
conn.create_store_v2("CREATE TABLE IF NOT EXISTS chats ( CREATE TABLE IF NOT EXISTS chats (
user_id BIGINT NOT NULL PRIMARY KEY, user_id BIGINT NOT NULL PRIMARY KEY,
chats TEXT NOT NULL chats TEXT NOT NULL
)"); );
conn.create_store_v2("CREATE TABLE IF NOT EXISTS event ( CREATE TABLE IF NOT EXISTS event (
user_id BIGINT NOT NULL PRIMARY KEY, user_id BIGINT NOT NULL PRIMARY KEY,
event TEXT NOT NULL event TEXT NOT NULL
)"); );
conn.create_store_v2("CREATE TABLE IF NOT EXISTS eventloginbonus ( CREATE TABLE IF NOT EXISTS eventloginbonus (
user_id BIGINT NOT NULL PRIMARY KEY, user_id BIGINT NOT NULL PRIMARY KEY,
eventloginbonus TEXT NOT NULL eventloginbonus TEXT NOT NULL
)"); );
conn.create_store_v2("CREATE TABLE IF NOT EXISTS server_data ( CREATE TABLE IF NOT EXISTS server_data (
user_id BIGINT NOT NULL PRIMARY KEY, user_id BIGINT NOT NULL PRIMARY KEY,
server_data TEXT NOT NULL server_data TEXT NOT NULL
)"); );
conn.create_store_v2("CREATE TABLE IF NOT EXISTS webui ( CREATE TABLE IF NOT EXISTS webui (
user_id BIGINT NOT NULL PRIMARY KEY, user_id BIGINT NOT NULL PRIMARY KEY,
token TEXT NOT NULL, token TEXT NOT NULL,
last_login BIGINT NOT NULL last_login BIGINT NOT NULL
)"); );
").unwrap();
} }
fn acc_exists(uid: i64) -> bool { fn acc_exists(uid: i64) -> bool {

View File

@@ -1,45 +1,29 @@
use rusqlite::{Connection, params, ToSql}; use rusqlite::{Connection, params, ToSql};
use std::sync::Mutex;
use json::{JsonValue, array}; use json::{JsonValue, array};
use crate::router::clear_rate::Live; use crate::router::clear_rate::Live;
macro_rules! lock_onto_mutex {
($mutex:expr) => {{
loop {
match $mutex.lock() {
Ok(value) => {
break value;
}
Err(_) => {
$mutex.clear_poison();
std::thread::sleep(std::time::Duration::from_millis(10));
}
}
}
}};
}
pub struct SQLite { pub struct SQLite {
engine: Mutex<Connection> path: String
} }
impl SQLite { impl SQLite {
pub fn new(path: &str, setup: fn(&SQLite)) -> SQLite { pub fn new(path: &str, setup: fn(&Connection)) -> SQLite {
let conn = Connection::open(crate::get_data_path(path)).unwrap();
conn.execute("PRAGMA foreign_keys = ON;", ()).unwrap();
let instance = SQLite { let instance = SQLite {
engine: Mutex::new(conn) path: crate::get_data_path(path)
}; };
setup(&instance); let conn = Connection::open(&instance.path).unwrap();
conn.busy_timeout(std::time::Duration::from_secs(10)).unwrap();
conn.execute("PRAGMA foreign_keys = ON;", ()).unwrap();
setup(&conn);
instance instance
} }
pub fn lock_and_exec(&self, command: &str, args: &[&dyn ToSql]) { pub fn lock_and_exec(&self, command: &str, args: &[&dyn ToSql]) {
let conn = lock_onto_mutex!(self.engine); let conn = Connection::open(&self.path).unwrap();
conn.execute(command, args).unwrap(); conn.execute(command, args).unwrap();
} }
pub fn lock_and_select(&self, command: &str, args: &[&dyn ToSql]) -> Result<String, rusqlite::Error> { pub fn lock_and_select(&self, command: &str, args: &[&dyn ToSql]) -> Result<String, rusqlite::Error> {
let conn = lock_onto_mutex!(self.engine); let conn = Connection::open(&self.path).unwrap();
let mut stmt = conn.prepare(command)?; let mut stmt = conn.prepare(command)?;
stmt.query_row(args, |row| { stmt.query_row(args, |row| {
match row.get::<usize, i64>(0) { match row.get::<usize, i64>(0) {
@@ -49,7 +33,7 @@ impl SQLite {
}) })
} }
pub fn lock_and_select_all(&self, command: &str, args: &[&dyn ToSql]) -> Result<JsonValue, rusqlite::Error> { pub fn lock_and_select_all(&self, command: &str, args: &[&dyn ToSql]) -> Result<JsonValue, rusqlite::Error> {
let conn = lock_onto_mutex!(self.engine); let conn = Connection::open(&self.path).unwrap();
let mut stmt = conn.prepare(command)?; let mut stmt = conn.prepare(command)?;
let map = stmt.query_map(args, |row| { let map = stmt.query_map(args, |row| {
match row.get::<usize, i64>(0) { match row.get::<usize, i64>(0) {
@@ -62,13 +46,13 @@ impl SQLite {
let res = val?; let res = val?;
match res.clone().parse::<i64>() { match res.clone().parse::<i64>() {
Ok(v) => rv.push(v).unwrap(), Ok(v) => rv.push(v).unwrap(),
Err(_) => rv.push(res).unwrap() Err(_) => rv.push(res).unwrap()
}; };
} }
Ok(rv) Ok(rv)
} }
pub fn get_live_data(&self, id: i64) -> Result<Live, rusqlite::Error> { pub fn get_live_data(&self, id: i64) -> Result<Live, rusqlite::Error> {
let conn = lock_onto_mutex!(self.engine); let conn = Connection::open(&self.path).unwrap();
let mut stmt = conn.prepare("SELECT * FROM lives WHERE live_id=?1")?; let mut stmt = conn.prepare("SELECT * FROM lives WHERE live_id=?1")?;
stmt.query_row(params!(id), |row| { stmt.query_row(params!(id), |row| {
Ok(Live { Ok(Live {
@@ -84,7 +68,4 @@ impl SQLite {
}) })
}) })
} }
pub fn create_store_v2(&self, table: &str) {
self.lock_and_exec(table, params!());
}
} }