Preferred library for grpc in rust

I am looking for a grpc library in rust that is easy to use. (I understand that easiness is subjective.) I am not worried about the performance aspect of the library right now. Any suggestions?

Have you tried grpc or tonic?

1 Like

Thanks for the suggestion. I have started using Tonic. But I've stumbled upon a problem.

I have an endpoint (delete), in which I am trying to remove an entry from a HashMap which is a field in MyChord. MyChord is the struct that implements the trait that is required to be the GRpc server. But I am not able to get the mutable reference to the hashmap so that I can remove the entry from it. Is there any workaround? Or should I not include the hashmap inside the MyChord, but access it in some other way?

#[derive(Debug, Default)]
pub struct MyChord {
    data_store: std::collections::HashMap<String, String>,
}

async fn delete(&mut self, request: Request<DeleteRequest>) -> Result<Response<DeleteResponse>, Status> {
        println!("Got a request for DELETE: {:?}", request);
        let request = request.into_inner();
        self.data_store.remove(&request.key);
        Ok(Response::new(DeleteResponse {}))
    }

Error:

method delete has an incompatible type for trait
types differ in mutability
note: expected fn pointer fn(&'life0 MyChord, tonic::request::Request<>) -> std::pin::Pin<>
found fn pointer fn(&'life0 mut MyChord, tonic::request::Request<>) -> std::pin::Pin<>
help: consider change the type to match the mutability in trait: &selfrustc(E0053)
server.rs(31, 21): types differ in mutability
node.rs(127, 13): type in trait

You have to use an immutable reference, and you will need some kind of synchronization primitive to encapsulate access to the map. One option is to use a Mutex and lock it when you need to access it. Another option is to use something like evmap or chashmap which work by locking access to smaller parts of the map. Finally you can use message passing to communicate with some task you've spawned.

Be aware that if you choose to use a locking mechanism, you need to choose between the std-version and the one in Tokio. The difference is that the one in Tokio requires an .await on the lock() call meaning the async fn can be suspended while waiting for the lock, whereas the std-lock will block.

Using the std mutex can be okay if you only ever lock the map for a very short time (and never across an .await point). If you sometimes need to lock the map for a longer time, you should be using the Tokio alternative.

1 Like

In general, there is no downside I know of to using tokio synchronization primitives or mpscs in an async application. At the very least, it helps future-proof your code, in case the work inside the lock becomes more intensive down the road.

1 Like

Thanks. I have decided to use chashmap (mainly because I want to reduce complexity and chashmap provides API that is easy to use).
But I am facing a new problem. Sometimes I want my grpc servers to pass the message on to other grpc servers. I am using a grpc client to connect to other servers. I am making this call within my grpc handler of the first server.

match self.grpc_client.delete(self.successor, request.key).await {
        Ok(_) => Ok(Response::new(DeleteResponse {})),
        Err(_) => Err(Status::new(tonic::Code::Unavailable, "failed to pass the message on"))
}

But I am not able to use it as expected because of the following error:

Ok(#[stable(feature = " ", since = " ")] T), Contains the success value
*mut (dyn std::ops::Fn() + 'static) cannot be shared between threads safely
*mut (dyn std::ops::Fn() + 'static) cannot be shared between threads safely
help: within core::fmt::Void, the trait std::marker::Sync is not implemented for *mut (dyn std::ops::Fn() + 'static)
note: required because it appears within the type std::marker::PhantomData<*mut (dyn std::ops::Fn() + 'static)>
note: required because it appears within the type core::fmt::Void
note: required because of the requirements on the impl of std::marker::Send for &core::fmt::Void
note: required because it appears within the type std::fmt::ArgumentV1<'_>
note: required because it appears within the type [std::fmt::ArgumentV1<'_>; 1]
note: required because it appears within the type for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7, 't8, 't9, 't10, 't11, 't12, 't13, 't14, 't15, 't16, 't17, 't18, 't19, 't20> {&'r GRpcClient, NodeID, std::string::String, &'s str, &'t0 str, [&'t1 str; 1], &'t2 [&'t3 str], &'t4 [&'t5 str; 1], NodeID, &'t6 usize, usize, std::string::String, &'t7 std::string::String, &'t8 &'t9 std::string::String, (&'t10 &'t11 std::string::String,), [std::fmt::ArgumentV1<'t12>; 1], &'t13 [std::fmt::ArgumentV1<'t14>], &'t15 [std::fmt::ArgumentV1<'t16>; 1], std::fmt::Arguments<'t17>, std::string::String, impl std::future::Future, impl std::future::Future, (), node_grpc::chord_client::ChordClient<tonic::transport::channel::Channel>, tonic::request::Request<node_grpc::DeleteRequest>, &'t18 mut node_grpc::chord_client::ChordClient<tonic::transport::channel::Channel>, node_grpc::chord_client::ChordClient<tonic::transport::channel::Channel>, tonic::request::Request<node_grpc::DeleteRequest>, impl std::future::Future, impl std::future::Future, ()}
note: required because it appears within the type [static generator@src/main.rs:94:109: 101:6 self:&GRpcClient, node:NodeID, key:std::string::String for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7, 't8, 't9, 't10, 't11, 't12, 't13, 't14, 't15, 't16, 't17, 't18, 't19, 't20> {&'r GRpcClient, NodeID, std::string::String, &'s str, &'t0 str, [&'t1 str; 1], &'t2 [&'t3 str], &'t4 [&'t5 str; 1], NodeID, &'t6 usize, usize, std::string::String, &'t7 std::string::String, &'t8 &'t9 std::string::String, (&'t10 &'t11 std::string::String,), [std::fmt::ArgumentV1<'t12>; 1], &'t13 [std::fmt::ArgumentV1<'t14>], &'t15 [std::fmt::ArgumentV1<'t16>; 1], std::fmt::Arguments<'t17>, std::string::String, impl std::future::Future, impl std::future::Future, (), node_grpc::chord_client::ChordClient<tonic::transport::channel::Channel>, tonic::request::Request<node_grpc::DeleteRequest>, &'t18 mut node_grpc::chord_client::ChordClient<tonic::transport::channel::Channel>, node_grpc::chord_client::ChordClient<tonic::transport::channel::Channel>, tonic::request::Request<node_grpc::DeleteRequest>, impl std::future::Future, impl std::future::Future, ()}]
note: required because it appears within the type std::future::GenFuture<[static generator@src/main.rs:94:109: 101:6 self:&GRpcClient, node:NodeID, key:std::string::String for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7, 't8, 't9, 't10, 't11, 't12, 't13, 't14, 't15, 't16, 't17, 't18, 't19, 't20> {&'r GRpcClient, NodeID, std::string::String, &'s str, &'t0 str, [&'t1 str; 1], &'t2 [&'t3 str], &'t4 [&'t5 str; 1], NodeID, &'t6 usize, usize, std::string::String, &'t7 std::string::String, &'t8 &'t9 std::string::String, (&'t10 &'t11 std::string::String,), [std::fmt::ArgumentV1<'t12>; 1], &'t13 [std::fmt::ArgumentV1<'t14>], &'t15 [std::fmt::ArgumentV1<'t16>; 1], std::fmt::Arguments<'t17>, std::string::String, impl std::future::Future, impl std::future::Future, (), node_grpc::chord_client::ChordClient<tonic::transport::channel::Channel>, tonic::request::Request<node_grpc::DeleteRequest>, &'t18 mut node_grpc::chord_client::ChordClient<tonic::transport::channel::Channel>, node_grpc::chord_client::ChordClient<tonic::transport::channel::Channel>, tonic::request::Request<node_grpc::DeleteRequest>, impl std::future::Future, impl std::future::Future, ()}]>
note: required because it appears within the type impl std::future::Future
note: required because it appears within the type impl std::future::Future
note: required because it appears within the type for<'r, 's, 't0, 't1, 't2, 't3> {&'r Node, tonic::request::Request<node_grpc::DeleteRequest>, node_grpc::DeleteRequest, std::string::String, Node, &'s Node, &'t0 GRpcClient, GRpcClient, Node, &'t1 Node, NodeID, node_grpc::DeleteRequest, std::string::String, impl std::future::Future, impl std::future::Future, ()}
note: required because it appears within the type [static generator@src/main.rs:163:105: 175:6 _self:&Node, request:tonic::request::Request<node_grpc::DeleteRequest> for<'r, 's, 't0, 't1, 't2, 't3> {&'r Node, tonic::request::Request<node_grpc::DeleteRequest>, node_grpc::DeleteRequest, std::string::String, Node, &'s Node, &'t0 GRpcClient, GRpcClient, Node, &'t1 Node, NodeID, node_grpc::DeleteRequest, std::string::String, impl std::future::Future, impl std::future::Future, ()}]
note: required because it appears within the type std::future::GenFuture<[static generator@src/main.rs:163:105: 175:6 _self:&Node, request:tonic::request::Request<node_grpc::DeleteRequest> for<'r, 's, 't0, 't1, 't2, 't3> {&'r Node, tonic::request::Request<node_grpc::DeleteRequest>, node_grpc::DeleteRequest, std::string::String, Node, &'s Node, &'t0 GRpcClient, GRpcClient, Node, &'t1 Node, NodeID, node_grpc::DeleteRequest, std::string::String, impl std::future::Future, impl std::future::Future, ()}]>
note: required because it appears within the type impl std::future::Future
note: required because it appears within the type impl std::future::Future
note: required for the cast to the object type dyn std::future::Future<Output = std::result::Result<tonic::response::Response<node_grpc::DeleteResponse>, tonic::status::Status>> + std::marker::Sendrustc(E0277)
main.rs(163, 105): *mut (dyn std::ops::Fn() + 'static) cannot be shared between threads safely

Struct that represents the grpc server:

pub struct Node {
    pub id: NodeID,
    pub predecessor: NodeID,
    pub successor: NodeID,
    pub grpc_client: GRpcClient,
    pub data_store: CHashMap<String, String>,
}

The signature of the grpcClient's delete method:

async fn delete(&self, node: NodeID, key: String) -> Result<DeleteResponse, Box < dyn std::error::Error>>

The signature of the grpcServer's delete method:
async fn delete(&self, request: Request) -> Result<Response< DeleteResponse >, Status>

Any Suggestions as to how I can pass the messages between grpc servers?

Okay, turns out it was just the RLS and VScode playing shenanigans. When I finally decided to just build the code, all the red squiggly lines disappeared. Now I feel like a fool for having wasted almost 3 hrs on googling and trying to fix a bug that didn't exist in the first place. And I have lost my faith in RLS now. :frowning:

I am not even making this up:
Screenshot from 2020-02-06 10-29-59

1 Like

Obligatory plug for rust-analyzer - I've run into a lot less weird issues since I switched to using that instead of RLS.

Only downside is that it's not on the VS Marketplace yet - you either have to build it from source, or use their weekly binary releases. I think it's going to get published to the marketplace in the not too distant future, though.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.