Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only store cheap/rc cloneable fields inside of Graph #136

Merged
merged 1 commit into from
Nov 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Only the latest 5.x version is supported, following the [Neo4j Version support p
let uri = "127.0.0.1:7687";
let user = "neo4j";
let pass = "neo";
let graph = Arc::new(Graph::new(&uri, user, pass).await.unwrap());
let graph = Graph::new(&uri, user, pass).await.unwrap();
for _ in 1..=42 {
let graph = graph.clone();
tokio::spawn(async move {
Expand Down
66 changes: 60 additions & 6 deletions lib/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,80 @@
pub use crate::errors::*;
use crate::errors::{Error, Result};
use std::{ops::Deref, sync::Arc};

const DEFAULT_DATABASE: &str = "neo4j";
const DEFAULT_FETCH_SIZE: usize = 200;
const DEFAULT_MAX_CONNECTIONS: usize = 16;

/// Newtype for the name of the database.
/// Stores the name as an `Arc<str>` to avoid cloning the name around.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Database(Arc<str>);

impl From<&str> for Database {
fn from(s: &str) -> Self {
Database(s.into())
}
}

impl From<String> for Database {
fn from(s: String) -> Self {
Database(s.into())
}
}

impl Default for Database {
fn default() -> Self {
Database(DEFAULT_DATABASE.into())
}
}

impl AsRef<str> for Database {
fn as_ref(&self) -> &str {
&self.0
}
}

impl Deref for Database {
type Target = str;

fn deref(&self) -> &Self::Target {
&self.0
}
}

/// The configuration that is used once a connection is alive.
#[derive(Debug, Clone)]
pub struct LiveConfig {
pub(crate) db: Database,
pub(crate) fetch_size: usize,
}

/// The configuration used to connect to the database, see [`crate::Graph::connect`].
#[derive(Debug, Clone)]
pub struct Config {
pub(crate) uri: String,
pub(crate) user: String,
pub(crate) password: String,
pub(crate) max_connections: usize,
pub(crate) db: String,
pub(crate) db: Database,
pub(crate) fetch_size: usize,
}

impl Config {
pub(crate) fn into_live_config(self) -> LiveConfig {
LiveConfig {
db: self.db,
fetch_size: self.fetch_size,
}
}
}

/// A builder to override default configurations and build the [`Config`].
pub struct ConfigBuilder {
uri: Option<String>,
user: Option<String>,
password: Option<String>,
db: String,
db: Database,
fetch_size: usize,
max_connections: usize,
}
Expand Down Expand Up @@ -52,7 +106,7 @@ impl ConfigBuilder {
/// The name of the database to connect to.
///
/// Defaults to "neo4j" if not set.
pub fn db(mut self, db: impl Into<String>) -> Self {
pub fn db(mut self, db: impl Into<Database>) -> Self {
self.db = db.into();
self
}
Expand Down Expand Up @@ -121,7 +175,7 @@ mod tests {
assert_eq!(config.uri, "127.0.0.1:7687");
assert_eq!(config.user, "some_user");
assert_eq!(config.password, "some_password");
assert_eq!(config.db, "some_db");
assert_eq!(&*config.db, "some_db");
assert_eq!(config.fetch_size, 10);
assert_eq!(config.max_connections, 5);
}
Expand All @@ -137,7 +191,7 @@ mod tests {
assert_eq!(config.uri, "127.0.0.1:7687");
assert_eq!(config.user, "some_user");
assert_eq!(config.password, "some_password");
assert_eq!(config.db, "neo4j");
assert_eq!(&*config.db, "neo4j");
assert_eq!(config.fetch_size, 200);
assert_eq!(config.max_connections, 16);
}
Expand Down
16 changes: 10 additions & 6 deletions lib/src/graph.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use crate::{
config::{Config, ConfigBuilder},
config::{Config, ConfigBuilder, Database, LiveConfig},
errors::Result,
pool::{create_pool, ConnectionPool},
query::Query,
stream::DetachedRowStream,
txn::Txn,
};

/// A neo4j database abstraction
/// A neo4j database abstraction.
/// This type can be cloned and shared across threads, internal resources
/// are reference-counted.
#[derive(Clone)]
pub struct Graph {
config: Config,
config: LiveConfig,
pool: ConnectionPool,
}

Expand All @@ -24,6 +27,7 @@ impl Graph {
/// You can build a config using [`ConfigBuilder::default()`].
pub async fn connect(config: Config) -> Result<Self> {
let pool = create_pool(&config).await?;
let config = config.into_live_config();
Ok(Graph { config, pool })
}

Expand All @@ -45,15 +49,15 @@ impl Graph {
/// All queries that needs to be run/executed within the transaction
/// should be executed using either [`Txn::run`] or [`Txn::execute`]
pub async fn start_txn(&self) -> Result<Txn> {
self.start_txn_on(&self.config.db).await
self.start_txn_on(self.config.db.clone()).await
}

/// Starts a new transaction on the provided database.
/// All queries that needs to be run/executed within the transaction
/// should be executed using either [`Txn::run`] or [`Txn::execute`]
pub async fn start_txn_on(&self, db: &str) -> Result<Txn> {
pub async fn start_txn_on(&self, db: impl Into<Database>) -> Result<Txn> {
let connection = self.pool.get().await?;
Txn::new(db, self.config.fetch_size, connection).await
Txn::new(db.into(), self.config.fetch_size, connection).await
}

/// Runs a query on the configured database using a connection from the connection pool,
Expand Down
4 changes: 2 additions & 2 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
//! let pass = "neo";
//! let id = uuid::Uuid::new_v4().to_string();
//!
//! let graph = std::sync::Arc::new(Graph::new(uri, user, pass).await.unwrap());
//! let graph = Graph::new(uri, user, pass).await.unwrap();
//!
#![doc = include_str!("../include/example.rs")]
//! }
Expand Down Expand Up @@ -435,7 +435,7 @@ mod txn;
mod types;
mod version;

pub use crate::config::{Config, ConfigBuilder};
pub use crate::config::{Config, ConfigBuilder, Database};
pub use crate::errors::*;
pub use crate::graph::{query, Graph};
pub use crate::query::Query;
Expand Down
9 changes: 5 additions & 4 deletions lib/src/txn.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{
config::Database,
errors::{unexpected, Result},
messages::{BoltRequest, BoltResponse},
pool::ManagedConnection,
Expand All @@ -11,21 +12,21 @@ use crate::{
/// When a transation is started, a dedicated connection is resered and moved into the handle which
/// will be released to the connection pool when the [`Txn`] handle is dropped.
pub struct Txn {
db: String,
db: Database,
fetch_size: usize,
connection: ManagedConnection,
}

impl Txn {
pub(crate) async fn new(
db: &str,
db: Database,
fetch_size: usize,
mut connection: ManagedConnection,
) -> Result<Self> {
let begin = BoltRequest::begin(db);
let begin = BoltRequest::begin(&db);
match connection.send_recv(begin).await? {
BoltResponse::Success(_) => Ok(Txn {
db: db.to_owned(),
db,
fetch_size,
connection,
}),
Expand Down
12 changes: 5 additions & 7 deletions lib/tests/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use neo4j_testcontainers::{Neo4j, Neo4jImage};
use neo4rs::{ConfigBuilder, Graph};
use testcontainers::{clients::Cli, Container};

use std::{error::Error, sync::Arc};
use std::error::Error;

#[allow(dead_code)]
#[derive(Default)]
Expand Down Expand Up @@ -39,7 +39,7 @@ impl Neo4jContainerBuilder {
}

pub struct Neo4jContainer {
graph: Arc<Graph>,
graph: Graph,
version: String,
_container: Option<Container<'static, Neo4jImage>>,
}
Expand Down Expand Up @@ -82,7 +82,7 @@ impl Neo4jContainer {
})
}

pub fn graph(&self) -> Arc<Graph> {
pub fn graph(&self) -> Graph {
self.graph.clone()
}

Expand Down Expand Up @@ -156,17 +156,15 @@ impl Neo4jContainer {
TestConnection { uri, auth, version }
}

async fn connect(config: ConfigBuilder, uri: String, auth: &TestAuth) -> Arc<Graph> {
async fn connect(config: ConfigBuilder, uri: String, auth: &TestAuth) -> Graph {
let config = config
.uri(uri)
.user(&auth.user)
.password(&auth.pass)
.build()
.unwrap();

let graph = Graph::connect(config).await.unwrap();

Arc::new(graph)
Graph::connect(config).await.unwrap()
}
}

Expand Down