obmarg / graphql-ws-client

A GraphQL over Websockets implementation for Rust
Apache License 2.0
39 stars 15 forks source link

Multiple multi-threaded subscriptions with ability to close all of them #87

Closed rhishikeshj closed 5 months ago

rhishikeshj commented 5 months ago

Hi, I am looking for some help with a specific usage pattern for graphql-ws-client. I couldn't find the solution in the examples or on the interwebs.

I ll try and describe the setup with some psuedo-code.

I have the use-case of subscribing to 5 different queries. From the cynic-multiple-subscriptions example, it is recommended that I re-use the Client struct and call client.subscribe with as many queries as I have. And then loop over the returned Stream

In my case, the job of subscribing and then looping over the potentially never-ending stream is done on separate threads for each query, and the updates are passed through mpsc channels.

fn subscribe_to(graphql_client: Client, query: StreamingOperation) {
  std::thread::spawn(|| {
    let stream = client.subscribe(query);
    while let Some(item) = stream.next().await {
      println!("{:?}", item);
    }
  }
  });
}

I also have a requirement of being able to close all subscriptions when a certain event occurs. For this I looked at the Client::close function. Now the trouble is that since I need to pass in the graphql client value to multiple threads, I decided to wrap that in an Arc<RwLock<Client>> RwLock because the subscribe method needs a mutable self so...

I also changed up the subscribe_to* functions to take an Arc, acquire a write lock before calling subscribe The issue is, when I need to close the client, I need an inner owned value to pass to the close function. So I tried something like Arc::into_inner(arc_rwlock_graphql_client)

But since every subscribe_to clones the Arc, the into_inner will always return None because that's the requirement of the Arc::into_inner fn

So my question is, what is the recommended way to implement such a pattern? Where I have to subscribe to multiple queries from multiple threads and also retain the ability to close all of them at once.

obmarg commented 5 months ago

Hi @rhishikeshj - thanks for the question. There's definitely options for doing this, but rather than explaining them - would it be easier for you if Client::Subscribe took a &self rather than &mut self?

I was a bit unsure about my decision to keep it as &mut self and think it'd be easy to change if it's causing issues.

obmarg commented 5 months ago

Although actually, I suppose that would still give you problems calling close because you need an owned client for that....

obmarg commented 5 months ago

@rhishikeshj I think https://github.com/obmarg/graphql-ws-client/pull/88 should make things much eaiser for you - do you want to take a look and try it out?

rhishikeshj commented 5 months ago

@obmarg Thank you for being so responsive to my question! I will definitely take a look at #88 and come back to you tomorrow morning.

rhishikeshj commented 5 months ago

@obmarg Yup, the changes in #88 work well. Now I can just clone the client and send it to multiple threads and also close the connection when the work is done. Thanks again for making the change quickly! If it helps, I can contribute a multi-threaded multi subscription example to the repo too :)

Looking forward to using this in the next release version.

obmarg commented 5 months ago

Released in v0.8.2.

If it helps, I can contribute a multi-threaded multi subscription example to the repo too :)

If you think it'd be useful then feel free, but no pressure - hopefully now it'll be easy for everyone to figure out.