Use only a single DB

This commit is contained in:
Spxg
2025-05-25 15:37:08 +08:00
parent 5a2bc95e23
commit 4ca69466ad
4 changed files with 55 additions and 73 deletions

View File

@@ -6,8 +6,8 @@ use wasm_bindgen::{JsCast, prelude::Closure};
use web_sys::{Blob, Event, FileReader, HtmlInputElement, Url, UrlSearchParams}; use web_sys::{Blob, Event, FileReader, HtmlInputElement, Url, UrlSearchParams};
use crate::{ use crate::{
DownloadDbOptions, FragileComfirmed, LoadDbOptions, PrepareOptions, SQLightError, FragileComfirmed, LoadDbOptions, PrepareOptions, SQLightError, SQLiteStatementResult,
SQLiteStatementResult, WorkerRequest, WorkerRequest,
app::{ app::{
ImportProgress, ImportProgress,
advanced_options_menu::AdvancedOptionsMenu, advanced_options_menu::AdvancedOptionsMenu,
@@ -89,7 +89,6 @@ pub fn execute(state: Store<GlobalState>) -> Box<dyn Fn() + Send + 'static> {
if let Some(worker) = &*state.worker().read_untracked() { if let Some(worker) = &*state.worker().read_untracked() {
worker.send_task(WorkerRequest::Prepare(PrepareOptions { worker.send_task(WorkerRequest::Prepare(PrepareOptions {
id: String::new(),
sql: if !selected_code.is_empty() && run_selected_code { sql: if !selected_code.is_empty() && run_selected_code {
selected_code selected_code
} else { } else {
@@ -97,7 +96,7 @@ pub fn execute(state: Store<GlobalState>) -> Box<dyn Fn() + Send + 'static> {
}, },
clear_on_prepare: !*state.keep_ctx().read_untracked(), clear_on_prepare: !*state.keep_ctx().read_untracked(),
})); }));
worker.send_task(WorkerRequest::Continue(String::new())); worker.send_task(WorkerRequest::Continue);
} }
}) })
} }
@@ -149,10 +148,7 @@ fn DownloadButton() -> impl IntoView {
let on_click = move |_| { let on_click = move |_| {
if let Some(worker) = &*state.worker().read() { if let Some(worker) = &*state.worker().read() {
worker.send_task(WorkerRequest::DownloadDb(DownloadDbOptions { worker.send_task(WorkerRequest::DownloadDb);
// FIXME: multi db
id: String::new(),
}));
} }
}; };
@@ -204,11 +200,7 @@ fn LoadButton(input_ref: NodeRef<Input>) -> impl IntoView {
let array_buffer = result.unchecked_into::<js_sys::ArrayBuffer>(); let array_buffer = result.unchecked_into::<js_sys::ArrayBuffer>();
let data = js_sys::Uint8Array::new(&array_buffer); let data = js_sys::Uint8Array::new(&array_buffer);
if let Some(worker) = &*state.worker().read() { if let Some(worker) = &*state.worker().read() {
worker.send_task(WorkerRequest::LoadDb(LoadDbOptions { worker.send_task(WorkerRequest::LoadDb(LoadDbOptions { data }));
// FIXME: multi db
id: String::new(),
data,
}));
} }
}) })
as Box<dyn FnMut(_)>)); as Box<dyn FnMut(_)>));

View File

