99designs/gqlgen

how do subscriptions work for multiple load balanced websocker servers?

bjm88 opened this issue · 4 comments

bjm88 commented

We are on latest version of gqlgen, really like it for general graphql work. For subscriptions we are prototyping it and found the chat example:
https://github.com/99designs/gqlgen/blob/master/example/chat/server/server.go
but not any other documentation.

Our primary concern/question is how would multiple servers in a real clustered env (aws/ecs/docker/go gorilla websockets for us) know about changes to properly publish to subscripting UI clients ? Does the framework have a hookup point somewhere to send mutations to a message bus that can notify all servers ? We have an existing redis powered message bus for just this use case in our chat application and could happily use it if we know how to hookup things into the gqlgen framework......

Any information along these lines would be really useful, thank you!

Hi, @bjm88

We make use of go-rscsrv-redigo which provide PubSub methods. For example:

func (r *subscriptionResolver) BookAdded(ctx context.Context) (<-chan *types.BookAddedPayload, error) {
	events := make(chan *types.BookAddedPayload, 1)

	go redigorscsrv.DefaultRedigoService.Subscribe(ctx, func() error {
		// subscribed to all channels :)
		return nil
	}, func(channel string, data []byte) error {
		var payload types.BookAddedPayload
		if err := json.Unmarshal(data, &payload); err != nil {
			return err
		}

		events <- &payload
		return nil
	}, "bookAdded")

	return events, nil
}

Then anywhere in our application, we could publish and information into the bookAdded channel.

if err := redigorscsrv.DefaultRedigoService.Publish(ctx, "bookAdded", &types.BookAddedPayload{Book: obj}); err != nil {
	rlog.Criticalf("unable to publish bookAdded payload: %s", err.Error())
}
stale commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

I'm getting undefined: redigosrv.DefaultRedigoService
Any update since then ?

@PunkHaz4rd I think the same thing can be with pub/sub with go-redis/redis

redigo also has pubsub but I haven't had time to checked them out.

Here is an example for Sub:

func (s *subscriptionResolver) NewEmails(ctx context.Context) (<-chan *models.Email, error) {
	channel := make(chan *models.Email, 1)
	go func() {
		sub := redisClient.Subscribe(ctx, "email") // redisClient is just a *redis.Client from redis.NewClient()
		_, err := sub.Receive(ctx)
		if err != nil {
			return
		}
		ch := sub.Channel()
		for {
			select {
			case message := <-ch:
				var email models.Email
				err := json.Unmarshal([]byte(message.Payload), &email) // use json instead
				if err != nil {
					log.Println(err)
					return
				}
				channel <- &email
			// close when context done
			case <-ctx.Done():
				sub.Close()
				return
			}
		}
	}()
	return channel, nil
}

For pub

emailJSON, err := json.Marshal(email)
if err != nil {
	...
}
err = r.Redis.Publish(ctx, "email", emailJSON).Err()
if err != nil {
	...
}

idk if using json is good practice.