Remove tokio as a dependency

Closes SUR-102
This commit is contained in:
Tobie Morgan Hitchcock 2022-05-13 21:51:59 +01:00
parent 6ff2a78c88
commit 7bd5802e99
6 changed files with 190 additions and 158 deletions

View file

@ -7,7 +7,7 @@ authors = ["Tobie Morgan Hitchcock <tobie@surrealdb.com>"]
[features] [features]
default = ["parallel", "kv-tikv", "kv-echodb", "kv-yokudb"] default = ["parallel", "kv-tikv", "kv-echodb", "kv-yokudb"]
parallel = ["tokio"] parallel = []
kv-tikv = ["tikv"] kv-tikv = ["tikv"]
kv-echodb = ["echodb"] kv-echodb = ["echodb"]
kv-indxdb = ["indxdb"] kv-indxdb = ["indxdb"]
@ -47,7 +47,6 @@ sha2 = "0.10.2"
slug = "0.1.4" slug = "0.1.4"
thiserror = "1.0.31" thiserror = "1.0.31"
tikv = { version = "0.1.0", package = "tikv-client", optional = true } tikv = { version = "0.1.0", package = "tikv-client", optional = true }
tokio = { version = "1.18.1", features = ["sync"], optional = true }
url = "2.2.2" url = "2.2.2"
utf-8 = "0.7.6" utf-8 = "0.7.6"
uuid = { version = "1.0.0", features = ["serde", "v4"] } uuid = { version = "1.0.0", features = ["serde", "v4"] }

View file

@ -7,9 +7,6 @@ use storekey::decode::Error as DecodeError;
use storekey::encode::Error as EncodeError; use storekey::encode::Error as EncodeError;
use thiserror::Error; use thiserror::Error;
#[cfg(feature = "parallel")]
use tokio::sync::mpsc::error::SendError as TokioError;
/// An error originating from the SurrealDB client library. /// An error originating from the SurrealDB client library.
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum Error { pub enum Error {
@ -252,15 +249,14 @@ impl From<tikv::Error> for Error {
} }
} }
#[cfg(feature = "parallel")] impl From<channel::RecvError> for Error {
impl From<TokioError<bytes::Bytes>> for Error { fn from(e: channel::RecvError) -> Error {
fn from(e: TokioError<bytes::Bytes>) -> Error {
Error::Channel(e.to_string()) Error::Channel(e.to_string())
} }
} }
impl From<channel::RecvError> for Error { impl From<channel::SendError<bytes::Bytes>> for Error {
fn from(e: channel::RecvError) -> Error { fn from(e: channel::SendError<bytes::Bytes>) -> Error {
Error::Channel(e.to_string()) Error::Channel(e.to_string())
} }
} }

17
lib/src/exe/mod.rs Normal file
View file

@ -0,0 +1,17 @@
use executor::{Executor, Task};
use once_cell::sync::Lazy;
use std::future::Future;
use std::panic::catch_unwind;
pub fn spawn<T: Send + 'static>(future: impl Future<Output = T> + Send + 'static) -> Task<T> {
static GLOBAL: Lazy<Executor<'_>> = Lazy::new(|| {
std::thread::spawn(|| {
catch_unwind(|| {
futures::executor::block_on(GLOBAL.run(futures::future::pending::<()>()))
})
.ok();
});
Executor::new()
});
GLOBAL.spawn(future)
}

View file