@@ -32,16 +32,14 @@ async fn execute_task(scope: DedicatedWorkerGlobalScope, mut rx: UnboundedReceiv
WorkerRequest::Prepare(options) => { WorkerRequest::Prepare(options) => {
WorkerResponse::Prepare(worker::prepare(options).await) WorkerResponse::Prepare(worker::prepare(options).await)
} }
WorkerRequest::Continue(id) => WorkerResponse::Continue(worker::r#continue(&id)), WorkerRequest::Continue => WorkerResponse::Continue(worker::r#continue().await),
WorkerRequest::StepOver(id) => WorkerResponse::StepOver(worker::step_over(&id)), WorkerRequest::StepOver => WorkerResponse::StepOver(worker::step_over().await),
WorkerRequest::StepIn(id) => WorkerResponse::StepIn(worker::step_in(&id)), WorkerRequest::StepIn => WorkerResponse::StepIn(worker::step_in().await),
WorkerRequest::StepOut(id) => WorkerResponse::StepOut(worker::step_out(&id)), WorkerRequest::StepOut => WorkerResponse::StepOut(worker::step_out().await),
WorkerRequest::LoadDb(options) => { WorkerRequest::LoadDb(options) => {
WorkerResponse::LoadDb(worker::load_db(options).await) WorkerResponse::LoadDb(worker::load_db(options).await)
} }
WorkerRequest::DownloadDb(options) => { WorkerRequest::DownloadDb => WorkerResponse::DownloadDb(worker::download_db().await),
WorkerResponse::DownloadDb(worker::download_db(options).await)
}
}; };
if let Err(err) = scope.post_message(&serde_wasm_bindgen::to_value(&resp).unwrap()) { if let Err(err) = scope.post_message(&serde_wasm_bindgen::to_value(&resp).unwrap()) {
log::error!("Failed to send task to window: {resp:?}, {err:?}"); log::error!("Failed to send task to window: {resp:?}, {err:?}");

View File

@@ -77,7 +77,7 @@ impl SQLightError {
pub enum WorkerError { pub enum WorkerError {
#[error(transparent)] #[error(transparent)]
SQLite(#[from] SQLitendError), SQLite(#[from] SQLitendError),
#[error("Not found database by id")] #[error("Not found database")]
NotFound, NotFound,
#[error("Execute sqlite with invaild state")] #[error("Execute sqlite with invaild state")]
InvaildState, InvaildState,
@@ -95,18 +95,18 @@ pub enum WorkerError {
pub enum WorkerRequest { pub enum WorkerRequest {
Open(OpenOptions), Open(OpenOptions),
Prepare(PrepareOptions), Prepare(PrepareOptions),
Continue(String), Continue,
StepOver(String), StepOver,
StepIn(String), StepIn,
StepOut(String), StepOut,
LoadDb(LoadDbOptions), LoadDb(LoadDbOptions),
DownloadDb(DownloadDbOptions), DownloadDb,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum WorkerResponse { pub enum WorkerResponse {
Ready, Ready,
Open(Result<String>), Open(Result<()>),
Prepare(Result<()>), Prepare(Result<()>),
Continue(Result<Vec<SQLiteStatementResult>>), Continue(Result<Vec<SQLiteStatementResult>>),
StepOver(Result<SQLiteStatementResult>), StepOver(Result<SQLiteStatementResult>),
@@ -131,16 +131,10 @@ pub struct OpenOptions {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct LoadDbOptions { pub struct LoadDbOptions {
pub id: String,
#[serde(with = "serde_wasm_bindgen::preserve")] #[serde(with = "serde_wasm_bindgen::preserve")]
pub data: Uint8Array, pub data: Uint8Array,
} }
#[derive(Debug, Serialize, Deserialize)]
pub struct DownloadDbOptions {
pub id: String,
}
impl OpenOptions { impl OpenOptions {
pub fn uri(&self) -> String { pub fn uri(&self) -> String {
format!( format!(
@@ -153,7 +147,6 @@ impl OpenOptions {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct PrepareOptions { pub struct PrepareOptions {
pub id: String,
pub sql: String, pub sql: String,
pub clear_on_prepare: bool, pub clear_on_prepare: bool,
} }
@@ -255,10 +248,11 @@ pub async fn handle_state(state: Store<GlobalState>, mut rx: UnboundedReceiver<W
match resp { match resp {
WorkerResponse::Ready => unreachable!(), WorkerResponse::Ready => unreachable!(),
WorkerResponse::Open(result) => match result { WorkerResponse::Open(result) => {
Ok(_) => (), if let Err(err) = result {
Err(err) => state.last_error().set(Some(SQLightError::new_worker(err))), state.last_error().set(Some(SQLightError::new_worker(err)));
}, }
}
WorkerResponse::Prepare(result) => { WorkerResponse::Prepare(result) => {
if let Err(err) = result { if let Err(err) = result {
state.last_error().set(Some(SQLightError::new_worker(err))); state.last_error().set(Some(SQLightError::new_worker(err)));

View File

@@ -1,24 +1,23 @@
mod sqlitend; mod sqlitend;
use crate::{ use crate::{
DownloadDbOptions, DownloadDbResponse, LoadDbOptions, OpenOptions, PERSIST_VFS, PrepareOptions, DownloadDbResponse, LoadDbOptions, OpenOptions, PERSIST_VFS, PrepareOptions,
SQLiteStatementResult, WorkerError, SQLiteStatementResult, WorkerError,
}; };
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use parking_lot::Mutex;
use sqlite_wasm_rs::{ use sqlite_wasm_rs::{
export::{OpfsSAHPoolCfgBuilder, OpfsSAHPoolUtil}, export::{OpfsSAHPoolCfgBuilder, OpfsSAHPoolUtil},
mem_vfs::MemVfsUtil, mem_vfs::MemVfsUtil,
utils::{copy_to_uint8_array, copy_to_vec}, utils::{copy_to_uint8_array, copy_to_vec},
}; };
use sqlitend::{SQLiteDb, SQLitePreparedStatement, SQLiteStatements}; use sqlitend::{SQLiteDb, SQLitePreparedStatement, SQLiteStatements};
use std::{collections::HashMap, sync::Arc}; use std::sync::Arc;
use tokio::sync::Mutex;
use tokio::sync::OnceCell; use tokio::sync::OnceCell;
type Result<T> = std::result::Result<T, WorkerError>; type Result<T> = std::result::Result<T, WorkerError>;
static DB_POOL: Lazy<Mutex<HashMap<String, SQLiteWorker>>> = static DB: Lazy<Mutex<Option<SQLiteWorker>>> = Lazy::new(|| Mutex::new(None));
Lazy::new(|| Mutex::new(HashMap::new()));
static FS_UTIL: Lazy<FSUtil> = Lazy::new(|| FSUtil { static FS_UTIL: Lazy<FSUtil> = Lazy::new(|| FSUtil {
mem: MemVfsUtil::new(), mem: MemVfsUtil::new(),
@@ -31,7 +30,6 @@ struct FSUtil {
} }
struct SQLiteWorker { struct SQLiteWorker {
id: String,
db: Option<Arc<SQLiteDb>>, db: Option<Arc<SQLiteDb>>,
open_options: OpenOptions, open_options: OpenOptions,
state: SQLiteState, state: SQLiteState,
@@ -47,11 +45,11 @@ struct PreparedState {
prepared: Option<SQLitePreparedStatement>, prepared: Option<SQLitePreparedStatement>,
} }
fn with_worker<F, T>(id: &str, mut f: F) -> Result<T> async fn with_worker<F, T>(mut f: F) -> Result<T>
where where
F: FnMut(&mut SQLiteWorker) -> Result<T>, F: FnMut(&mut SQLiteWorker) -> Result<T>,
{ {
f(DB_POOL.lock().get_mut(id).ok_or(WorkerError::NotFound)?) f(DB.lock().await.as_mut().ok_or(WorkerError::NotFound)?)
} }
async fn init_opfs_util() -> Result<&'static OpfsSAHPoolUtil> { async fn init_opfs_util() -> Result<&'static OpfsSAHPoolUtil> {
@@ -77,8 +75,8 @@ fn get_opfs_util() -> Result<&'static OpfsSAHPoolUtil> {
FS_UTIL.opfs.get().ok_or(WorkerError::Unexpected) FS_UTIL.opfs.get().ok_or(WorkerError::Unexpected)
} }
pub async fn download_db(options: DownloadDbOptions) -> Result<DownloadDbResponse> { pub async fn download_db() -> Result<DownloadDbResponse> {
with_worker(&options.id, |worker| { with_worker(|worker| {
let filename = &worker.open_options.filename; let filename = &worker.open_options.filename;
let db = if worker.open_options.persist { let db = if worker.open_options.persist {
get_opfs_util()? get_opfs_util()?
@@ -95,12 +93,13 @@ pub async fn download_db(options: DownloadDbOptions) -> Result<DownloadDbRespons
data: copy_to_uint8_array(&db), data: copy_to_uint8_array(&db),
}) })
}) })
.await
} }
pub async fn load_db(options: LoadDbOptions) -> Result<()> { pub async fn load_db(options: LoadDbOptions) -> Result<()> {
let db = copy_to_vec(&options.data); let db = copy_to_vec(&options.data);
with_worker(&options.id, |worker| { with_worker(|worker| {
worker.db.take(); worker.db.take();
let filename = &worker.open_options.filename; let filename = &worker.open_options.filename;
@@ -122,35 +121,29 @@ pub async fn load_db(options: LoadDbOptions) -> Result<()> {
worker.state = SQLiteState::Idie; worker.state = SQLiteState::Idie;
Ok(()) Ok(())
}) })
.await
} }
pub async fn open(options: OpenOptions) -> Result<String> { pub async fn open(options: OpenOptions) -> Result<()> {
if let Some(worker) = DB_POOL.lock().get(&options.filename) { let mut locker = DB.lock().await;
return Ok(worker.id.clone()); locker.take();
}
if options.persist { if options.persist {
let util = init_opfs_util().await?; init_opfs_util().await?;
if util.get_capacity() - util.get_file_count() * 3 < 3 {
util.add_capacity(3)
.await
.map_err(|_| WorkerError::Unexpected)?;
} }
}
// FIXME: multi db support
let id = String::new();
let db = SQLiteDb::open(&options.uri())?; let db = SQLiteDb::open(&options.uri())?;
let worker = SQLiteWorker { let worker = SQLiteWorker {
id: id.clone(),
db: Some(db), db: Some(db),
open_options: options, open_options: options,
state: SQLiteState::Idie, state: SQLiteState::Idie,
}; };
DB_POOL.lock().insert(id.clone(), worker); *locker = Some(worker);
Ok(id) Ok(())
} }
pub async fn prepare(options: PrepareOptions) -> Result<()> { pub async fn prepare(options: PrepareOptions) -> Result<()> {
with_worker(&options.id, |worker| { with_worker(|worker| {
if options.clear_on_prepare { if options.clear_on_prepare {
worker.db.take(); worker.db.take();
@@ -178,10 +171,11 @@ pub async fn prepare(options: PrepareOptions) -> Result<()> {
}); });
Ok(()) Ok(())
}) })
.await
} }
pub fn r#continue(id: &str) -> Result<Vec<SQLiteStatementResult>> { pub async fn r#continue() -> Result<Vec<SQLiteStatementResult>> {
with_worker(id, |worker| { with_worker(|worker| {
let state = std::mem::replace(&mut worker.state, SQLiteState::Idie); let state = std::mem::replace(&mut worker.state, SQLiteState::Idie);
let mut result = match state { let mut result = match state {
SQLiteState::Idie => return Err(WorkerError::InvaildState), SQLiteState::Idie => return Err(WorkerError::InvaildState),
@@ -197,10 +191,11 @@ pub fn r#continue(id: &str) -> Result<Vec<SQLiteStatementResult>> {
result.push(SQLiteStatementResult::Finish); result.push(SQLiteStatementResult::Finish);
Ok(result) Ok(result)
}) })
.await
} }
pub fn step_over(id: &str) -> Result<SQLiteStatementResult> { pub async fn step_over() -> Result<SQLiteStatementResult> {
with_worker(id, |worker| match &mut worker.state { with_worker(|worker| match &mut worker.state {
SQLiteState::Idie => Err(WorkerError::InvaildState), SQLiteState::Idie => Err(WorkerError::InvaildState),
SQLiteState::Prepared(prepared_state) => { SQLiteState::Prepared(prepared_state) => {
if let Some(prepared) = &mut prepared_state.prepared { if let Some(prepared) = &mut prepared_state.prepared {
@@ -218,10 +213,11 @@ pub fn step_over(id: &str) -> Result<SQLiteStatementResult> {
} }
} }
}) })
.await
} }
pub fn step_in(id: &str) -> Result<()> { pub async fn step_in() -> Result<()> {
with_worker(id, |worker| { with_worker(|worker| {
match &mut worker.state { match &mut worker.state {
SQLiteState::Idie => return Err(WorkerError::InvaildState), SQLiteState::Idie => return Err(WorkerError::InvaildState),
SQLiteState::Prepared(prepared_state) => { SQLiteState::Prepared(prepared_state) => {
@@ -237,10 +233,11 @@ pub fn step_in(id: &str) -> Result<()> {
}; };
Ok(()) Ok(())
}) })
.await
} }
pub fn step_out(id: &str) -> Result<SQLiteStatementResult> { pub async fn step_out() -> Result<SQLiteStatementResult> {
with_worker(id, |worker| match &mut worker.state { with_worker(|worker| match &mut worker.state {
SQLiteState::Idie => Err(WorkerError::InvaildState), SQLiteState::Idie => Err(WorkerError::InvaildState),
SQLiteState::Prepared(prepared_state) => { SQLiteState::Prepared(prepared_state) => {
if let Some(prepared) = prepared_state.prepared.take() { if let Some(prepared) = prepared_state.prepared.take() {
@@ -250,4 +247,5 @@ pub fn step_out(id: &str) -> Result<SQLiteStatementResult> {
} }
} }
}) })
.await
} }