server icon indicating copy to clipboard operation
server copied to clipboard

Does Subscriptions Have a Backplane to Support Multiple Servers

Open RehanSaeed opened this issue 5 years ago • 10 comments

I've tried subscriptions and it works in a single server scenario. How can I make it work when you have multiple load balanced servers? Signal-R uses a concept called a backplane which is basically a data store where a list of subscribers are stored.

Ideally there would be a simple interface I can implement to add, get or delete subscriptions. I could then implement the interface and store the subcriptions in Redis for example.

The closest thing I can find is the ISubscriptionManager but that seems to also mix the concepts of storing subscriptions and executing operations.

RehanSaeed avatar Jul 27 '18 13:07 RehanSaeed

Multiple servers would be tricky to implement. Subscriptions itself would work but how would you get the transport to work? Especially when 99% time client is browser connecting trough web socket. Multiple servers where each connection goes to one server should work.

pekkah avatar Aug 01 '18 11:08 pekkah

Not sure about the connection. How does Signal-R do this? Could Subscriptions use some parts of Signal-R to get this done?

RehanSaeed avatar Aug 01 '18 12:08 RehanSaeed

The way to do it is to have a backplane that notifies all the instances, and adds the message to all the subscriptions. In my team we use Azure Service Bus for this, but you can really use anything you want, that is able to receive a message, and then send the message to multiple other services.

Something akin to this: image

BenjaBobs avatar Aug 01 '18 12:08 BenjaBobs

Just wanted to chime in on this. I love GraphQL and we will be using in our software for queries and mutations, but we will be sticking with SignalR for real time events due to elasticity concerns.

JustinKaffenberger avatar Oct 04 '18 13:10 JustinKaffenberger

Apollo GraphQL handles this nicely with Redis as the backplane. Is there no equivalent to that for dotnet? Without something like that, subscriptions have no practical use in production applications.

dflor003 avatar Feb 04 '21 20:02 dflor003

I am looking into this as well at the moment. I think the first step would be to make the subscriptions async, to be non-blocking. I think IObservable is not the correct primitive. Either IAsyncObservable or IAsyncEnumerable would be better.

SebastianStehle avatar Aug 15 '22 12:08 SebastianStehle

You are likely right, @SebastianStehle, that another choice would be better. Unfortunately IAsyncObservable isn't a part of .NET Standard. But IAsyncEnumerable can convey (1) a data event, (2) an exception, and (3) a completion, just like IObservable does. I think the biggest issue is in the design (or tooling) -- it is quite easy with IObservable and System.Reactive to write a singleton service that provides notifications to as many clients as wish to connect without any additional CPU/database load. It may be little more difficult with IAsyncEnumerable. Likely IAsyncObservable is a better choice, if only it was included in .NET Standard.

Just FYI, GraphQL.NET Server v7 does not internally block on any calls when called through its IObserver interface; notifications simply post to a queue to be transmitted to the listener(s) asynchronously. The ordering of notifications is also maintained. So you can write a completely asynchronous IObservable implementation without any blocking taking place. Of course this also means that you cannot know which events have been transmitted to the client or not (probably not guaranteed in any case, however).

So if you wish to write your implementation as IAsyncEnumerable, simply call AsyncEnumerable.ToObservable (part of System.Reactive) to convert it to an IObservable, and you should suffer no ill effects under GraphQL.NET Server v7. Most likely you can do a similar conversion from IAsyncObservable to IObservable.

Link: https://fuqua.io/Rx.NET/ix-docs/html/M_System_Linq_AsyncEnumerable_ToObservable__1.htm

I use IAsyncEnumerable in my GraphQL.NET client to pull results from a subscription, and it works very well.

Shane32 avatar Aug 15 '22 13:08 Shane32

We may want to write connectors for various existing broadcast layer tooling (e.g. Azure Service Bus as noted above) to connect with GraphQL.NET subscription support.

Shane32 avatar Aug 15 '22 14:08 Shane32

Another question is where you make the decision what to push to a subscription.

I would say, in a typical application, only a very small subset of all events are pushed to a subscription. So you probably use domain events or event sourcing and only 1% of these events are actually consumed by a subscriber. So why would you send all these events over the backbone? I think it would be more efficient if you would handle the subscription with these steps:

  1. Publish new subscriptions to all nodes.
  2. Keep a copy of the subscription on all nodes that emit domain events or something similar.
  3. Compare the domain events with subscriptions.
  4. If the current node is also a graphql server try to handle the event locally first or publish to to the backbone otherwise.

The only question is: How do you remove subscription in case of errors? (node restart or so)

SebastianStehle avatar Aug 15 '22 15:08 SebastianStehle

I am working on a small system for my personal needs: https://github.com/SebastianStehle/graphql-ext/tree/main/GraphQLExample/Subscriptions

It is just the abstraction yet, but the idea is to make subscription evaluation on the node where the events are published. The subscription is just an interface, so you can implement custom rules to evaluate who should receive events.

EDIT: I have added an example to test the abstraction.

SebastianStehle avatar Aug 16 '22 07:08 SebastianStehle