Gqlgen: how do subscriptions work for multiple load balanced websocker servers?

Created on 28 Aug 2019  路  4Comments  路  Source: 99designs/gqlgen

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!

stale

Most helpful comment

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())
}

All 4 comments

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())
}

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ksoda picture ksoda  路  3Comments

bieber picture bieber  路  4Comments

JulienBreux picture JulienBreux  路  3Comments

RobertoOrtis picture RobertoOrtis  路  3Comments

steebchen picture steebchen  路  3Comments