From 7dc1646119cdbc6a3811827c5d48a909432a7931 Mon Sep 17 00:00:00 2001 From: Jackson Owens Date: Thu, 22 Jun 2023 16:03:00 -0400 Subject: [PATCH] *,sstable: upgrade zstd to v1.5.6 Upgrade zstd to v1.5.6. Due to changes in the mechanics of zstd.Decompress function, this required a change to using a new zstd.DecompressInto entrypoint that guarantees it'll deserialize into the provided destination buffer, never allocating a new buffer. Close #1706. --- go.mod | 2 +- go.sum | 4 ++-- sstable/compression.go | 16 ++++++++++------ sstable/compression_cgo.go | 14 +++++++++----- sstable/compression_nocgo.go | 9 ++++----- sstable/reader.go | 2 +- sstable/suffix_rewriter.go | 5 +++-- sstable/testdata/h.zstd-compression.sst | Bin 11548 -> 11524 bytes sstable/writer_fixture_test.go | 8 ++++++++ 9 files changed, 38 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index 75a653ad3c..705bb530f3 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/cockroachdb/pebble require ( - github.com/DataDog/zstd v1.4.5 + github.com/DataDog/zstd v1.5.6-0.20230824185856-869dae002e5e github.com/HdrHistogram/hdrhistogram-go v1.1.2 github.com/cespare/xxhash/v2 v2.2.0 github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f diff --git a/go.sum b/go.sum index 5b77668069..aaedd95c39 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= -github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/DataDog/zstd v1.5.6-0.20230824185856-869dae002e5e h1:ZIWapoIRN1VqT8GR8jAwb1Ie9GyehWjVcGh32Y2MznE= +github.com/DataDog/zstd v1.5.6-0.20230824185856-869dae002e5e/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190129172621-c8b1d7a94ddf/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= diff --git a/sstable/compression.go b/sstable/compression.go index 0db70c873a..70f4c6033d 100644 --- a/sstable/compression.go +++ b/sstable/compression.go @@ -33,7 +33,9 @@ func decompressedLen(blockType blockType, b []byte) (int, int, error) { } } -func decompressInto(blockType blockType, compressed []byte, buf []byte) ([]byte, error) { +// decompressInto decompresses compressed into buf. The buf slice must have the +// exact size as the decompressed value. +func decompressInto(blockType blockType, compressed []byte, buf []byte) error { var result []byte var err error switch blockType { @@ -41,15 +43,17 @@ func decompressInto(blockType blockType, compressed []byte, buf []byte) ([]byte, result, err = snappy.Decode(buf, compressed) case zstdCompressionBlockType: result, err = decodeZstd(buf, compressed) + default: + return base.CorruptionErrorf("pebble/table: unknown block compression: %d", errors.Safe(blockType)) } if err != nil { - return nil, base.MarkCorruptionError(err) + return base.MarkCorruptionError(err) } - if len(result) != 0 && (len(result) != len(buf) || &result[0] != &buf[0]) { - return nil, base.CorruptionErrorf("pebble/table: decompressed into unexpected buffer: %p != %p", + if len(result) != len(buf) || (len(result) > 0 && &result[0] != &buf[0]) { + return base.CorruptionErrorf("pebble/table: decompressed into unexpected buffer: %p != %p", errors.Safe(result), errors.Safe(buf)) } - return result, nil + return nil } // decompressBlock decompresses an SST block, with manually-allocated space. @@ -68,7 +72,7 @@ func decompressBlock(blockType blockType, b []byte) (*cache.Value, error) { // Allocate sufficient space from the cache. decoded := cache.Alloc(decodedLen) decodedBuf := decoded.Buf() - if _, err := decompressInto(blockType, b, decodedBuf); err != nil { + if err := decompressInto(blockType, b, decodedBuf); err != nil { cache.Free(decoded) return nil, err } diff --git a/sstable/compression_cgo.go b/sstable/compression_cgo.go index ad7d844201..ba9c02c69c 100644 --- a/sstable/compression_cgo.go +++ b/sstable/compression_cgo.go @@ -13,11 +13,15 @@ import ( "github.com/DataDog/zstd" ) -// decodeZstd decompresses b with the Zstandard algorithm. -// It reuses the preallocated capacity of decodedBuf if it is sufficient. -// On success, it returns the decoded byte slice. -func decodeZstd(decodedBuf, b []byte) ([]byte, error) { - return zstd.Decompress(decodedBuf, b) +// decodeZstd decompresses src with the Zstandard algorithm. The destination +// buffer must already be sufficiently sized, otherwise decodeZstd may error. +func decodeZstd(dst, src []byte) ([]byte, error) { + n, err := zstd.DecompressInto(dst, src) + // NB: zstd.DecompressInto may return n < 0 if err != nil. + if err != nil { + return nil, err + } + return dst[:n], nil } // encodeZstd compresses b with the Zstandard algorithm at default compression diff --git a/sstable/compression_nocgo.go b/sstable/compression_nocgo.go index 42c34fb7f0..cfd79dd272 100644 --- a/sstable/compression_nocgo.go +++ b/sstable/compression_nocgo.go @@ -9,13 +9,12 @@ package sstable import "github.com/klauspost/compress/zstd" -// decodeZstd decompresses b with the Zstandard algorithm. -// It reuses the preallocated capacity of decodedBuf if it is sufficient. -// On success, it returns the decoded byte slice. -func decodeZstd(decodedBuf, b []byte) ([]byte, error) { +// decodeZstd decompresses src with the Zstandard algorithm. The destination +// buffer must already be sufficiently sized, otherwise decodeZstd may error. +func decodeZstd(dst, src []byte) ([]byte, error) { decoder, _ := zstd.NewReader(nil) defer decoder.Close() - return decoder.DecodeAll(b, decodedBuf[:0]) + return decoder.DecodeAll(src, dst[:0]) } // encodeZstd compresses b with the Zstandard algorithm at default compression diff --git a/sstable/reader.go b/sstable/reader.go index 275b5083ae..c837f28dee 100644 --- a/sstable/reader.go +++ b/sstable/reader.go @@ -692,7 +692,7 @@ func (r *Reader) readBlock( } else { decompressed = cacheValueOrBuf{v: cache.Alloc(decodedLen)} } - if _, err := decompressInto(typ, compressed.get()[prefixLen:], decompressed.get()); err != nil { + if err := decompressInto(typ, compressed.get()[prefixLen:], decompressed.get()); err != nil { compressed.release() return bufferHandle{}, err } diff --git a/sstable/suffix_rewriter.go b/sstable/suffix_rewriter.go index 083ba5e02f..6b428d720a 100644 --- a/sstable/suffix_rewriter.go +++ b/sstable/suffix_rewriter.go @@ -535,8 +535,9 @@ func readBlockBuf(r *Reader, bh BlockHandle, buf []byte) ([]byte, []byte, error) if cap(buf) < decompressedLen { buf = make([]byte, decompressedLen) } - res, err := decompressInto(typ, raw[prefix:], buf[:decompressedLen]) - return res, buf, err + dst := buf[:decompressedLen] + err = decompressInto(typ, raw[prefix:], dst) + return dst, buf, err } // memReader is a thin wrapper around a []byte such that it can be passed to diff --git a/sstable/testdata/h.zstd-compression.sst b/sstable/testdata/h.zstd-compression.sst index 57205b5fde49962c998440088881e492d7b74421..6f59d24c8457b5cb092fab6cf05fbb7607a05f18 100644 GIT binary patch delta 9779 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 vg{|F-Zo^_c;C8@n*X