Skip to content

Leak if not reading the output of Query function #35

Open
@sankarp-kavach

Description

@sankarp-kavach

A sample program:

package main

import (
	"fmt"
	"net/http"
	_ "net/http/pprof"
	"time"

	"github.com/gomodule/redigo/redis"
	rg "github.com/redislabs/redisgraph-go"
	"github.com/sirupsen/logrus"
)

func process() {
	for {
		queryStr := fmt.Sprintf("MATCH (X) RETURN X")

		_ = query("mesh7Graph", queryStr)
		logrus.Println("blah")
	}
}

func query(graphName, query string) error {
	conn := pool.Get()
	defer conn.Close()

	g := rg.GraphNew(graphName, conn)
	if false {
		_, err := g.Query(query)
		if err != nil {
			logrus.Errorf("GraphDB query error: %v", err)
			return err
		}
	} else {
		res, err := g.Query(query)
		if err != nil {
			logrus.Errorf("GraphDB query error: %v", err)
			return err
		}

		for res.Next() {
			_ = res.Record()
		}
	}

	return nil
}

var pool *redis.Pool

const (
	address  = "<REDIS_SERVER_URL>:6379"
	password = "<REDIS_PASSWORD>"
)

func main() {
	pool = &redis.Pool{
		MaxIdle:     100,
		MaxActive:   100,
		IdleTimeout: 240 * time.Second,
		// Dial or DialContext must be set.
		// When both are set, DialContext takes precedence over Dial.
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", address)
			logrus.Tracef("Dialing redis @ %v", address)
			if err != nil {
				logrus.Errorf("Dial failed. Error: %v", err)
				return nil, err
			}
			if _, err := c.Do("AUTH", password); err != nil {
				logrus.Errorf("Authorization failed. Error: %v", err)
				c.Close()
				return nil, err
			}
			return c, nil
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			if time.Since(t) < time.Minute {
				return nil
			}
			_, err := c.Do("PING")
			return err
		},
	}

	go process()

	logrus.Fatal(http.ListenAndServe("localhost:8080", nil))
}

Now run the above program toggling the if block with false and true. The program will just continuously do some redisgraph calls and not parse the response in one case, will parse the response in another case.

When the above program is running, do:

curl -sK -v http://localhost:8080/debug/pprof/heap > heap.out
go tool pprof heap.out
(pprof) lines
(pprof) web

In the case when we ignore the output of Query function, we get a memory leak in the pprof output. When the response is parsed and the record are read, there is no leak reported in the pprof output.

The docs do not mention anything about reading the Query function output. There is no close function on the QueryResult struct either, if I have to call it via defer. There is no other function except Query (like an Exec in the SQL) which I can do to not read results.

I will be happy to provide any other debugging information if you want.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions