fastcache icon indicating copy to clipboard operation
fastcache copied to clipboard

Memory leak when using `LoadFromFile` multiple times

Open Link512 opened this issue 4 years ago • 0 comments

I have a service that every N minutes loads a fastcache from file. Every time this happens, the program leaks memory. This was easily observable by simply loading a cache multiple times in the same program and then using htop/ActivityMonitor to see this steady increase of memory. In my particular use case, the cache is rather large (~18M keys, 662MB on disk).

The snippet to reproduce this is straight-forward:

package main

import (
	"fmt"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	"github.com/VictoriaMetrics/fastcache"
)

func main() {
	cachePath := "path_to_cache"

	tick := time.NewTicker(15 * time.Second)
	sig := make(chan os.Signal, 2)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
	fmt.Println(os.Getpid())
	for {
		select {
		case <-tick.C:
			newCache, err := fastcache.LoadFromFile(cachePath)
			if err != nil {
				panic(err)
			}
			_ = newCache
			runtime.GC()
			fmt.Println("reloading cache done")
		case <-sig:
			tick.Stop()
			return
		}
	}
}

After some profiling and debugging I've found that the use of getChunk() with memmap is the culprit. When we reload the cache every time, we never call putChunk, only getChunk, which causes the library to continuosly allocate memory via memmap which is never munmaped. To verify this, I added a panic to putChunk, which was never triggered.

I think perhaps one solution is to add LoadFromFile as a method also to the Cache object, so that the chunks from the old buckets can be reused when loading a new cache. I might be able to help with a PR if time permits, but I thought in the meantime you should be aware of this issue

Link512 avatar Jul 06 '21 11:07 Link512