Giter VIP home page Giter VIP logo

Comments (14)

fMeow avatar fMeow commented on May 31, 2024

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:

  1. edit the source code of arangors that add Clone trait to Connection, DataBase and Collection.
  2. 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 of Connection or Database.
  3. simply wrap Database object in Arc.
    Then the wrapped object is now clonable and we only create ONE Connection or Database if the resource cost, when connecting to arangoDB server and holding multiple instances of Connection or DataBase, 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.

inzanez avatar inzanez commented on May 31, 2024

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.

arn-the-long-beard avatar arn-the-long-beard commented on May 31, 2024

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.

inzanez avatar inzanez commented on May 31, 2024

@arn-the-long-beard you should find it using cargo search r2d2-arangors

from arangors.

arn-the-long-beard avatar arn-the-long-beard commented on May 31, 2024

Thank you ! I'll come back once I have implemented everything :)

from arangors.

arn-the-long-beard avatar arn-the-long-beard commented on May 31, 2024

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 an Fn closure
--> src/main.rs:59:19
|
56 | let db= Arc::new(conn.db("test_db").unwrap()) ;
| -- captured outer variable
...
59 | .data(db)
| ^^ move occurs because db has type std::sync::Arc<arangors::database::Database<'_>>, which does not implement the Copy 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 that conn is borrowed for 'static

Whats is the thing I am missing there ?
I ll use r2d2-arangors, once I solved this first :)

from arangors.

fMeow avatar fMeow commented on May 31, 2024

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.

fMeow avatar fMeow commented on May 31, 2024

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.

arn-the-long-beard avatar arn-the-long-beard commented on May 31, 2024

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.

arn-the-long-beard avatar arn-the-long-beard commented on May 31, 2024

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 type impl 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 in impl 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.

fMeow avatar fMeow commented on May 31, 2024

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.

arn-the-long-beard avatar arn-the-long-beard commented on May 31, 2024

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.

arn-the-long-beard avatar arn-the-long-beard commented on May 31, 2024

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 :

  1. 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.
  2. 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.
  3. 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 ?

  1. 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 ?

  2. 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 )

  3. 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.

inzanez avatar inzanez commented on May 31, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.