@ -12,7 +12,7 @@ use crate::sql;
use crate::sql::query::Query; use crate::sql::query::Query;
use crate::sql::thing::Thing; use crate::sql::thing::Thing;
use bytes::Bytes; use bytes::Bytes;
use tokio::sync::mpsc::Sender; use channel::Receiver;
/// The underlying datastore instance which stores the dataset. /// The underlying datastore instance which stores the dataset.
pub struct Datastore { pub struct Datastore {
@ -199,166 +199,176 @@ impl Datastore {
} }
/// Performs a full database export as SQL /// Performs a full database export as SQL
pub async fn export(&self, ns: String, db: String, chn: Sender<Bytes>) -> Result<(), Error> { pub async fn export(&self, ns: String, db: String) -> Result<Receiver<Bytes>, Error> {
// Start a new transaction // Start a new transaction
let mut txn = self.transaction(false, false).await?; let mut txn = self.transaction(false, false).await?;
// Output OPTIONS // Create a new channel
{ let (chn, rcv) = channel::bounded(10);
chn.send(output!("-- ------------------------------")).await?; // Spawn the export
chn.send(output!("-- OPTION")).await?; crate::exe::spawn(async move {
chn.send(output!("-- ------------------------------")).await?; // Output OPTIONS
chn.send(output!("")).await?; {
chn.send(output!("OPTION IMPORT;")).await?;
chn.send(output!("")).await?;
}
// Output LOGINS
{
let dls = txn.all_dl(&ns, &db).await?;
if !dls.is_empty() {
chn.send(output!("-- ------------------------------")).await?; chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("-- LOGINS")).await?; chn.send(output!("-- OPTION")).await?;
chn.send(output!("-- ------------------------------")).await?; chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("")).await?; chn.send(output!("")).await?;
for dl in dls { chn.send(output!("OPTION IMPORT;")).await?;
chn.send(output!(format!("{};", dl))).await?;
}
chn.send(output!("")).await?; chn.send(output!("")).await?;
} }
} // Output LOGINS
// Output TOKENS {
{ let dls = txn.all_dl(&ns, &db).await?;
let dts = txn.all_dt(&ns, &db).await?; if !dls.is_empty() {
if !dts.is_empty() {
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("-- TOKENS")).await?;
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("")).await?;
for dt in dts {
chn.send(output!(format!("{};", dt))).await?;
}
chn.send(output!("")).await?;
}
}
// Output SCOPES
{
let scs = txn.all_sc(&ns, &db).await?;
if !scs.is_empty() {
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("-- SCOPES")).await?;
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("")).await?;
for sc in scs {
chn.send(output!(format!("{};", sc))).await?;
}
chn.send(output!("")).await?;
}
}
// Output TABLES
{
let tbs = txn.all_tb(&ns, &db).await?;
if !tbs.is_empty() {
for tb in &tbs {
// Output TABLE
chn.send(output!("-- ------------------------------")).await?; chn.send(output!("-- ------------------------------")).await?;
chn.send(output!(format!("-- TABLE: {}", tb.name))).await?; chn.send(output!("-- LOGINS")).await?;
chn.send(output!("-- ------------------------------")).await?; chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("")).await?; chn.send(output!("")).await?;
chn.send(output!(format!("{};", tb))).await?; for dl in dls {
chn.send(output!(format!("{};", dl))).await?;
}
chn.send(output!("")).await?; chn.send(output!("")).await?;
// Output FIELDS }
{ }
let fds = txn.all_fd(&ns, &db, &tb.name).await?; // Output TOKENS
if !fds.is_empty() { {
for fd in &fds { let dts = txn.all_dt(&ns, &db).await?;
chn.send(output!(format!("{};", fd))).await?; if !dts.is_empty() {
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("-- TOKENS")).await?;
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("")).await?;
for dt in dts {
chn.send(output!(format!("{};", dt))).await?;
}
chn.send(output!("")).await?;
}
}
// Output SCOPES
{
let scs = txn.all_sc(&ns, &db).await?;
if !scs.is_empty() {
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("-- SCOPES")).await?;
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("")).await?;
for sc in scs {
chn.send(output!(format!("{};", sc))).await?;
}
chn.send(output!("")).await?;
}
}
// Output TABLES
{
let tbs = txn.all_tb(&ns, &db).await?;
if !tbs.is_empty() {
for tb in &tbs {
// Output TABLE
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!(format!("-- TABLE: {}", tb.name))).await?;
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("")).await?;
chn.send(output!(format!("{};", tb))).await?;
chn.send(output!("")).await?;
// Output FIELDS
{
let fds = txn.all_fd(&ns, &db, &tb.name).await?;
if !fds.is_empty() {
for fd in &fds {
chn.send(output!(format!("{};", fd))).await?;
}
chn.send(output!("")).await?;
}
}
// Output INDEXS
let ixs = txn.all_fd(&ns, &db, &tb.name).await?;
if !ixs.is_empty() {
for ix in &ixs {
chn.send(output!(format!("{};", ix))).await?;
}
chn.send(output!("")).await?;
}
// Output EVENTS
let evs = txn.all_ev(&ns, &db, &tb.name).await?;
if !evs.is_empty() {
for ev in &evs {
chn.send(output!(format!("{};", ev))).await?;
} }
chn.send(output!("")).await?; chn.send(output!("")).await?;
} }
} }
// Output INDEXS // Start transaction
let ixs = txn.all_fd(&ns, &db, &tb.name).await?;
if !ixs.is_empty() {
for ix in &ixs {
chn.send(output!(format!("{};", ix))).await?;
}
chn.send(output!("")).await?;
}
// Output EVENTS
let evs = txn.all_ev(&ns, &db, &tb.name).await?;
if !evs.is_empty() {
for ev in &evs {
chn.send(output!(format!("{};", ev))).await?;
}
chn.send(output!("")).await?;
}
}
// Start transaction
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("-- TRANSACTION")).await?;
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("")).await?;
chn.send(output!("BEGIN TRANSACTION;")).await?;
chn.send(output!("")).await?;
// Output TABLE data
for tb in &tbs {
chn.send(output!("-- ------------------------------")).await?; chn.send(output!("-- ------------------------------")).await?;
chn.send(output!(format!("-- TABLE DATA: {}", tb.name))).await?; chn.send(output!("-- TRANSACTION")).await?;
chn.send(output!("-- ------------------------------")).await?; chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("")).await?; chn.send(output!("")).await?;
// Fetch records chn.send(output!("BEGIN TRANSACTION;")).await?;
let beg = thing::prefix(&ns, &db, &tb.name); chn.send(output!("")).await?;
let end = thing::suffix(&ns, &db, &tb.name); // Output TABLE data
let mut nxt: Option<Vec<u8>> = None; for tb in &tbs {
loop { chn.send(output!("-- ------------------------------")).await?;
let res = match nxt { chn.send(output!(format!("-- TABLE DATA: {}", tb.name))).await?;
None => { chn.send(output!("-- ------------------------------")).await?;
let min = beg.clone(); chn.send(output!("")).await?;
let max = end.clone(); // Fetch records
txn.scan(min..max, 1000).await? let beg = thing::prefix(&ns, &db, &tb.name);
} let end = thing::suffix(&ns, &db, &tb.name);
Some(ref mut beg) => { let mut nxt: Option<Vec<u8>> = None;
beg.push(0x00); loop {
let min = beg.clone(); let res = match nxt {
let max = end.clone(); None => {
txn.scan(min..max, 1000).await? let min = beg.clone();
} let max = end.clone();
}; txn.scan(min..max, 1000).await?
if !res.is_empty() {
// Get total results
let n = res.len();
// Exit when settled
if n == 0 {
break;
}
// Loop over results
for (i, (k, v)) in res.into_iter().enumerate() {
// Ready the next
if n == i + 1 {
nxt = Some(k.clone());
} }
// Parse the key-value Some(ref mut beg) => {
let k: crate::key::thing::Thing = (&k).into(); beg.push(0x00);
let v: crate::sql::value::Value = (&v).into(); let min = beg.clone();
let t = Thing::from((k.tb, k.id)); let max = end.clone();
// Write record txn.scan(min..max, 1000).await?
chn.send(output!(format!("UPDATE {} CONTENT {};", t, v))).await?; }
};
if !res.is_empty() {
// Get total results
let n = res.len();
// Exit when settled
if n == 0 {
break;
}
// Loop over results
for (i, (k, v)) in res.into_iter().enumerate() {
// Ready the next
if n == i + 1 {
nxt = Some(k.clone());
}
// Parse the key-value
let k: crate::key::thing::Thing = (&k).into();
let v: crate::sql::value::Value = (&v).into();
let t = Thing::from((k.tb, k.id));
// Write record
chn.send(output!(format!("UPDATE {} CONTENT {};", t, v)))
.await?;
}
continue;
} }
continue; break;
} }
break; chn.send(output!("")).await?;
} }
// Commit transaction
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("-- TRANSACTION")).await?;
chn.send(output!("-- ------------------------------")).await?;
chn.send(output!("")).await?;
chn.send(output!("COMMIT TRANSACTION;")).await?;
chn.send(output!("")).await?; chn.send(output!("")).await?;
} }
// Commit transaction };
chn.send(output!("-- ------------------------------")).await?; // Everything exported
chn.send(output!("-- TRANSACTION")).await?; Ok::<(), Error>(())
chn.send(output!("-- ------------------------------")).await?; // Task done
chn.send(output!("")).await?; })
chn.send(output!("COMMIT TRANSACTION;")).await?; .detach();
chn.send(output!("")).await?; // Send back the receiver
} Ok(rcv)
}
// Everything fine
Ok(())
} }
} }

