call-me-corgi
call-me-corgi

Reputation: 243

Dealing with context when reusing a mongodb connection

I'm making multiple goroutines share a single connection by passing client as an argument.

uri := "mongodb://localhost:27017"
ctx := context.Background()
client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri))

go Foo(client)
go Bar(client)

func Foo(client *mongo.Client) {
        // ... 
}

func Bar(client *mongoClient) {
        // ...
}

I'm confused about what to do with ctx. Should I create a new context everytime I query to the database, or should I reuse context just like the client?

Upvotes: 1

Views: 2073

Answers (2)

Thomas Dorn
Thomas Dorn

Reputation: 1

I have also done it like this

type DB struct {
    client *mongo.Client
}

func (db *DB) GetVideoStream {}

func main() {
    ctx, _ := context.WithTimeout(context.Background(), 60*time.Second)
    client, err := mongo.Connect(ctx, clientOpts)
    db := &DB{client: client}
    go db.GetVideoStream()
    http.HandleFunc("/api/", db.GetVideoStream)
}

You can use pointer receivers to do the same thing.

I am new still new to the language

Upvotes: 0

Illia
Illia

Reputation: 374

It depends on how your Foo and Bar methods behave. Let's imagine the Foo method is a simple short-lived goroutine that makes one query to DB and the only thing you want is to check if its parent context is not Done or Cancelled. Then you can provide parent context to your Foo method.

func main() {
    uri := "mongodb://localhost:27017"
    ctx := context.Background()
    client, err := Connect(ctx, uri)

    ctx, cancel := context.WithCancel(ctx)

    
    if err != nil {
        panic(err)
    }

    go Foo(ctx, client)
    go Bar(context.WithValue(ctx, "uri", uri), client)

    // cancel parent context
    cancel()

    time.Sleep(5*time.Second)
}

func Foo(ctx context.Context, client *Client) {
    fmt.Printf("Foo: %s\n", ctx.Value("uri"))
    select {
        case <- ctx.Done():
            err := ctx.Err()
            if err != nil {
                // you could switch for the actual reason
                fmt.Println("In our case context canceled: ", err)
                return
            }
            fmt.Printf("Do something...")
    }
}

On the other hand, if Bar performs some non-trivial logic and makes more than one call to DB you probably want a separate context to be able to cancel it separately from your parent context. Then you could derive a new context from your parent.

func Bar(ctx context.Context, client *Client) {
    // Bar has a non trivial logic and needs a separate cancellation and handling
    ctx, cancelFunc := context.WithCancel(ctx)
    fmt.Printf("Bar: %s\n", ctx.Value("uri"))
    
    // cancel derived context
    cancelFunc()

}

Upvotes: 1

Related Questions