Hello! I am attempting to use qdrant/rust-client from a tonic-based grpc service's async_trait implementation e.g.: ``` pub struct MyServerStruct { qdrant_client: qdrant_client::client::QdrantClient } #[async_trait] impl thing_api_server::ThingApi for MyServerStruct { async fn list_things( &self, _request: Request, ) -> Result, Status> { // ... let collections_response = self.qdrant_client.list_collections().await; // <---- // ... return Ok(Response::new(ListThingsResponse::default())); } } ``` But I get a compile error hinting that something in the channel_pool is not `Send` ``` error: future cannot be sent between threads safely --> src/service.rs:55:60 | 55 | ) -> Result, Status> { | ____________________________________________________________^ 56 | | 57 | | let collections_response = self.qdrant_client.list_collections().await; 58 | | 59 | | 60 | | return Ok(Response::new(ListThingsResponse::default())); 61 | | } | |_____^ future created by async block is not `Send` ``` Should I expect the qdrant rust-client to work for this case, or should I just use the raw tonic-generated clients instead?
Last active 5 months ago
17 replies
3 views
- PI
Hello! I am attempting to use qdrant/rust-client from a tonic-based grpc service's async_trait implementation e.g.:
pub struct MyServerStruct { qdrant_client: qdrant_client::client::QdrantClient } #[async_trait] impl thing_api_server::ThingApi for MyServerStruct { async fn list_things( &self, _request: Request, ) -> Result, Status> { // ... let collections_response = self.qdrant_client.list_collections().await; // <---- // ... return Ok(Response::new(ListThingsResponse::default())); } }
But I get a compile error hinting that something in the channel_pool is not
Send
error: future cannot be sent between threads safely --> src/service.rs:55:60 | 55 | ) -> Result, Status> { | ____________________________________________________________^ 56 | | 57 | | let collections_response = self.qdrant_client.list_collections().await; 58 | | 59 | | 60 | | return Ok(Response::new(ListThingsResponse::default())); 61 | | } | |_____^ future created by async block is not `Send`
Should I expect the qdrant rust-client to work for this case, or should I just use the raw tonic-generated clients instead?
- AN
hmm, not sure what causes this. Do you have any suggestions on how to fix the client? Raw tonic generated client might be inconvenient to use, cause it is too verbose
- PI
I don't have any specific suggestions yet / don't understand the exact cause myself. I'm going to try tweaking the
get_channel
in channel_pool and see if that unravels the mystery at all. - PI
I gotta run for tonight, but I did get it to compile 🎉 by refactoring the
get_channel
function. Still needs to be run / tested.. but I'll post what I've got for now:original:
async fn get_channel(&self) -> Result { let channel = self.channel.read().unwrap(); return if let Some(channel) = &*channel { Ok(channel.clone()) } else { drop(channel); Ok(self.make_channel().await?) }; }
refactored:
async fn get_channel(&self) -> Result { if let Some(channelx) = &*self.channel.read().unwrap() { return Ok(channelx.clone()); } // need this explicit scope otherwise compile fails. { // Idk if this channel lock needs to be read() or write().. compiles with either let channelG = self.channel.write().unwrap(); drop(channelG); } let channel = self.make_channel().await?; return Ok(channel.clone()); }
- PI
yeah that change seems to work. I don't yet understand what the
drop(channel)
is achieving / why it doesn't seem to matter ifchannel.write().unwrap()
orchannel.read().unwrap()
is dropped. - AN
I though
drop(channel);
do same thing as scope - AN
also what does this block do:
// need this explicit scope otherwise compile fails. { // Idk if this channel lock needs to be read() or write().. compiles with either let channelG = self.channel.write().unwrap(); drop(channelG); }
? - PI
Great question - I don't think it does anything lol
- AN
it looks like it acquire lock and then immediately release it
- PI
I was just trying to refactor the original code without modifying, but it looks like that block might be able to be removed entirely?
- AN
looks like it
- AN
also we don't need to clone
let channel = self.make_channel().await?; return Ok(channel.clone());
here - PI
yes - here is the latest version that seems to work
async fn get_channel(&self) -> Result { if let Some(channel) = &*self.channel.read().unwrap() { return Ok(channel.clone()); } let channel = self.make_channel().await?; return Ok(channel); }
- AN
would you mind to make a PR?
- PI
sure - should be able to do that sometime today
- AN
Thank you!
- PI
Ok I have made the PR https://github.com/qdrant/rust-client/pull/10 Hopefully that's sufficient. I did not apply cargo fmt, as it modifies a lot of unrelated code. Cheers!
Last active 5 months ago
17 replies
3 views