Switch to std::sync::OnceLock and fix Surreal::clone (#2370)

This commit is contained in:
Rushmore Mushambi 2023-08-10 08:33:38 +02:00 committed by GitHub
parent 4ae1a0d1a9
commit 61f4580ba8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 113 additions and 79 deletions

1
Cargo.lock generated
View file

@ -24,6 +24,7 @@ name = "actix-example"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"actix-web", "actix-web",
"once_cell",
"serde", "serde",
"surrealdb", "surrealdb",
"thiserror", "thiserror",

View file

@ -3,7 +3,7 @@ name = "surrealdb"
publish = true publish = true
edition = "2021" edition = "2021"
version = "1.0.0-beta.9" version = "1.0.0-beta.9"
rust-version = "1.65.0" rust-version = "1.70.0"
readme = "CARGO.md" readme = "CARGO.md"
authors = ["Tobie Morgan Hitchcock <tobie@surrealdb.com>"] authors = ["Tobie Morgan Hitchcock <tobie@surrealdb.com>"]
description = "A scalable, distributed, collaborative, document-graph database, for the realtime web" description = "A scalable, distributed, collaborative, document-graph database, for the realtime web"

View file

@ -18,7 +18,6 @@ View the [features](https://surrealdb.com/features), the latest [releases](https
- [x] Compiles to WebAssembly - [x] Compiles to WebAssembly
- [x] Supports typed SQL statements - [x] Supports typed SQL statements
- [x] Invalid SQL queries are never sent to the server, the client uses the same parser the server uses - [x] Invalid SQL queries are never sent to the server, the client uses the same parser the server uses
- [x] Static clients, no need for `once_cell` or `lazy_static`
- [x] Clonable connections with auto-reconnect capabilities, no need for a connection pool - [x] Clonable connections with auto-reconnect capabilities, no need for a connection pool
- [x] Range queries - [x] Range queries
- [x] Consistent API across all supported protocols and storage engines - [x] Consistent API across all supported protocols and storage engines

View file

@ -6,6 +6,7 @@ publish = false
[dependencies] [dependencies]
actix-web = { version = "4.3.1", features = ["macros"] } actix-web = { version = "4.3.1", features = ["macros"] }
once_cell = "1.18.0"
serde = { version = "1.0.171", features = ["derive"] } serde = { version = "1.0.171", features = ["derive"] }
surrealdb = { path = "../.." } surrealdb = { path = "../.." }
thiserror = "1.0.43" thiserror = "1.0.43"

View file

@ -2,12 +2,13 @@ mod error;
mod person; mod person;
use actix_web::{App, HttpServer}; use actix_web::{App, HttpServer};
use once_cell::sync::Lazy;
use surrealdb::engine::remote::ws::Client; use surrealdb::engine::remote::ws::Client;
use surrealdb::engine::remote::ws::Ws; use surrealdb::engine::remote::ws::Ws;
use surrealdb::opt::auth::Root; use surrealdb::opt::auth::Root;
use surrealdb::Surreal; use surrealdb::Surreal;
static DB: Surreal<Client> = Surreal::init(); static DB: Lazy<Surreal<Client>> = Lazy::new(|| Surreal::init());
#[actix_web::main] #[actix_web::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {

View file

@ -1,9 +1,10 @@
use once_cell::sync::Lazy;
use surrealdb::engine::remote::ws::Client; use surrealdb::engine::remote::ws::Client;
use surrealdb::engine::remote::ws::Ws; use surrealdb::engine::remote::ws::Ws;
use surrealdb::Surreal; use surrealdb::Surreal;
use tokio::sync::mpsc; use tokio::sync::mpsc;
static DB: Surreal<Client> = Surreal::init(); static DB: Lazy<Surreal<Client>> = Lazy::new(|| Surreal::init());
const NUM: usize = 100_000; const NUM: usize = 100_000;

View file

@ -125,6 +125,8 @@ use crate::api::Result;
use crate::api::Surreal; use crate::api::Surreal;
use crate::iam::Level; use crate::iam::Level;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::OnceLock;
use url::Url; use url::Url;
/// A trait for converting inputs to a server address object /// A trait for converting inputs to a server address object
@ -583,10 +585,11 @@ impl Surreal<Any> {
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// use once_cell::sync::Lazy;
/// use surrealdb::Surreal; /// use surrealdb::Surreal;
/// use surrealdb::engine::any::Any; /// use surrealdb::engine::any::Any;
/// ///
/// static DB: Surreal<Any> = Surreal::init(); /// static DB: Lazy<Surreal<Any>> = Lazy::new(|| Surreal::init());
/// ///
/// # #[tokio::main] /// # #[tokio::main]
/// # async fn main() -> surrealdb::Result<()> { /// # async fn main() -> surrealdb::Result<()> {
@ -596,7 +599,7 @@ impl Surreal<Any> {
/// ``` /// ```
pub fn connect(&self, address: impl IntoEndpoint) -> Connect<Any, ()> { pub fn connect(&self, address: impl IntoEndpoint) -> Connect<Any, ()> {
Connect { Connect {
router: Some(&self.router), router: self.router.clone(),
address: address.into_endpoint(), address: address.into_endpoint(),
capacity: 0, capacity: 0,
client: PhantomData, client: PhantomData,
@ -643,9 +646,9 @@ impl Surreal<Any> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn connect(address: impl IntoEndpoint) -> Connect<'static, Any, Surreal<Any>> { pub fn connect(address: impl IntoEndpoint) -> Connect<Any, Surreal<Any>> {
Connect { Connect {
router: None, router: Arc::new(OnceLock::new()),
address: address.into_endpoint(), address: address.into_endpoint(),
capacity: 0, capacity: 0,
client: PhantomData, client: PhantomData,

View file

@ -16,12 +16,12 @@ use crate::api::opt::Tls;
use crate::api::DbResponse; use crate::api::DbResponse;
#[allow(unused_imports)] // used by the DB engines #[allow(unused_imports)] // used by the DB engines
use crate::api::ExtraFeatures; use crate::api::ExtraFeatures;
use crate::api::OnceLockExt;
use crate::api::Result; use crate::api::Result;
use crate::api::Surreal; use crate::api::Surreal;
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::error::Db as DbError; use crate::error::Db as DbError;
use flume::Receiver; use flume::Receiver;
use once_cell::sync::OnceCell;
#[cfg(feature = "protocol-http")] #[cfg(feature = "protocol-http")]
use reqwest::ClientBuilder; use reqwest::ClientBuilder;
use std::collections::HashSet; use std::collections::HashSet;
@ -30,6 +30,7 @@ use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::sync::atomic::AtomicI64; use std::sync::atomic::AtomicI64;
use std::sync::Arc; use std::sync::Arc;
use std::sync::OnceLock;
#[cfg(feature = "protocol-ws")] #[cfg(feature = "protocol-ws")]
use tokio_tungstenite::tungstenite::protocol::WebSocketConfig; use tokio_tungstenite::tungstenite::protocol::WebSocketConfig;
#[cfg(feature = "protocol-ws")] #[cfg(feature = "protocol-ws")]
@ -211,7 +212,7 @@ impl Connection for Any {
} }
Ok(Surreal { Ok(Surreal {
router: OnceCell::with_value(Arc::new(Router { router: Arc::new(OnceLock::with_value(Router {
features, features,
conn: PhantomData, conn: PhantomData,
sender: route_tx, sender: route_tx,

View file

@ -9,17 +9,18 @@ use crate::api::engine::any::Any;
use crate::api::err::Error; use crate::api::err::Error;
use crate::api::opt::Endpoint; use crate::api::opt::Endpoint;
use crate::api::DbResponse; use crate::api::DbResponse;
use crate::api::OnceLockExt;
use crate::api::Result; use crate::api::Result;
use crate::api::Surreal; use crate::api::Surreal;
use crate::error::Db as DbError; use crate::error::Db as DbError;
use flume::Receiver; use flume::Receiver;
use once_cell::sync::OnceCell;
use std::collections::HashSet; use std::collections::HashSet;
use std::future::Future; use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::sync::atomic::AtomicI64; use std::sync::atomic::AtomicI64;
use std::sync::Arc; use std::sync::Arc;
use std::sync::OnceLock;
impl crate::api::Connection for Any {} impl crate::api::Connection for Any {}
@ -161,7 +162,7 @@ impl Connection for Any {
} }
Ok(Surreal { Ok(Surreal {
router: OnceCell::with_value(Arc::new(Router { router: Arc::new(OnceLock::with_value(Router {
features, features,
conn: PhantomData, conn: PhantomData,
sender: route_tx, sender: route_tx,

View file

@ -77,11 +77,12 @@ use tokio::io::AsyncWriteExt;
/// Instantiating a global instance /// Instantiating a global instance
/// ///
/// ``` /// ```
/// use once_cell::sync::Lazy;
/// use surrealdb::{Result, Surreal}; /// use surrealdb::{Result, Surreal};
/// use surrealdb::engine::local::Db; /// use surrealdb::engine::local::Db;
/// use surrealdb::engine::local::Mem; /// use surrealdb::engine::local::Mem;
/// ///
/// static DB: Surreal<Db> = Surreal::init(); /// static DB: Lazy<Surreal<Db>> = Lazy::new(|| Surreal::init());
/// ///
/// #[tokio::main] /// #[tokio::main]
/// async fn main() -> Result<()> { /// async fn main() -> Result<()> {
@ -343,7 +344,7 @@ impl Surreal<Db> {
/// Connects to a specific database endpoint, saving the connection on the static client /// Connects to a specific database endpoint, saving the connection on the static client
pub fn connect<P>(&self, address: impl IntoEndpoint<P, Client = Db>) -> Connect<Db, ()> { pub fn connect<P>(&self, address: impl IntoEndpoint<P, Client = Db>) -> Connect<Db, ()> {
Connect { Connect {
router: Some(&self.router), router: self.router.clone(),
address: address.into_endpoint(), address: address.into_endpoint(),
capacity: 0, capacity: 0,
client: PhantomData, client: PhantomData,

View file

@ -8,6 +8,7 @@ use crate::api::engine::local::Db;
use crate::api::err::Error; use crate::api::err::Error;
use crate::api::opt::Endpoint; use crate::api::opt::Endpoint;
use crate::api::ExtraFeatures; use crate::api::ExtraFeatures;
use crate::api::OnceLockExt;
use crate::api::Result; use crate::api::Result;
use crate::api::Surreal; use crate::api::Surreal;
use crate::dbs::Session; use crate::dbs::Session;
@ -17,7 +18,6 @@ use crate::opt::auth::Root;
use flume::Receiver; use flume::Receiver;
use flume::Sender; use flume::Sender;
use futures::StreamExt; use futures::StreamExt;
use once_cell::sync::OnceCell;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::future::Future; use std::future::Future;
@ -25,6 +25,7 @@ use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::sync::atomic::AtomicI64; use std::sync::atomic::AtomicI64;
use std::sync::Arc; use std::sync::Arc;
use std::sync::OnceLock;
impl crate::api::Connection for Db {} impl crate::api::Connection for Db {}
@ -55,7 +56,7 @@ impl Connection for Db {
features.insert(ExtraFeatures::Backup); features.insert(ExtraFeatures::Backup);
Ok(Surreal { Ok(Surreal {
router: OnceCell::with_value(Arc::new(Router { router: Arc::new(OnceLock::with_value(Router {
features, features,
conn: PhantomData, conn: PhantomData,
sender: route_tx, sender: route_tx,

View file

@ -6,6 +6,7 @@ use crate::api::conn::Route;
use crate::api::conn::Router; use crate::api::conn::Router;
use crate::api::engine::local::Db; use crate::api::engine::local::Db;
use crate::api::opt::Endpoint; use crate::api::opt::Endpoint;
use crate::api::OnceLockExt;
use crate::api::Result; use crate::api::Result;
use crate::api::Surreal; use crate::api::Surreal;
use crate::dbs::Session; use crate::dbs::Session;
@ -15,7 +16,6 @@ use crate::opt::auth::Root;
use flume::Receiver; use flume::Receiver;
use flume::Sender; use flume::Sender;
use futures::StreamExt; use futures::StreamExt;
use once_cell::sync::OnceCell;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::future::Future; use std::future::Future;
@ -23,6 +23,7 @@ use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::sync::atomic::AtomicI64; use std::sync::atomic::AtomicI64;
use std::sync::Arc; use std::sync::Arc;
use std::sync::OnceLock;
use wasm_bindgen_futures::spawn_local; use wasm_bindgen_futures::spawn_local;
impl crate::api::Connection for Db {} impl crate::api::Connection for Db {}
@ -51,7 +52,7 @@ impl Connection for Db {
conn_rx.into_recv_async().await??; conn_rx.into_recv_async().await??;
Ok(Surreal { Ok(Surreal {
router: OnceCell::with_value(Arc::new(Router { router: Arc::new(OnceLock::with_value(Router {
features: HashSet::new(), features: HashSet::new(),
conn: PhantomData, conn: PhantomData,
sender: route_tx, sender: route_tx,

View file

@ -76,11 +76,12 @@ impl Surreal<Client> {
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// use once_cell::sync::Lazy;
/// use surrealdb::Surreal; /// use surrealdb::Surreal;
/// use surrealdb::engine::remote::http::Client; /// use surrealdb::engine::remote::http::Client;
/// use surrealdb::engine::remote::http::Http; /// use surrealdb::engine::remote::http::Http;
/// ///
/// static DB: Surreal<Client> = Surreal::init(); /// static DB: Lazy<Surreal<Client>> = Lazy::new(|| Surreal::init());
/// ///
/// # #[tokio::main] /// # #[tokio::main]
/// # async fn main() -> surrealdb::Result<()> { /// # async fn main() -> surrealdb::Result<()> {
@ -93,7 +94,7 @@ impl Surreal<Client> {
address: impl IntoEndpoint<P, Client = Client>, address: impl IntoEndpoint<P, Client = Client>,
) -> Connect<Client, ()> { ) -> Connect<Client, ()> {
Connect { Connect {
router: Some(&self.router), router: self.router.clone(),
address: address.into_endpoint(), address: address.into_endpoint(),
capacity: 0, capacity: 0,
client: PhantomData, client: PhantomData,

View file

@ -9,12 +9,12 @@ use crate::api::opt::Endpoint;
#[cfg(any(feature = "native-tls", feature = "rustls"))] #[cfg(any(feature = "native-tls", feature = "rustls"))]
use crate::api::opt::Tls; use crate::api::opt::Tls;
use crate::api::ExtraFeatures; use crate::api::ExtraFeatures;
use crate::api::OnceLockExt;
use crate::api::Result; use crate::api::Result;
use crate::api::Surreal; use crate::api::Surreal;
use flume::Receiver; use flume::Receiver;
use futures::StreamExt; use futures::StreamExt;
use indexmap::IndexMap; use indexmap::IndexMap;
use once_cell::sync::OnceCell;
use reqwest::header::HeaderMap; use reqwest::header::HeaderMap;
use reqwest::ClientBuilder; use reqwest::ClientBuilder;
use std::collections::HashSet; use std::collections::HashSet;
@ -23,6 +23,7 @@ use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::sync::atomic::AtomicI64; use std::sync::atomic::AtomicI64;
use std::sync::Arc; use std::sync::Arc;
use std::sync::OnceLock;
use url::Url; use url::Url;
impl crate::api::Connection for Client {} impl crate::api::Connection for Client {}
@ -71,7 +72,7 @@ impl Connection for Client {
features.insert(ExtraFeatures::Backup); features.insert(ExtraFeatures::Backup);
Ok(Surreal { Ok(Surreal {
router: OnceCell::with_value(Arc::new(Router { router: Arc::new(OnceLock::with_value(Router {
features, features,
conn: PhantomData, conn: PhantomData,
sender: route_tx, sender: route_tx,

View file

@ -6,13 +6,13 @@ use crate::api::conn::Param;
use crate::api::conn::Route; use crate::api::conn::Route;
use crate::api::conn::Router; use crate::api::conn::Router;
use crate::api::opt::Endpoint; use crate::api::opt::Endpoint;
use crate::api::OnceLockExt;
use crate::api::Result; use crate::api::Result;
use crate::api::Surreal; use crate::api::Surreal;
use flume::Receiver; use flume::Receiver;
use flume::Sender; use flume::Sender;
use futures::StreamExt; use futures::StreamExt;
use indexmap::IndexMap; use indexmap::IndexMap;
use once_cell::sync::OnceCell;
use reqwest::header::HeaderMap; use reqwest::header::HeaderMap;
use reqwest::ClientBuilder; use reqwest::ClientBuilder;
use std::collections::HashSet; use std::collections::HashSet;
@ -21,6 +21,7 @@ use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::sync::atomic::AtomicI64; use std::sync::atomic::AtomicI64;
use std::sync::Arc; use std::sync::Arc;
use std::sync::OnceLock;
use url::Url; use url::Url;
use wasm_bindgen_futures::spawn_local; use wasm_bindgen_futures::spawn_local;
@ -50,7 +51,7 @@ impl Connection for Client {
conn_rx.into_recv_async().await??; conn_rx.into_recv_async().await??;
Ok(Surreal { Ok(Surreal {
router: OnceCell::with_value(Arc::new(Router { router: Arc::new(OnceLock::with_value(Router {
features: HashSet::new(), features: HashSet::new(),
conn: PhantomData, conn: PhantomData,
sender: route_tx, sender: route_tx,

View file

@ -58,11 +58,12 @@ impl Surreal<Client> {
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// use once_cell::sync::Lazy;
/// use surrealdb::Surreal; /// use surrealdb::Surreal;
/// use surrealdb::engine::remote::ws::Client; /// use surrealdb::engine::remote::ws::Client;
/// use surrealdb::engine::remote::ws::Ws; /// use surrealdb::engine::remote::ws::Ws;
/// ///
/// static DB: Surreal<Client> = Surreal::init(); /// static DB: Lazy<Surreal<Client>> = Lazy::new(|| Surreal::init());
/// ///
/// # #[tokio::main] /// # #[tokio::main]
/// # async fn main() -> surrealdb::Result<()> { /// # async fn main() -> surrealdb::Result<()> {
@ -75,7 +76,7 @@ impl Surreal<Client> {
address: impl IntoEndpoint<P, Client = Client>, address: impl IntoEndpoint<P, Client = Client>,
) -> Connect<Client, ()> { ) -> Connect<Client, ()> {
Connect { Connect {
router: Some(&self.router), router: self.router.clone(),
address: address.into_endpoint(), address: address.into_endpoint(),
capacity: 0, capacity: 0,
client: PhantomData, client: PhantomData,

View file

@ -13,6 +13,7 @@ use crate::api::err::Error;
use crate::api::opt::Endpoint; use crate::api::opt::Endpoint;
#[cfg(any(feature = "native-tls", feature = "rustls"))] #[cfg(any(feature = "native-tls", feature = "rustls"))]
use crate::api::opt::Tls; use crate::api::opt::Tls;
use crate::api::OnceLockExt;
use crate::api::Result; use crate::api::Result;
use crate::api::Surreal; use crate::api::Surreal;
use crate::engine::remote::ws::IntervalStream; use crate::engine::remote::ws::IntervalStream;
@ -25,7 +26,6 @@ use futures::SinkExt;
use futures::StreamExt; use futures::StreamExt;
use futures_concurrency::stream::Merge as _; use futures_concurrency::stream::Merge as _;
use indexmap::IndexMap; use indexmap::IndexMap;
use once_cell::sync::OnceCell;
use serde::Deserialize; use serde::Deserialize;
use std::borrow::BorrowMut; use std::borrow::BorrowMut;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
@ -38,6 +38,7 @@ use std::mem;
use std::pin::Pin; use std::pin::Pin;
use std::sync::atomic::AtomicI64; use std::sync::atomic::AtomicI64;
use std::sync::Arc; use std::sync::Arc;
use std::sync::OnceLock;
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio::time; use tokio::time;
use tokio::time::MissedTickBehavior; use tokio::time::MissedTickBehavior;
@ -129,7 +130,7 @@ impl Connection for Client {
router(url, maybe_connector, capacity, config, socket, route_rx); router(url, maybe_connector, capacity, config, socket, route_rx);
Ok(Surreal { Ok(Surreal {
router: OnceCell::with_value(Arc::new(Router { router: Arc::new(OnceLock::with_value(Router {
features: HashSet::new(), features: HashSet::new(),
conn: PhantomData, conn: PhantomData,
sender: route_tx, sender: route_tx,

View file

@ -11,6 +11,7 @@ use crate::api::engine::remote::ws::PING_INTERVAL;
use crate::api::engine::remote::ws::PING_METHOD; use crate::api::engine::remote::ws::PING_METHOD;
use crate::api::err::Error; use crate::api::err::Error;
use crate::api::opt::Endpoint; use crate::api::opt::Endpoint;
use crate::api::OnceLockExt;
use crate::api::Result; use crate::api::Result;
use crate::api::Surreal; use crate::api::Surreal;
use crate::engine::remote::ws::IntervalStream; use crate::engine::remote::ws::IntervalStream;
@ -23,7 +24,6 @@ use futures::SinkExt;
use futures::StreamExt; use futures::StreamExt;
use futures_concurrency::stream::Merge as _; use futures_concurrency::stream::Merge as _;
use indexmap::IndexMap; use indexmap::IndexMap;
use once_cell::sync::OnceCell;
use pharos::Channel; use pharos::Channel;
use pharos::Observable; use pharos::Observable;
use pharos::ObserveConfig; use pharos::ObserveConfig;
@ -37,6 +37,7 @@ use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::sync::atomic::AtomicI64; use std::sync::atomic::AtomicI64;
use std::sync::Arc; use std::sync::Arc;
use std::sync::OnceLock;
use std::time::Duration; use std::time::Duration;
use trice::Instant; use trice::Instant;
use wasm_bindgen_futures::spawn_local; use wasm_bindgen_futures::spawn_local;
@ -82,7 +83,7 @@ impl Connection for Client {
conn_rx.into_recv_async().await??; conn_rx.into_recv_async().await??;
Ok(Surreal { Ok(Surreal {
router: OnceCell::with_value(Arc::new(Router { router: Arc::new(OnceLock::with_value(Router {
features: HashSet::new(), features: HashSet::new(),
conn: PhantomData, conn: PhantomData,
sender: route_tx, sender: route_tx,

View file

@ -72,15 +72,16 @@ use crate::api::opt::auth::Jwt;
use crate::api::opt::IntoEndpoint; use crate::api::opt::IntoEndpoint;
use crate::api::Connect; use crate::api::Connect;
use crate::api::Connection; use crate::api::Connection;
use crate::api::ExtractRouter; use crate::api::OnceLockExt;
use crate::api::Surreal; use crate::api::Surreal;
use crate::sql::to_value; use crate::sql::to_value;
use crate::sql::Uuid; use crate::sql::Uuid;
use crate::sql::Value; use crate::sql::Value;
use once_cell::sync::OnceCell;
use serde::Serialize; use serde::Serialize;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::path::Path; use std::path::Path;
use std::sync::Arc;
use std::sync::OnceLock;
impl Method { impl Method {
#[allow(dead_code)] // used by `ws` and `http` #[allow(dead_code)] // used by `ws` and `http`
@ -114,18 +115,19 @@ impl<C> Surreal<C>
where where
C: Connection, C: Connection,
{ {
/// Creates a new static instance of the client /// Initialises a new unconnected instance of the client
/// ///
/// The static singleton ensures that a single database instance is available across very large /// This makes it easy to create a static singleton of the client. The static singleton
/// or complicated applications. With the singleton, only one connection to the database is /// ensures that a single database instance is available across very large or complicated
/// instantiated, and the database connection does not have to be shared across components /// applications. With the singleton, only one connection to the database is instantiated,
/// or controllers. /// and the database connection does not have to be shared across components or controllers.
/// ///
/// # Examples /// # Examples
/// ///
/// Using a static, compile-time scheme /// Using a static, compile-time scheme
/// ///
/// ```no_run /// ```no_run
/// use once_cell::sync::Lazy;
/// use serde::{Serialize, Deserialize}; /// use serde::{Serialize, Deserialize};
/// use std::borrow::Cow; /// use std::borrow::Cow;
/// use surrealdb::Surreal; /// use surrealdb::Surreal;
@ -134,7 +136,7 @@ where
/// use surrealdb::engine::remote::ws::Client; /// use surrealdb::engine::remote::ws::Client;
/// ///
/// // Creates a new static instance of the client /// // Creates a new static instance of the client
/// static DB: Surreal<Client> = Surreal::init(); /// static DB: Lazy<Surreal<Client>> = Lazy::new(|| Surreal::init());
/// ///
/// #[derive(Serialize, Deserialize)] /// #[derive(Serialize, Deserialize)]
/// struct Person { /// struct Person {
@ -168,6 +170,7 @@ where
/// Using a dynamic, run-time scheme /// Using a dynamic, run-time scheme
/// ///
/// ```no_run /// ```no_run
/// use once_cell::sync::Lazy;
/// use serde::{Serialize, Deserialize}; /// use serde::{Serialize, Deserialize};
/// use std::borrow::Cow; /// use std::borrow::Cow;
/// use surrealdb::Surreal; /// use surrealdb::Surreal;
@ -175,7 +178,7 @@ where
/// use surrealdb::opt::auth::Root; /// use surrealdb::opt::auth::Root;
/// ///
/// // Creates a new static instance of the client /// // Creates a new static instance of the client
/// static DB: Surreal<Any> = Surreal::init(); /// static DB: Lazy<Surreal<Any>> = Lazy::new(|| Surreal::init());
/// ///
/// #[derive(Serialize, Deserialize)] /// #[derive(Serialize, Deserialize)]
/// struct Person { /// struct Person {
@ -205,9 +208,9 @@ where
/// Ok(()) /// Ok(())
/// } /// }
/// ``` /// ```
pub const fn init() -> Self { pub fn init() -> Self {
Self { Self {
router: OnceCell::new(), router: Arc::new(OnceLock::new()),
} }
} }
@ -230,9 +233,9 @@ where
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn new<P>(address: impl IntoEndpoint<P, Client = C>) -> Connect<'static, C, Self> { pub fn new<P>(address: impl IntoEndpoint<P, Client = C>) -> Connect<C, Self> {
Connect { Connect {
router: None, router: Arc::new(OnceLock::new()),
address: address.into_endpoint(), address: address.into_endpoint(),
capacity: 0, capacity: 0,
client: PhantomData, client: PhantomData,

View file

@ -16,6 +16,7 @@ use crate::api::Response as QueryResponse;
use crate::api::Surreal; use crate::api::Surreal;
use crate::sql::statements::BeginStatement; use crate::sql::statements::BeginStatement;
use crate::sql::statements::CommitStatement; use crate::sql::statements::CommitStatement;
use once_cell::sync::Lazy;
use protocol::Client; use protocol::Client;
use protocol::Test; use protocol::Test;
use semver::Version; use semver::Version;
@ -23,7 +24,7 @@ use std::ops::Bound;
use types::User; use types::User;
use types::USER; use types::USER;
static DB: Surreal<Client> = Surreal::init(); static DB: Lazy<Surreal<Client>> = Lazy::new(|| Surreal::init());
#[tokio::test] #[tokio::test]
async fn api() { async fn api() {

View file

@ -9,17 +9,18 @@ use crate::api::opt::Endpoint;
use crate::api::opt::IntoEndpoint; use crate::api::opt::IntoEndpoint;
use crate::api::Connect; use crate::api::Connect;
use crate::api::ExtraFeatures; use crate::api::ExtraFeatures;
use crate::api::OnceLockExt;
use crate::api::Result; use crate::api::Result;
use crate::api::Surreal; use crate::api::Surreal;
use crate::iam::Level; use crate::iam::Level;
use flume::Receiver; use flume::Receiver;
use once_cell::sync::OnceCell;
use std::collections::HashSet; use std::collections::HashSet;
use std::future::Future; use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::sync::atomic::AtomicI64; use std::sync::atomic::AtomicI64;
use std::sync::Arc; use std::sync::Arc;
use std::sync::OnceLock;
use url::Url; use url::Url;
#[derive(Debug)] #[derive(Debug)]
@ -52,7 +53,7 @@ impl Surreal<Client> {
address: impl IntoEndpoint<P, Client = Client>, address: impl IntoEndpoint<P, Client = Client>,
) -> Connect<Client, ()> { ) -> Connect<Client, ()> {
Connect { Connect {
router: Some(&self.router), router: self.router.clone(),
address: address.into_endpoint(), address: address.into_endpoint(),
capacity: 0, capacity: 0,
client: PhantomData, client: PhantomData,
@ -86,7 +87,7 @@ impl Connection for Client {
}; };
server::mock(route_rx); server::mock(route_rx);
Ok(Surreal { Ok(Surreal {
router: OnceCell::with_value(Arc::new(router)), router: Arc::new(OnceLock::with_value(router)),
}) })
}) })
} }

View file

@ -13,7 +13,6 @@ use crate::api::conn::DbResponse;
use crate::api::conn::Router; use crate::api::conn::Router;
use crate::api::err::Error; use crate::api::err::Error;
use crate::api::opt::Endpoint; use crate::api::opt::Endpoint;
use once_cell::sync::OnceCell;
use semver::BuildMetadata; use semver::BuildMetadata;
use semver::VersionReq; use semver::VersionReq;
use std::fmt::Debug; use std::fmt::Debug;
@ -22,6 +21,7 @@ use std::future::IntoFuture;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::sync::OnceLock;
/// A specialized `Result` type /// A specialized `Result` type
pub type Result<T> = std::result::Result<T, crate::Error>; pub type Result<T> = std::result::Result<T, crate::Error>;
@ -34,15 +34,15 @@ pub trait Connection: conn::Connection {}
/// The future returned when creating a new SurrealDB instance /// The future returned when creating a new SurrealDB instance
#[derive(Debug)] #[derive(Debug)]
#[must_use = "futures do nothing unless you `.await` or poll them"] #[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Connect<'r, C: Connection, Response> { pub struct Connect<C: Connection, Response> {
router: Option<&'r OnceCell<Arc<Router<C>>>>, router: Arc<OnceLock<Router<C>>>,
address: Result<Endpoint>, address: Result<Endpoint>,
capacity: usize, capacity: usize,
client: PhantomData<C>, client: PhantomData<C>,
response_type: PhantomData<Response>, response_type: PhantomData<Response>,
} }
impl<C, R> Connect<'_, C, R> impl<C, R> Connect<C, R>
where where
C: Connection, C: Connection,
{ {
@ -78,12 +78,12 @@ where
} }
} }
impl<'r, Client> IntoFuture for Connect<'r, Client, Surreal<Client>> impl<Client> IntoFuture for Connect<Client, Surreal<Client>>
where where
Client: Connection, Client: Connection,
{ {
type Output = Result<Surreal<Client>>; type Output = Result<Surreal<Client>>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + Sync + 'r>>; type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + Sync>>;
fn into_future(self) -> Self::IntoFuture { fn into_future(self) -> Self::IntoFuture {
Box::pin(async move { Box::pin(async move {
@ -94,33 +94,24 @@ where
} }
} }
impl<'r, Client> IntoFuture for Connect<'r, Client, ()> impl<Client> IntoFuture for Connect<Client, ()>
where where
Client: Connection, Client: Connection,
{ {
type Output = Result<()>; type Output = Result<()>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + Sync + 'r>>; type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + Sync>>;
fn into_future(self) -> Self::IntoFuture { fn into_future(self) -> Self::IntoFuture {
Box::pin(async move { Box::pin(async move {
match self.router { let arc = Client::connect(self.address?, self.capacity).await?.router;
Some(router) => { let cell = Arc::into_inner(arc).expect("new connection to have no references");
let option = let router = cell.into_inner().expect("router to be set");
Client::connect(self.address?, self.capacity).await?.router.into_inner(); if self.router.set(router).is_ok() {
match option {
Some(client) => {
if router.set(client).is_ok() {
let client = Surreal { let client = Surreal {
router: router.clone(), router: self.router,
}; };
client.check_server_version().await?; client.check_server_version().await?;
} }
}
None => unreachable!(),
}
}
None => unreachable!(),
}
Ok(()) Ok(())
}) })
} }
@ -134,7 +125,7 @@ pub(crate) enum ExtraFeatures {
/// A database client instance for embedded or remote databases /// A database client instance for embedded or remote databases
#[derive(Debug)] #[derive(Debug)]
pub struct Surreal<C: Connection> { pub struct Surreal<C: Connection> {
router: OnceCell<Arc<Router<C>>>, router: Arc<OnceLock<Router<C>>>,
} }
impl<C> Surreal<C> impl<C> Surreal<C>
@ -176,14 +167,22 @@ where
} }
} }
trait ExtractRouter<C> trait OnceLockExt<C>
where where
C: Connection, C: Connection,
{ {
fn with_value(value: Router<C>) -> OnceLock<Router<C>> {
let cell = OnceLock::new();
match cell.set(value) {
Ok(()) => cell,
Err(_) => unreachable!("don't have exclusive access to `cell`"),
}
}
fn extract(&self) -> Result<&Router<C>>; fn extract(&self) -> Result<&Router<C>>;
} }
impl<C> ExtractRouter<C> for OnceCell<Arc<Router<C>>> impl<C> OnceLockExt<C> for OnceLock<Router<C>>
where where
C: Connection, C: Connection,
{ {

View file

@ -182,6 +182,19 @@ mod api_integration {
}; };
} }
#[tokio::test]
async fn surreal_clone() {
use surrealdb::engine::any::Any;
let db: Surreal<Db> = Surreal::init();
db.clone().connect::<Mem>(()).await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();
let db: Surreal<Any> = Surreal::init();
db.clone().connect("memory").await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();
}
include!("api/mod.rs"); include!("api/mod.rs");
include!("api/backup.rs"); include!("api/backup.rs");
} }

View file

@ -1,11 +1,11 @@
#[cfg(feature = "has-storage")] #[cfg(feature = "has-storage")]
use crate::net::client_ip::ClientIp; use crate::net::client_ip::ClientIp;
#[cfg(feature = "has-storage")] #[cfg(feature = "has-storage")]
use once_cell::sync::OnceCell; use std::sync::OnceLock;
use std::{net::SocketAddr, path::PathBuf}; use std::{net::SocketAddr, path::PathBuf};
#[cfg(feature = "has-storage")] #[cfg(feature = "has-storage")]
pub static CF: OnceCell<Config> = OnceCell::new(); pub static CF: OnceLock<Config> = OnceLock::new();
use std::time::Duration; use std::time::Duration;

View file

@ -1,12 +1,12 @@
use crate::cli::CF; use crate::cli::CF;
use crate::err::Error; use crate::err::Error;
use clap::Args; use clap::Args;
use once_cell::sync::OnceCell; use std::sync::OnceLock;
use std::time::Duration; use std::time::Duration;
use surrealdb::kvs::Datastore; use surrealdb::kvs::Datastore;
use surrealdb::opt::auth::Root; use surrealdb::opt::auth::Root;
pub static DB: OnceCell<Datastore> = OnceCell::new(); pub static DB: OnceLock<Datastore> = OnceLock::new();
#[derive(Args, Debug)] #[derive(Args, Debug)]
pub struct StartCommandDbsOptions { pub struct StartCommandDbsOptions {