-
Notifications
You must be signed in to change notification settings - Fork 81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Memory leak in Thumbnail generation #561
Comments
Quick Update: Can confirm that the pipe was the issue. This quick and dirty fix has been running on my server for a few days now and even on the off-chance that a gif palette pops up, it disappears if a gc is triggered before retrieving the profile. diff --git a/thumbnailing/i/gif.go b/thumbnailing/i/gif.go
index 676d0cb..625d88c 100644
--- a/thumbnailing/i/gif.go
+++ b/thumbnailing/i/gif.go
@@ -1,6 +1,8 @@
package i
import (
+ "fmt"
+ "bytes"
"errors"
"image"
"image/draw"
@@ -76,19 +78,15 @@ func (d gifGenerator) GenerateThumbnail(b io.Reader, contentType string, width i
}
// The thumbnailer decided that it shouldn't thumbnail, so encode it ourselves
- pr, pw := io.Pipe()
- go func(pw *io.PipeWriter, p *image.Paletted) {
- err = u.Encode(ctx, pw, p)
- if err != nil {
- _ = pw.CloseWithError(errors.New("gif: error encoding still frame thumbnail: " + err.Error()))
- } else {
- _ = pw.Close()
- }
- }(pw, targetImg)
+ buf := bytes.NewBuffer(make([]byte,0,128*1024))
+ err = u.Encode(ctx, buf, targetImg)
+ if err != nil {
+ return nil, fmt.Errorf("failed to encode static gif preview: %w",err)
+ }
return &m.Thumbnail{
Animated: false,
ContentType: "image/png",
- Reader: pr,
+ Reader: io.NopCloser(buf),
}, nil
}
@@ -110,20 +108,16 @@ func (d gifGenerator) GenerateThumbnail(b io.Reader, contentType string, width i
g.Config.Width = g.Image[0].Bounds().Max.X
g.Config.Height = g.Image[0].Bounds().Max.Y
- pr, pw := io.Pipe()
- go func(pw *io.PipeWriter, g *gif.GIF) {
- err = gif.EncodeAll(pw, g)
- if err != nil {
- _ = pw.CloseWithError(errors.New("gif: error encoding final thumbnail: " + err.Error()))
- } else {
- _ = pw.Close()
- }
- }(pw, g)
+ buf := bytes.NewBuffer(make([]byte,0,512*1024))
+ err = gif.EncodeAll(buf, g)
+ if err != nil {
+ return nil, fmt.Errorf("failed to encode animated gif preview: %w",err)
+ }
return &m.Thumbnail{
ContentType: "image/gif",
Animated: true,
- Reader: pr,
+ Reader: io.NopCloser(buf),
}, nil
} On that note, I'll probably add a removal of that profiler-header to the misc fixes PR, since I had to mess with my reverse proxy to run this like a normal person :D
|
When using an io.Pipe, it must be ensured that the pipe is drained. If it is not, all references required for the goroutine will not be freed by the garbage collector, as an active reference still exists (the abandoned goroutine the pipe is written to. To fix this, write to a non-blocking buffer. This may increase the RAM usage in the short run, but can be fully garbage collected, even if not read from. Since the size of the buffer is at most the size of the thumbnail and it being freed quickly, this should post a significantly smaller burden on the server. Signed-off-by: Moritz Poldrack <[email protected]> Fixes: 4378a4e ("Avoid use of buffers throughout thumbnailing") Fixes: t2bot#561
When a timeout happens during thumbnail generation, the
image.Paletted
leaks, since the Goroutine has no way of terminating. My working hypothesis is that when the does not connect, the pipewriter is stuck and thus never completes. The solution would be to simply write the result to a channel. Since I plan to give some parts a bit of a make-over after the current misc-fixes PR, I can take a look at it afterwards.The text was updated successfully, but these errors were encountered: