diff --git a/tsdb/chunkenc/chunk.go b/tsdb/chunkenc/chunk.go index 5f9349f05b..2bc1bf33fd 100644 --- a/tsdb/chunkenc/chunk.go +++ b/tsdb/chunkenc/chunk.go @@ -49,6 +49,7 @@ type Chunk interface { // be re-used or a new iterator can be allocated. Iterator(Iterator) Iterator NumSamples() int + Compact() } // Appender adds sample pairs to a chunk. diff --git a/tsdb/chunkenc/xor.go b/tsdb/chunkenc/xor.go index ce6e0a9512..5401261d9d 100644 --- a/tsdb/chunkenc/xor.go +++ b/tsdb/chunkenc/xor.go @@ -49,6 +49,10 @@ import ( "math/bits" ) +const ( + chunkCompactCapacityThreshold = 32 +) + // XORChunk holds XOR encoded sample data. type XORChunk struct { b bstream @@ -75,6 +79,14 @@ func (c *XORChunk) NumSamples() int { return int(binary.BigEndian.Uint16(c.Bytes())) } +func (c *XORChunk) Compact() { + if l := len(c.b.stream); cap(c.b.stream) > l+chunkCompactCapacityThreshold { + buf := make([]byte, l) + copy(buf, c.b.stream) + c.b.stream = buf + } +} + // Appender implements the Chunk interface. func (c *XORChunk) Appender() (Appender, error) { it := c.iterator(nil) diff --git a/tsdb/head.go b/tsdb/head.go index ce4a367192..5769c770aa 100644 --- a/tsdb/head.go +++ b/tsdb/head.go @@ -1691,6 +1691,11 @@ func (s *memSeries) cut(mint int64) *memChunk { s.chunks = append(s.chunks, c) s.headChunk = c + // Remove exceeding capacity from the previous chunk byte slice to save memory. + if l := len(s.chunks); l > 1 { + s.chunks[l-2].chunk.Compact() + } + // Set upper bound on when the next chunk must be started. An earlier timestamp // may be chosen dynamically at a later point. s.nextAt = rangeForTimestamp(mint, s.chunkRange)