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?
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.
In general, there is no downside I know of to using tokio synchronization primitives or mpsc
s 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.
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: withincore::fmt::Void
, the traitstd::marker::Sync
is not implemented for*mut (dyn std::ops::Fn() + 'static)
note: required because it appears within the typestd::marker::PhantomData<*mut (dyn std::ops::Fn() + 'static)>
note: required because it appears within the typecore::fmt::Void
note: required because of the requirements on the impl ofstd::marker::Send
for&core::fmt::Void
note: required because it appears within the typestd::fmt::ArgumentV1<'_>
note: required because it appears within the type[std::fmt::ArgumentV1<'_>; 1]
note: required because it appears within the typefor<'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 typestd::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 typeimpl std::future::Future
note: required because it appears within the typeimpl std::future::Future
note: required because it appears within the typefor<'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 typestd::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 typeimpl std::future::Future
note: required because it appears within the typeimpl std::future::Future
note: required for the cast to the object typedyn std::future::Future<Output = std::result::Result<tonic::response::Response<node_grpc::DeleteResponse>, tonic::status::Status>> + std::marker::Send
rustc(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.
I am not even making this up:
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.
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.