Closed segakazzz closed 3 years ago
Moving this one to plush as it seems to be related to it. Thanks for reporting it @segakazzz
I'm also experiencing this memory leak. I removed csrf altogether and still see the memory leak in production, on buffalo v0.16.20
.
I spent some time digging and found the source of the memory leak. It's not exactly a leak, but by design. Plush caches every template in an unbounded cache var cache = map[string]*Template{}
which retains the template string data. Additionally, it uses the entire raw template input as the string
in the map vs some hash, further increasing memory usage. Using a local modified version of plush with the caching removed, the problem goes away.
The reason why the problem goes away when removing the authenticity token meta tag in the example above is because that tag generates a unique token and causes the template to be different and thus a cache miss, then a cache creation.
This design does not work for applications with dynamic templates. For my sites, there are a lot of pages loading content from the DB and are highly variable, causing the cache to grow linearly until the host is OOM.
I'm not sure what the best solution here is, but caching at this level should not happen by default, or the cache should be bounded (e.g. fixed size LRU) - although as demonstrated by the CSRF example, that invalidates the cache entirely. Some sort of "partial" caching could work for expensive templates but it should be opt-in.
@saurori @segakazzz I did put this PR together, https://github.com/gobuffalo/plush/pull/119. thoughts? I think we could test it with :
go get github.com/gobuffalo/plush/v4@task-optin-cache
@paganotoni tested locally with that branch. Memory usage looks good. Hitting the same page with ab -n 1000 -c 2
before and after the fix, there is a small performance hit which is to be expected (165.51 [#/sec] (mean)
-> 146.70 [#/sec] (mean)
).
@paganotoni @saurori Thanks for the update. I compared before and after and memory allocation of bytes.(*Buffer).String
was gone. Awesome!!!
I tried pprof after ab -n 10000 -c 100 http://127.0.0.1:3000/
and here is the result of both.
$ go tool pprof -top http://127.0.0.1:6060/debug/pprof/heap
Fetching profile over HTTP from http://127.0.0.1:6060/debug/pprof/heap
Type: inuse_space
Time: Jan 19, 2021 at 8:09pm (JST)
Showing nodes accounting for 37206.80kB, 100% of 37206.80kB total
flat flat% sum% cum cum%
28745.56kB 77.26% 77.26% 28745.56kB 77.26% bytes.(*Buffer).String (inline)
1542.01kB 4.14% 81.40% 1542.01kB 4.14% bufio.NewReaderSize (inline)
1024.06kB 2.75% 84.16% 1024.06kB 2.75% github.com/gobuffalo/plush/v4/parser.(*parser).parseHTMLLiteral
768.26kB 2.06% 86.22% 2304.33kB 6.19% github.com/gobuffalo/plush/v4.Parse
516.01kB 1.39% 87.61% 516.01kB 1.39% github.com/jackc/chunkreader/v2.(*ChunkReader).newBuf (inline)
513kB 1.38% 88.99% 513kB 1.38% bufio.NewWriterSize (inline)
512.75kB 1.38% 90.36% 512.75kB 1.38% bytes.makeSlice
512.56kB 1.38% 91.74% 32587.26kB 87.58% github.com/gobuffalo/buffalo/render.templateRenderer.exec
512.19kB 1.38% 93.12% 512.19kB 1.38% runtime.malg
512.17kB 1.38% 94.50% 512.17kB 1.38% github.com/gorilla/sessions.(*Registry).Get
512.14kB 1.38% 95.87% 512.14kB 1.38% github.com/sirupsen/logrus.(*Entry).WithFields
512.05kB 1.38% 97.25% 512.05kB 1.38% github.com/gobuffalo/packd.buildFile
512.02kB 1.38% 98.62% 512.02kB 1.38% encoding/json.newStructEncoder
512.02kB 1.38% 100% 1536.08kB 4.13% github.com/gobuffalo/plush/v4.NewTemplate
- After with github.com/gobuffalo/plush/v4@task-optin-cache
$ go tool pprof -top http://127.0.0.1:6060/debug/pprof/heap
Fetching profile over HTTP from http://127.0.0.1:6060/debug/pprof/heap
Type: inuse_space
Time: Jan 19, 2021 at 8:11pm (JST)
Showing nodes accounting for 4655.46kB, 100% of 4655.46kB total
flat flat% sum% cum cum%
1066.64kB 22.91% 22.91% 1066.64kB 22.91% sync.(Map).Store
1024.38kB 22.00% 44.92% 1024.38kB 22.00% runtime.malg
516.01kB 11.08% 56.00% 516.01kB 11.08% unicode.init
512.25kB 11.00% 67.00% 512.25kB 11.00% bytes.makeSlice
512.14kB 11.00% 78.00% 512.14kB 11.00% github.com/gobuffalo/plush/v4.(Context).Set
512.04kB 11.00% 89.00% 512.04kB 11.00% strings.(*Builder).grow (inline)
512.01kB 11.00% 100% 1578.64kB 33.91% mime.setExtensionType
Solved in v4.1.0
. Thanks @segakazzz @saurori for the help with this one.
@paganotoni, How about if we detect if there's a CSRF token & disable the cache accordingly? We're already parsing the file so it could be flagged during lexing.
Description
Memory usage in heap is increasing proportionately to the volume of access caused by the following memory allocations. pprof report shows as follows.
After many trials, I found that the issue doesn't happen by removing following meta tag in application.plush.html
Steps to Reproduce the Problem
Add following code for pprof in main.go
func init() { go func() { if envy.Get("GO_ENV", "development") == "development" { fmt.Println("start pprof on :6060") log.Println(http.ListenAndServe("127.0.0.1:6060", nil)) } }() }
$ cd buffalonew $ buffalo dev
$ go tool pprof -top http://127.0.0.1:6060/debug/pprof/heap
$ ab -n 10000 -c 100 http://127.0.0.1:3000/
$ go tool pprof -top http://127.0.0.1:6060/debug/pprof/heap