Uploaded image for project: 'Go Driver'
  1. Go Driver
  2. GODRIVER-3340

Goroutine / memory leak in zstd network compression decoding

    • Type: Icon: Bug Bug
    • Resolution: Unresolved
    • Priority: Icon: Unknown Unknown
    • 2.0.1
    • Affects Version/s: 1.16.1
    • Component/s: None
    • None
    • Go Drivers

      Detailed steps to reproduce the problem?

      The program below will demonstrate the leak by incrementally outputting goroutine counts. It also exposes a pprof server at localhost:8080 for profile analysis.

      package main
      
      import (
      	"context"
      	"fmt"
      	"log"
      	"net/http"
      	_ "net/http/pprof"
      	"runtime"
      	"time"
      
      	"go.mongodb.org/mongo-driver/bson"
      	"go.mongodb.org/mongo-driver/mongo"
      	"go.mongodb.org/mongo-driver/mongo/options"
      )
      
      func main() {
      	go func() {
      		log.Println(http.ListenAndServe("localhost:8080", nil))
      	}()
      
      	ctx := context.Background()
      	uri := "mongodb://localhost:27017"
      	opts := options.Client().ApplyURI(uri).SetCompressors([]string{"zstd"})
      
      	client, err := mongo.Connect(ctx, opts)
      	if err != nil {
      		log.Fatalf("Error connecting to mongo server: %v", err)
      	}
      	defer func() {
      		if err = client.Disconnect(ctx); err != nil {
      			panic(err)
      		}
      	}()
      
      	connectCtx, cancel := context.WithTimeout(ctx, time.Second*5)
      	defer cancel()
      	if err := client.Ping(connectCtx, nil); err != nil {
      		log.Fatalf("Error pinging mongo server: %v\n", err)
      	}
      
      	type TestStruct struct {
      		X string `bson:"x"`
      	}
      	collection := client.Database("leaktest").Collection("testcollection")
      	if _, err := collection.InsertOne(ctx, TestStruct{X: "123"}); err != nil {
      		log.Fatalf("Error intializing collection: %v\n", err)
      	}
      
      	for i := 0; i < 30; i++ {
      		var doc TestStruct
      		if err := collection.FindOne(ctx, bson.M{"x": "123"}).Decode(&doc); err != nil {
      			log.Fatalf("Error fetching document: %v\n", err)
      		}
      		time.Sleep(1 * time.Second)
      		runtime.GC()
      		fmt.Printf("Goroutines: %d\n", runtime.NumGoroutine())
      	}
      }
      
      

      Sample output from pprof while running:

      go tool pprof -top http://localhost:8080/debug/pprof/goroutine
      File: main
      Type: goroutine
      Time: Sep 12, 2024 at 1:39pm (PDT)
      Showing nodes accounting for 388, 100% of 388 total
            flat  flat%   sum%        cum   cum%
             387 99.74% 99.74%        387 99.74%  runtime.gopark
               1  0.26%   100%          1  0.26%  runtime.goroutineProfileWithLabels
               0     0%   100%        378 97.42%  github.com/klauspost/compress/zstd.(*blockDec).startDecoder
               0     0%   100%          1  0.26%  go.mongodb.org/mongo-driver/x/mongo/driver.Operation.ExecuteExhaust
               0     0%   100%          1  0.26%  go.mongodb.org/mongo-driver/x/mongo/driver.Operation.readWireMessage
               0     0%   100%          1  0.26%  go.mongodb.org/mongo-driver/x/mongo/driver/operation.(*Hello).StreamResponse
               0     0%   100%          1  0.26%  go.mongodb.org/mongo-driver/x/mongo/driver/topology.(*Server).check
               0     0%   100%          1  0.26%  go.mongodb.org/mongo-driver/x/mongo/driver/topology.(*Server).update
               0     0%   100%          1  0.26%  go.mongodb.org/mongo-driver/x/mongo/driver/topology.(*cancellListener).Listen
               0     0%   100%          1  0.26%  go.mongodb.org/mongo-driver/x/mongo/driver/topology.(*connection).read
               0     0%   100%          1  0.26%  go.mongodb.org/mongo-driver/x/mongo/driver/topology.(*connection).readWireMessage
               0     0%   100%          2  0.52%  go.mongodb.org/mongo-driver/x/mongo/driver/topology.(*pool).createConnections
               0     0%   100%          2  0.52%  go.mongodb.org/mongo-driver/x/mongo/driver/topology.(*pool).createConnections.func2
      
      

      Definition of done: what must be done to consider the task complete?

      Enabling zstd compression on the client no longer leaks goroutines.

      The exact Go version used, with patch level:

      $ go version

      go version go1.22.7 darwin/arm64

      The exact version of the Go driver used:

      $ go list -m go.mongodb.org/mongo-driver

      go.mongodb.org/mongo-driver v1.16.1

      Describe how MongoDB is set up. Local vs Hosted, version, topology, load balanced, etc.

      For the reproduction using the program above a local MongoDB docker container was running, using the mongo:7.0 image.

      Security Vulnerabilities

      If you’ve identified a security vulnerability in a driver or any other MongoDB project, please report it according to the instructions here

            Assignee:
            matt.dale@mongodb.com Matt Dale
            Reporter:
            matt@convictional.com Matt Lougheed
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: