Comments (14)
Thank you for sharing your experience with us.
The Clone
trait is not implemented for neither Connection
nor Database
with historical reason. If you do not care about the reason, you can move on to the next paragraph. At that time, Connection
holds a cached list of Database
. If Clone
are allowed, it can cause synchronization problems. Concretely, say we have two Connection
objects A and B, and we drop a database with A for some reason. And then, we can still access the dropped database in B, because it is cached, and not knowing the database has been dropped. A manual sync from server might help, but I think it better to just create a new object instead of cloning the object.
Due to the orphan rule of rust, we cannot implement any trait
to a struct
if both are from other files or mod
instead of the one we are writing on. That is the reason you got a compiler error when you tried implementing Clone
to Database
in your own code.
The current solution can be:
- edit the source code of
arangors
that addClone
trait toConnection
,DataBase
andCollection
. - forget cloning, and simply create a arangors
Connection
in each thread.
This solution is reasonable that the cost of connection is a one time HTTP request to arangoDB server, in purpose of retrieval of access token, in addition to a ignorable memory cost of holding several instances ofConnection
orDatabase
. - simply wrap
Database
object inArc
.
Then the wrapped object is now clonable and we only create ONEConnection
orDatabase
if the resource cost, when connecting to arangoDB server and holding multiple instances ofConnection
orDataBase
, is really untolerable.let db= Arc::new(conn.db("test_db").unwrap());
I personally recommend the third solution as it's simple and the usage of Arc
is quite common in actix, which means the performance cost of inducing an Arc
is ignorable in almost all cases.
But well, I should consider just adding a Clone
to struct in arangors
, as there is no reason now to avoid Clone
.
Have a nice day.
from arangors.
Another solution could be using r2d2-arangors
. It's an r2d2 implementation for the arangors
driver. That way you could wrap the r2d2 pool in an Arc
and just spawn new connections from that one.
May your beard grow ever longer.
from arangors.
Hey guys !
Wow for your answer @fMeow , Thank you so much to take the time to explain. I am learning a lot right now !
I'll give a try to Arc as you suggest. I will also try Rd2d @inzanez !
Actually my code is directly inspired by the example for authentication service in the Actix-web repo example. I had some R2d2 and I was looking for its implementation for Arangors but I didn't find it.
Thank you so much guys !
Have a nice day !
from arangors.
@arn-the-long-beard you should find it using cargo search r2d2-arangors
from arangors.
Thank you ! I'll come back once I have implemented everything :)
from arangors.
Okay, I got some issues still, héhé :)
When trying Arc, the compiler gives me 2 errors.
Here is the code .
let conn = Connection::establish_jwt("http://localhost:8529", "USER", "BEST PASSWORDEVER").unwrap();
let db= Arc::new(conn.db("test_db").unwrap()) ;
let mut server = HttpServer::new(move|| {
App::new()
.data(db)
.wrap(Logger::default())
.service(
web::scope("/api")
.service(
web::resource("/auth")
.route(web::post().to(auth::login))
),
)
});
Then I got 2 errors. I understand the sens of the second that explains that the value lives too short. How can I make it live longer ? For the first error, it seems because of missing traits.
I do not understand the Fn closure message so.
error[E0507]: cannot move out of
db
, a captured variable in anFn
closure
--> src/main.rs:59:19
|
56 | let db= Arc::new(conn.db("test_db").unwrap()) ;
| -- captured outer variable
...
59 | .data(db)
| ^^ move occurs becausedb
has typestd::sync::Arc<arangors::database::Database<'_>>
, which does not implement theCopy
trait
And
error[E0597]:
conn
does not live long enough
--> src/main.rs:56:23
|
56 | let db= Arc::new(conn.db("test_db").unwrap()) ;
| ^^^^--------------
| |
| borrowed value does not live long enough
| argument requires thatconn
is borrowed for'static
Whats is the thing I am missing there ?
I ll use r2d2-arangors, once I solved this first :)
from arangors.
Wow, it's my first time that I heard of r2d2-arangors
. Thanks for the community efforts. You should certainly tried it since the r2d2
family handles connection pool nicely.
As for the compiler error on life-time, this is deliberately designed that a Database
instance should live no longer than the Connection
instance from which it derived. This is obvious that all access to arango server are dependent on Connection
instance. If it is dropped, then all the derived instances like Database
, Collection
or something about AQL also stop functioning.
So the solution is simple, wrap conn into Arc
instead of db. And create a db instance in need.
let conn = Connection::establish_jwt("http://localhost:8529", "USER", "BEST PASSWORDEVER").unwrap();
let conn = Arc::new(conn);
let mut server = HttpServer::new(move|| {
App::new()
.data(conn)
.wrap(Logger::default())
.service(
web::scope("/api")
.service(
web::resource("/auth")
.route(web::post().to(auth::login))
),
)
});
If you care about the performance, you can wrap a tuple into Arc
and stoed it in App
.
let conn = Connection::establish_jwt("http://localhost:8529", "USER", "BEST PASSWORDEVER").unwrap();
let db= conn.db("test_db").unwrap() ;
let saved = Arc::new((conn, db));
Maybe the following way can bypass the compiler error about lifetime, but I am not sure. If compiles, it's certainly the most elegant way that meets your need.
let db = Connection::establish_jwt("http://localhost:8529", "USER", "BEST PASSWORDEVER").unwrap().db("test_db").unwrap();
let db = Arc::new(db);
from arangors.
The lastest version (v0.3.0) add Clone
to both Connection
and Database
. The actix App should be happy now. Also, you can use implement a custom client with awc, and thus get rid of the all dependencies that reqwest
induces.
from arangors.
Hey ! I was just gonna post :)
I succeed to make it work with r2d2, but it is adding a lot of code that I do not understand.
let manager = ArangoDBConnectionManager::new("http://localhost:8529", "user", "password", true);
let pool = r2d2::Pool::builder()
.build(manager)
.expect("Failed to create pool.");
let mut server = HttpServer::new(move || {
App::new()
.data(pool.clone())
.wrap(Logger::default())
.service(
web::scope("/api")
.service(
web::resource("/auth")
.route(web::post().to(auth::login))
),
)
});
then in my handler
pub async fn login(auth_data: web::Json<AuthData>,
id: Identity, conn: web::Data<DbConnection>) -> Result<HttpResponse, ServiceError> {
let res = web::block(move || query(auth_data.into_inner(), conn)).await;
match res {
Ok(user) => {
let user_string = serde_json::to_string(&user).unwrap();
id.remember(user_string);
Ok(HttpResponse::Ok().finish())
}
Err(err) => match err {
BlockingError::Error(service_error) => Err(service_error),
BlockingError::Canceled => Err(ServiceError::InternalServerError),
},
}
}
and the query
fn query(auth_data: AuthData, conn: web::Data<DbConnection>) -> Result<User, ServiceError> {
let mut vars = HashMap::new();
vars.insert("email", serde_json::value::to_value(auth_data.email).unwrap());
let conn = &conn.get().unwrap();
let db = conn.db("test_db").unwrap();
let res: Result<Vec<FullUser>, failure::Error> = db.aql_bind_vars(r#"FOR u in users FILTER u.data.email == @email return u"#, vars);
// Ok(user) => Ok(user),
// Err(e) => Err(ServiceError::InternalServerError),
// };
if res.is_err() {
eprintln!("DbError: {}", res.unwrap_err());
return Err(ServiceError::InternalServerError);
}
if let Some(user) = res.unwrap().pop() {
if let Ok(matching) = verify(&user.hash, &auth_data.password) {
if matching {
return Ok(user.data.into());
}
}
}
Err(ServiceError::Unauthorized)
}
Question :
I used &conn instead of conn like on the actix ecample. but it works fine with just conn.
What are the benefits of using R2d2 over Arangors alone ?
I had to use diesel and r2d2, and I would like to limit my dependencies to the strict minimum :)
I ll try again with 0.3.0 without r2d2 to see how it does looks like the code.
Thank you a lot for your update 👍
from arangors.
Okay I think I got the anwser. I do not succeed to make the 0.3.0 works. I got this error every I am using the connection.
error[E0599]: no method named
unwrap
found for opaque typeimpl std::future::Future
in the current scope
--> src/handlers/auth.rs:45:17
|
45 | let res= db.unwrap().aql_bind_vars(r#"FOR u in users FILTER u.data.email == @email return u"#, vars);
| ^^^^^^ method not found inimpl std::future::Future
And for some reason, I got a mismatch of type between the compiler and my IDE info. I have some Future in places where I do not have them.
I ll 'stick with r2d2, it actually make stuff easier :) The Future stuff is still not known for me. I need to skip it for now.
Thank you a lot guys for you help, I want to help in both projects. I am not the smartest programmer in the world, but I am good at documentation and giving examples 👍
How can I help you ? :)
from arangors.
Thank you so much. Reaching out is already a valuable effort to help arangors
and r2d2-arangors
to make things clear, something that might be obvious for ones familiar with source code, but confusing for new users.
The v0.3.0 use async
version by default, so either add await
to every async
function. Notice the await
keyword in the following example:
let conn = Connection::establish_jwt("http://localhost:8529", "USER", "BEST PASSWORDEVER").await.unwrap();
let db= conn.db("test_db").await.unwrap();
Refer to documentation when you are not sure whether a function is async
or not. The one at docs.rs might differs a lot from your local compiler version if you tweak arangors
with feature gate, so it's recommended to refer to cargo doc
instead of docs.rs.
Also you can stick with the sync one by specified feature gate in the following way:
[dependencies]
arangors = { version = "0.3", features = ["reqwest_blocking"], default-features = false }
I recommend migrating to async
, as the actix is spawned upon tokio
async runtime, which is really a performance gain for web server. Another advantage is that, you can painlessly migrate to awc
backend, actix web client, to further slim dependencies.
I am planning to implement awc
backend in crates. Before that, you can also implement a client by yourself. See readme.md
and example/custom_client.rs
from arangors.
Hello !
Okay, I ll give a try to the async feature Thank you for the tips and the precious explanations :)
I''ll come back with the example code :)
from arangors.
Hey guys, I was busy with some IRL stuff for the last few days. I am back. I just tried to used r2d2-arangors with the last arangors 0.3.0. It seems we need an update on the r2d2-arangors part because of dependency to 0.2.0.
Here is the working code in pure 0.3.0 without r2d2.
main.rs
let conn = Connection::establish_jwt("http://localhost:8529", "USER", "BEST PASSWORDEVER").unwrap();
let conn = Arc::new(conn);
let mut server = HttpServer::new(move || {
App::new()
.data(conn.clone())
.wrap(Logger::default())
.service(
web::scope("/api")
.service(
web::resource("/auth")
.route(web::post().to(auth::login))
),
)
});
login.rs
pub async fn login(auth_data: web::Json<AuthData>,
id: Identity, conn: web::Data<Arc<GenericConnection<ReqwestClient>>>) -> Result<HttpResponse, ServiceError> {
let query = query(auth_data.into_inner(), conn).await;
let res = web::block(move || query)
.await;
match res {
Ok(user) => {
let user_string = serde_json::to_string(&user).unwrap();
id.remember(user_string);
Ok(HttpResponse::Ok().json(user))
}
Err(err) => match err {
BlockingError::Error(service_error) => Err(service_error),
BlockingError::Canceled => Err(ServiceError::InternalServerError),
},
}
}
//
/// Query for login
async fn query(auth_data: AuthData, conn: web::Data<Arc<GenericConnection<ReqwestClient>>>) -> Result<User, ServiceError> {
let mut vars = HashMap::new();
vars.insert("email", serde_json::value::to_value(auth_data.email).unwrap());
// let conn = &conn.get().unwrap();
let db = conn.db("test_db").await.unwrap();
let res: Result<Vec<FullUser>, ClientError> = db
.aql_bind_vars(r#"FOR u in users FILTER u.data.email == @email return u"#, vars)
.await;
// Ok(user) => Ok(user),
// Err(e) => Err(ServiceError::InternalServerError),
// };
if res.is_err() {
eprintln!("DbError: {}", res.unwrap_err());
return Err(ServiceError::InternalServerError);
}
if let Some(user) = res.unwrap().pop() {
if let Ok(matching) = verify(&user.hash, &auth_data.password) {
if matching {
return Ok(user.data.into());
}
}
}
Err(ServiceError::Unauthorized)
}
Observations :
- I had to put a little more code, and make a new statement for the query part. I have the .await keyword everywhere now. My rust plugin on Clion does not give me info about it.
- I try to have my code very clean when I write js and ts stuff, my rust seems a bit dirty and redundant for now.
- I tried to make my custom client with the actix web client , but I failed due to some typing things between the arangors client and headers used in awc.
New questions :)
1 ) When I try to clone "Connection" without Arc, I get this Error
error[E0599]: no method named `clone` found for struct `arangors::connection::GenericConnection<arangors::client::reqwest::ReqwestClient>` in the current scope
--> src/main.rs:62:24
|
62 | .data(conn.clone())
| ^^^^^ method not found in `arangors::connection::GenericConnection<arangors::client::reqwest::ReqwestClient>`
|
= note: the method `clone` exists but the following trait bounds were not satisfied:
`arangors::connection::GenericConnection<arangors::client::reqwest::ReqwestClient> : std::clone::Clone`
error: aborting due to previous error
What does it mean not statisfied in this context ?
-
When I use the Arc<GenericConnection>, it is read as GenericConnection in the handler. It is very awesome and handy. but how does the compiler translate from Arc to T ?
-
Is the use of r2r2 purpose for r2d2 arangors to increase performance by playing with pool and multi threading ? ( I am not familiar with concurrent stuff, i am Node.js programmer from base and I never needed so much to play with thread stuff before )
-
I wanna help arangors project, where should I start ?
I never have contribute to a project on github before.
I would like to make tiny example code that works in order to make the library more accessible and make it more sexy to use until I get enough skill to write good code.
Thank you a lot guys,
Best regards,
The man with a bear longer than one week ago.
from arangors.
I just added support for async connection pooling using mobc:
https://github.com/inzanez/mobc-arangors
Published crate is called 'mobc-arangors'.
from arangors.
Related Issues (20)
- Add Trait for Database<T> and Transaction<T> HOT 4
- arangors::Connexion problem
- rust web development HOT 1
- User Management HOT 2
- Get rid of uclient dep HOT 2
- Release: new version with transactions, analyzers and views? HOT 2
- Example dependency using the blocking feature in README.md is wrong. HOT 1
- Transaction support improvements
- Error Improvements HOT 1
- Improve error message when credentials for connection are wrong HOT 5
- Make fields on `InsertOption`, `UpdateOption`, etc public HOT 3
- reqwest 0.11 breaks tokio dependencies HOT 4
- Is this project unmaintained? HOT 3
- Make Document and Header derive Clone HOT 2
- Unit test failing on AqlQueryBuilder HOT 5
- rustls features don't work
- arangors 0.5.0 phantom compile errors HOT 11
- Are bind parameters passed on to HTTP API? HOT 3
- save and update views HOT 1
- support pipeline analyzer HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from arangors.