Gqlgen: [bug] Subscription release Go routines at scale

Created on 10 Sep 2018  路  3Comments  路  Source: 99designs/gqlgen

My test

Expected Behaviour

  • I should have ~5 go-routines

Actual Behavior

  • I have ~45 go-routines

Minimal graphql.schema and models to reproduce

type ObjectReturned {
    name: String!
}

type Query {
    Get(name: String!): ObjectReturned
}

type Subscription {
    Updated(): ObjectReturned!
}
bug

Most helpful comment

Thanks for the example, made it easy to track this down.

So theres a couple of leaks, one is in your example, the other has a PR.

// Stop turns off a ticker. After Stop, no more ticks will be sent.
// Stop does not close the channel, to prevent a concurrent goroutine
// reading from the channel from seeing an erroneous "tick".
func (t *Ticker) Stop() {
    stopTimer(&t.r)
}

So you probably want to coordinate close properly, you also want to close the channel you return. Updated example:

// Updated returns a fake object each 5 seconds
func (sr *subscriptionResolver) Updated(ctx context.Context) (<-chan model.ObjectReturned, error) {
    object := make(chan model.ObjectReturned, 1)

    ticker := time.NewTicker(5 * time.Second)

    go func() {
        for {
            select {
            case t := <-ticker.C:
                object <- model.ObjectReturned{
                    Name: fmt.Sprintf("%s", t),
                }
            case <-ctx.Done():
                ticker.Stop()
                close(object)
                return
            }
        }
    }()

    return object, nil
}

All 3 comments

Do you have an idea @vektah? 馃槙
(sorry for the direct ping 馃槚)

Thanks for the example, made it easy to track this down.

So theres a couple of leaks, one is in your example, the other has a PR.

// Stop turns off a ticker. After Stop, no more ticks will be sent.
// Stop does not close the channel, to prevent a concurrent goroutine
// reading from the channel from seeing an erroneous "tick".
func (t *Ticker) Stop() {
    stopTimer(&t.r)
}

So you probably want to coordinate close properly, you also want to close the channel you return. Updated example:

// Updated returns a fake object each 5 seconds
func (sr *subscriptionResolver) Updated(ctx context.Context) (<-chan model.ObjectReturned, error) {
    object := make(chan model.ObjectReturned, 1)

    ticker := time.NewTicker(5 * time.Second)

    go func() {
        for {
            select {
            case t := <-ticker.C:
                object <- model.ObjectReturned{
                    Name: fmt.Sprintf("%s", t),
                }
            case <-ctx.Done():
                ticker.Stop()
                close(object)
                return
            }
        }
    }()

    return object, nil
}

Hum, sorry! Effectively, I forgot to close channel :/

Thanks for the PR !

I fixed my example.

Was this page helpful?
0 / 5 - 0 ratings