View file

@ -19,12 +19,15 @@ mod ctx;
mod dbs; mod dbs;
mod doc; mod doc;
mod err; mod err;
mod exe;
mod fnc; mod fnc;
mod key; mod key;
mod kvs; mod kvs;
// SQL
pub mod sql; pub mod sql;
// Exports
pub use dbs::Auth; pub use dbs::Auth;
pub use dbs::Response; pub use dbs::Response;
pub use dbs::Session; pub use dbs::Session;
@ -33,3 +36,6 @@ pub use kvs::Datastore;
pub use kvs::Key; pub use kvs::Key;
pub use kvs::Transaction; pub use kvs::Transaction;
pub use kvs::Val; pub use kvs::Val;
// Re-exports
pub use channel::Receiver;

View file

@ -25,19 +25,23 @@ async fn handler(session: Session) -> Result<impl warp::Reply, warp::Rejection>
let dbv = session.db.clone().unwrap(); let dbv = session.db.clone().unwrap();
// Create a chunked response // Create a chunked response
let (mut chn, bdy) = Body::channel(); let (mut chn, bdy) = Body::channel();
// Initiate a new async channel
let (snd, mut rcv) = tokio::sync::mpsc::channel(100);
// Spawn a new database export // Spawn a new database export
tokio::spawn(db.export(nsv, dbv, snd)); match db.export(nsv, dbv).await {
// Process all processed values Ok(rcv) => {
tokio::spawn(async move { // Process all processed values
while let Some(v) = rcv.recv().await { tokio::spawn(async move {
let _ = chn.send_data(v).await; while let Ok(v) = rcv.recv().await {
let _ = chn.send_data(v).await;
}
});
// Return the chunked body
Ok(warp::reply::Response::new(bdy))
} }
}); // There was en error with the export
// Return the chunked body _ => Err(warp::reject::custom(Error::InvalidAuth)),
Ok(warp::reply::Response::new(bdy)) }
} }
// There was an error with permissions
_ => Err(warp::reject::custom(Error::InvalidAuth)), _ => Err(warp::reject::custom(Error::InvalidAuth)),
} }
} }