Logo-amall

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(
            &amp;self,
            _request: Request,
        ) -&gt; Result, Status&gt; {
            // ...
            let collections_response =  self.qdrant_client.list_collections().await; // &lt;----
            // ...
            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
      --&gt; src/service.rs:55:60
       |
    55 |       ) -&gt; Result, Status&gt; {
       |  ____________________________________________________________^
    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(&amp;self) -&gt; Result {
            let channel = self.channel.read().unwrap();
            return if let Some(channel) = &amp;*channel {
                Ok(channel.clone())
            } else {
                drop(channel);
                Ok(self.make_channel().await?)
            };
        }
    

    refactored:

     async fn get_channel(&amp;self) -&gt; Result {
            if let Some(channelx) = &amp;*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 if channel.write().unwrap() or channel.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(&amp;self) -&gt; Result {
            if let Some(channel) = &amp;*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