123 lines
3.3 KiB
Go
123 lines
3.3 KiB
Go
// Copyright (C) MongoDB, Inc. 2024-present.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License. You may obtain
|
|
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.psichedelico.com/go/bson"
|
|
"gitea.psichedelico.com/go/bson/mongo"
|
|
"gitea.psichedelico.com/go/bson/mongo/options"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/goleak"
|
|
)
|
|
|
|
var dbName = fmt.Sprintf("goleak-%d", time.Now().Unix())
|
|
|
|
// TestGoroutineLeak creates clients with various client configurations, runs
|
|
// some operations with each one, then disconnects the client. It asserts that
|
|
// no goroutines were leaked after the client is disconnected.
|
|
func TestGoroutineLeak(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
opts *options.ClientOptions
|
|
}{
|
|
{
|
|
desc: "base",
|
|
opts: options.Client(),
|
|
},
|
|
{
|
|
desc: "compressors=snappy",
|
|
opts: options.Client().SetCompressors([]string{"snappy"}),
|
|
},
|
|
{
|
|
desc: "compressors=zlib",
|
|
opts: options.Client().SetCompressors([]string{"zlib"}),
|
|
},
|
|
{
|
|
desc: "compressors=zstd",
|
|
opts: options.Client().SetCompressors([]string{"zstd"}),
|
|
},
|
|
{
|
|
desc: "minPoolSize=10",
|
|
opts: options.Client().SetMinPoolSize(10),
|
|
},
|
|
{
|
|
desc: "serverMonitoringMode=poll",
|
|
opts: options.Client().SetServerMonitoringMode(options.ServerMonitoringModePoll),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
// These can't be run in parallel because goleak currently can't filter
|
|
// out goroutines from other parallel subtests.
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
defer goleak.VerifyNone(t)
|
|
|
|
base := options.Client()
|
|
if u := os.Getenv("MONGODB_URI"); u != "" {
|
|
base.ApplyURI(u)
|
|
}
|
|
client, err := mongo.Connect(base, tc.opts)
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
err = client.Disconnect(context.Background())
|
|
require.NoError(t, err)
|
|
}()
|
|
|
|
db := client.Database(dbName)
|
|
defer func() {
|
|
err := db.Drop(context.Background())
|
|
require.NoError(t, err)
|
|
}()
|
|
|
|
coll := db.Collection(collectionName(t))
|
|
|
|
// Start a change stream to simulate a change listener workload.
|
|
cs, err := coll.Watch(context.Background(), mongo.Pipeline{})
|
|
require.NoError(t, err)
|
|
defer cs.Close(context.Background())
|
|
|
|
// Run some Insert and FindOne operations to simulate a writing and
|
|
// reading workload. Run 50 iterations to increase the probability
|
|
// that a goroutine leak will happen if a problem exists.
|
|
for i := 0; i < 50; i++ {
|
|
_, err = coll.InsertOne(context.Background(), bson.M{"x": 123})
|
|
require.NoError(t, err)
|
|
|
|
var res bson.D
|
|
err = coll.FindOne(context.Background(), bson.D{}).Decode(&res)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Intentionally cause some timeouts. Ignore any errors.
|
|
for i := 0; i < 50; i++ {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Microsecond)
|
|
coll.FindOne(ctx, bson.D{}).Err()
|
|
cancel()
|
|
}
|
|
|
|
// Finish simulating the change listener workload. Use "Next" to
|
|
// fetch at least one change stream document batch and decode the
|
|
// first document.
|
|
cs.Next(context.Background())
|
|
var res bson.D
|
|
err = cs.Decode(&res)
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
}
|
|
|
|
func collectionName(t *testing.T) string {
|
|
return fmt.Sprintf("%s-%d", t.Name(), time.Now().Unix())
|
|
}
|