compress: stop buffering the compressibility check output in memory

The compressibility heuristic compressed a 1 MiB sample of each upload
into a bytes.Buffer only to read its length, growing up to ~1 MiB of
garbage per file. Write the sample through a counting io.Discard-style
writer instead so no output buffer is allocated at all.
This commit is contained in:
Nick Craig-Wood
2026-09-01 14:21:52 +01:00
parent 03c1c08a27
commit da868b06f8
4 changed files with 36 additions and 6 deletions
+9
View File
@@ -1125,6 +1125,15 @@ func (o *Object) Remove(ctx context.Context) error {
return objErr return objErr
} }
// countingDiscard is an io.Writer which discards its input and counts
// the bytes written
type countingDiscard int64
func (c *countingDiscard) Write(p []byte) (int, error) {
*c += countingDiscard(len(p))
return len(p), nil
}
// ReadCloserWrapper combines a Reader and a Closer to a ReadCloser // ReadCloserWrapper combines a Reader and a Closer to a ReadCloser
type ReadCloserWrapper struct { type ReadCloserWrapper struct {
io.Reader io.Reader
+2 -3
View File
@@ -2,7 +2,6 @@ package compress
import ( import (
"bufio" "bufio"
"bytes"
"context" "context"
"crypto/md5" "crypto/md5"
"encoding/hex" "encoding/hex"
@@ -23,7 +22,7 @@ type gzipModeHandler struct{}
// isCompressible checks the compression ratio of the provided data and returns true if the ratio exceeds // isCompressible checks the compression ratio of the provided data and returns true if the ratio exceeds
// the configured threshold // the configured threshold
func (g *gzipModeHandler) isCompressible(r io.Reader, compressionMode int) (bool, error) { func (g *gzipModeHandler) isCompressible(r io.Reader, compressionMode int) (bool, error) {
var b bytes.Buffer var b countingDiscard
var n int64 var n int64
w, err := sgzip.NewWriterLevel(&b, sgzip.DefaultCompression) w, err := sgzip.NewWriterLevel(&b, sgzip.DefaultCompression)
if err != nil { if err != nil {
@@ -37,7 +36,7 @@ func (g *gzipModeHandler) isCompressible(r io.Reader, compressionMode int) (bool
if err != nil { if err != nil {
return false, err return false, err
} }
ratio := float64(n) / float64(b.Len()) ratio := float64(n) / float64(b)
return ratio > minCompressionRatio, nil return ratio > minCompressionRatio, nil
} }
+23
View File
@@ -3,6 +3,7 @@ package compress
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/rand"
"io" "io"
"testing" "testing"
"time" "time"
@@ -112,3 +113,25 @@ func TestRcat(t *testing.T) {
}) })
} }
} }
// TestIsCompressible checks the compressibility heuristic on data at
// both ends of the scale for every compression mode which has one.
func TestIsCompressible(t *testing.T) {
compressible := bytes.Repeat([]byte("compress me "), 4096)
incompressible := make([]byte, len(compressible))
_, err := io.ReadFull(rand.Reader, incompressible)
require.NoError(t, err)
for name, handler := range map[string]compressionModeHandler{
"gzip": &gzipModeHandler{},
"zstd": &zstdModeHandler{},
} {
t.Run(name, func(t *testing.T) {
ok, err := handler.isCompressible(bytes.NewReader(compressible), 0)
require.NoError(t, err)
assert.True(t, ok)
ok, err = handler.isCompressible(bytes.NewReader(incompressible), 0)
require.NoError(t, err)
assert.False(t, ok)
})
}
}
+2 -3
View File
@@ -2,7 +2,6 @@ package compress
import ( import (
"bufio" "bufio"
"bytes"
"context" "context"
"crypto/md5" "crypto/md5"
"encoding/hex" "encoding/hex"
@@ -23,7 +22,7 @@ type zstdModeHandler struct{}
// isCompressible checks the compression ratio of the provided data and returns true if the ratio exceeds // isCompressible checks the compression ratio of the provided data and returns true if the ratio exceeds
// the configured threshold // the configured threshold
func (z *zstdModeHandler) isCompressible(r io.Reader, compressionMode int) (bool, error) { func (z *zstdModeHandler) isCompressible(r io.Reader, compressionMode int) (bool, error) {
var b bytes.Buffer var b countingDiscard
var n int64 var n int64
w, err := NewWriterSzstd(&b, zstd.WithEncoderLevel(zstd.SpeedDefault)) w, err := NewWriterSzstd(&b, zstd.WithEncoderLevel(zstd.SpeedDefault))
if err != nil { if err != nil {
@@ -37,7 +36,7 @@ func (z *zstdModeHandler) isCompressible(r io.Reader, compressionMode int) (bool
if err != nil { if err != nil {
return false, err return false, err
} }
ratio := float64(n) / float64(b.Len()) ratio := float64(n) / float64(b)
return ratio > minCompressionRatio, nil return ratio > minCompressionRatio, nil
} }