#![recursion_limit = "256"]
#[macro_use]
extern crate tracing;
use std::future::Future;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use async_channel::Receiver;
#[doc(inline)]
pub use surrealdb_core::kvs::Datastore;
#[doc(inline)]
pub use surrealdb_core::options::EngineOptions;
use surrealdb_engine_api::SessionId;
use surrealdb_rpc::capabilities::Capabilities;
use surrealdb_types::{Error, Notification};
mod engine;
mod session;
#[doc(inline)]
pub use surrealdb_core::kvs::tasks;
pub use crate::engine::LocalEngine;
use crate::session::SessionRegistry;
#[derive(Debug, Clone)]
pub struct Root {
pub username: String,
pub password: String,
}
#[derive(Debug, Clone, Default)]
pub struct LocalConfig {
pub path: String,
pub root: Option<Root>,
pub query_timeout: Option<Duration>,
pub transaction_timeout: Option<Duration>,
pub capabilities: Capabilities,
pub temporary_directory: Option<PathBuf>,
pub node_membership_refresh_interval: Option<Duration>,
pub node_membership_check_interval: Option<Duration>,
pub node_membership_cleanup_interval: Option<Duration>,
pub changefeed_gc_interval: Option<Duration>,
}
impl LocalConfig {
fn engine_options(&self) -> EngineOptions {
let mut opt = EngineOptions::default();
if let Some(interval) = self.node_membership_refresh_interval {
opt.node_membership_refresh_interval = interval;
}
if let Some(interval) = self.node_membership_check_interval {
opt.node_membership_check_interval = interval;
}
if let Some(interval) = self.node_membership_cleanup_interval {
opt.node_membership_cleanup_interval = interval;
}
if let Some(interval) = self.changefeed_gc_interval {
opt.changefeed_gc_interval = interval;
}
opt
}
}
fn std_error_to_types_error(error: impl std::fmt::Display) -> surrealdb_types::Error {
surrealdb_types::Error::internal(error.to_string())
}
#[cfg(not(target_family = "wasm"))]
fn spawn<F>(future: F)
where
F: Future<Output = ()> + Send + 'static,
{
drop(tokio::spawn(future));
}
#[cfg(target_family = "wasm")]
fn spawn<F>(future: F)
where
F: Future<Output = ()> + 'static,
{
wasm_bindgen_futures::spawn_local(future);
}
pub async fn connect(
config: LocalConfig,
session_rx: Receiver<SessionId>,
) -> Result<Arc<LocalEngine>, Error> {
let builder = Datastore::builder()
.with_engine_options(config.engine_options())
.with_query_timeout(config.query_timeout)
.with_transaction_timeout(config.transaction_timeout)
.with_auth(config.root.is_some());
#[cfg(storage)]
let builder = builder.with_temporary_directory(config.temporary_directory);
let (notifications, builder) = if config.capabilities.allows_live_query_notifications() {
let (send, recv) = async_channel::bounded(surrealdb_cnf::NOTIFICATIONS_CHANNEL_SIZE);
(Some(recv), builder.with_notify(send))
} else {
(None, builder)
};
let builder = builder.with_capabilities(config.capabilities);
let kvs = builder
.without_maintenance_tasks()
.build_with_path(&config.path)
.await
.map_err(std_error_to_types_error)?;
kvs.check_version().await.map_err(std_error_to_types_error)?;
kvs.bootstrap().await.map_err(std_error_to_types_error)?;
kvs.start_maintenance_tasks();
if let Some(root) = &config.root {
kvs.initialise_credentials(&root.username, &root.password)
.await
.map_err(std_error_to_types_error)?;
}
Ok(serve(kvs, notifications, session_rx))
}
pub fn from_datastore(
datastore: Arc<Datastore>,
notifications: Option<Receiver<Notification>>,
session_rx: Receiver<SessionId>,
) -> Arc<LocalEngine> {
serve(datastore, notifications, session_rx)
}
fn serve(
kvs: Arc<Datastore>,
notifications: Option<Receiver<Notification>>,
session_rx: Receiver<SessionId>,
) -> Arc<LocalEngine> {
let sessions = Arc::new(SessionRegistry::default());
if let Some(notifications) = notifications.clone() {
spawn(session::pump(Arc::clone(&kvs), Arc::clone(&sessions), notifications));
}
spawn(session::run(Arc::clone(&kvs), Arc::clone(&sessions), session_rx, notifications));
Arc::new(LocalEngine {
kvs,
sessions,
})
}