Merge pull request #2168 from tomwilkie/chunk-len
Add call to estimate number of samples in a chunk to the API
This commit is contained in:
		
						commit
						127332c56f
					
				|  | @ -260,7 +260,7 @@ func (d *Desc) MaybeEvict() bool { | ||||||
| // Chunk is the interface for all chunks. Chunks are generally not
 | // Chunk is the interface for all chunks. Chunks are generally not
 | ||||||
| // goroutine-safe.
 | // goroutine-safe.
 | ||||||
| type Chunk interface { | type Chunk interface { | ||||||
| 	// add adds a SamplePair to the chunks, performs any necessary
 | 	// Add adds a SamplePair to the chunks, performs any necessary
 | ||||||
| 	// re-encoding, and adds any necessary overflow chunks. It returns the
 | 	// re-encoding, and adds any necessary overflow chunks. It returns the
 | ||||||
| 	// new version of the original chunk, followed by overflow chunks, if
 | 	// new version of the original chunk, followed by overflow chunks, if
 | ||||||
| 	// any. The first chunk returned might be the same as the original one
 | 	// any. The first chunk returned might be the same as the original one
 | ||||||
|  | @ -276,6 +276,10 @@ type Chunk interface { | ||||||
| 	UnmarshalFromBuf([]byte) error | 	UnmarshalFromBuf([]byte) error | ||||||
| 	Encoding() Encoding | 	Encoding() Encoding | ||||||
| 	Utilization() float64 | 	Utilization() float64 | ||||||
|  | 
 | ||||||
|  | 	// Len returns the number of samples in the chunk.  Implementations may be
 | ||||||
|  | 	// expensive.
 | ||||||
|  | 	Len() int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Iterator enables efficient access to the content of a chunk. It is
 | // Iterator enables efficient access to the content of a chunk. It is
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,46 @@ | ||||||
|  | // Copyright 2016 The Prometheus Authors
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||||
|  | // you may not use this file except in compliance with the License.
 | ||||||
|  | // You may obtain a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | // http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||||
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||||
|  | // See the License for the specific language governing permissions and
 | ||||||
|  | // limitations under the License.
 | ||||||
|  | 
 | ||||||
|  | // Note: this file has tests for code in both delta.go and doubledelta.go --
 | ||||||
|  | // it may make sense to split those out later, but given that the tests are
 | ||||||
|  | // near-identical and share a helper, this feels simpler for now.
 | ||||||
|  | 
 | ||||||
|  | package chunk | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/prometheus/common/model" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestLen(t *testing.T) { | ||||||
|  | 	chunks := []Chunk{} | ||||||
|  | 	for _, encoding := range []Encoding{Delta, DoubleDelta, Varbit} { | ||||||
|  | 		c, err := NewForEncoding(encoding) | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 		chunks = append(chunks, c) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, c := range chunks { | ||||||
|  | 		for i := 0; i <= 10; i++ { | ||||||
|  | 			if c.Len() != i { | ||||||
|  | 				t.Errorf("chunk type %s should have %d samples, had %d", c.Encoding(), i, c.Len()) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			cs, _ := c.Add(model.SamplePair{model.Time(i), model.SampleValue(i)}) | ||||||
|  | 			c = cs[0] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -75,7 +75,7 @@ func newDeltaEncodedChunk(tb, vb deltaBytes, isInt bool, length int) *deltaEncod | ||||||
| // Add implements chunk.
 | // Add implements chunk.
 | ||||||
| func (c deltaEncodedChunk) Add(s model.SamplePair) ([]Chunk, error) { | func (c deltaEncodedChunk) Add(s model.SamplePair) ([]Chunk, error) { | ||||||
| 	// TODO(beorn7): Since we return &c, this method might cause an unnecessary allocation.
 | 	// TODO(beorn7): Since we return &c, this method might cause an unnecessary allocation.
 | ||||||
| 	if c.len() == 0 { | 	if c.Len() == 0 { | ||||||
| 		c = c[:deltaHeaderBytes] | 		c = c[:deltaHeaderBytes] | ||||||
| 		binary.LittleEndian.PutUint64(c[deltaHeaderBaseTimeOffset:], uint64(s.Timestamp)) | 		binary.LittleEndian.PutUint64(c[deltaHeaderBaseTimeOffset:], uint64(s.Timestamp)) | ||||||
| 		binary.LittleEndian.PutUint64(c[deltaHeaderBaseValueOffset:], math.Float64bits(float64(s.Value))) | 		binary.LittleEndian.PutUint64(c[deltaHeaderBaseValueOffset:], math.Float64bits(float64(s.Value))) | ||||||
|  | @ -191,7 +191,7 @@ func (c deltaEncodedChunk) FirstTime() model.Time { | ||||||
| 
 | 
 | ||||||
| // NewIterator implements chunk.
 | // NewIterator implements chunk.
 | ||||||
| func (c *deltaEncodedChunk) NewIterator() Iterator { | func (c *deltaEncodedChunk) NewIterator() Iterator { | ||||||
| 	return newIndexAccessingChunkIterator(c.len(), &deltaEncodedIndexAccessor{ | 	return newIndexAccessingChunkIterator(c.Len(), &deltaEncodedIndexAccessor{ | ||||||
| 		c:      *c, | 		c:      *c, | ||||||
| 		baseT:  c.baseTime(), | 		baseT:  c.baseTime(), | ||||||
| 		baseV:  c.baseValue(), | 		baseV:  c.baseValue(), | ||||||
|  | @ -296,7 +296,8 @@ func (c deltaEncodedChunk) sampleSize() int { | ||||||
| 	return int(c.timeBytes() + c.valueBytes()) | 	return int(c.timeBytes() + c.valueBytes()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c deltaEncodedChunk) len() int { | // Len implements Chunk. Runs in constant time.
 | ||||||
|  | func (c deltaEncodedChunk) Len() int { | ||||||
| 	if len(c) < deltaHeaderBytes { | 	if len(c) < deltaHeaderBytes { | ||||||
| 		return 0 | 		return 0 | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -83,14 +83,14 @@ func newDoubleDeltaEncodedChunk(tb, vb deltaBytes, isInt bool, length int) *doub | ||||||
| // Add implements chunk.
 | // Add implements chunk.
 | ||||||
| func (c doubleDeltaEncodedChunk) Add(s model.SamplePair) ([]Chunk, error) { | func (c doubleDeltaEncodedChunk) Add(s model.SamplePair) ([]Chunk, error) { | ||||||
| 	// TODO(beorn7): Since we return &c, this method might cause an unnecessary allocation.
 | 	// TODO(beorn7): Since we return &c, this method might cause an unnecessary allocation.
 | ||||||
| 	if c.len() == 0 { | 	if c.Len() == 0 { | ||||||
| 		return c.addFirstSample(s), nil | 		return c.addFirstSample(s), nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	tb := c.timeBytes() | 	tb := c.timeBytes() | ||||||
| 	vb := c.valueBytes() | 	vb := c.valueBytes() | ||||||
| 
 | 
 | ||||||
| 	if c.len() == 1 { | 	if c.Len() == 1 { | ||||||
| 		return c.addSecondSample(s, tb, vb) | 		return c.addSecondSample(s, tb, vb) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -103,10 +103,10 @@ func (c doubleDeltaEncodedChunk) Add(s model.SamplePair) ([]Chunk, error) { | ||||||
| 		return addToOverflowChunk(&c, s) | 		return addToOverflowChunk(&c, s) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	projectedTime := c.baseTime() + model.Time(c.len())*c.baseTimeDelta() | 	projectedTime := c.baseTime() + model.Time(c.Len())*c.baseTimeDelta() | ||||||
| 	ddt := s.Timestamp - projectedTime | 	ddt := s.Timestamp - projectedTime | ||||||
| 
 | 
 | ||||||
| 	projectedValue := c.baseValue() + model.SampleValue(c.len())*c.baseValueDelta() | 	projectedValue := c.baseValue() + model.SampleValue(c.Len())*c.baseValueDelta() | ||||||
| 	ddv := s.Value - projectedValue | 	ddv := s.Value - projectedValue | ||||||
| 
 | 
 | ||||||
| 	ntb, nvb, nInt := tb, vb, c.isInt() | 	ntb, nvb, nInt := tb, vb, c.isInt() | ||||||
|  | @ -198,7 +198,7 @@ func (c doubleDeltaEncodedChunk) FirstTime() model.Time { | ||||||
| 
 | 
 | ||||||
| // NewIterator( implements chunk.
 | // NewIterator( implements chunk.
 | ||||||
| func (c *doubleDeltaEncodedChunk) NewIterator() Iterator { | func (c *doubleDeltaEncodedChunk) NewIterator() Iterator { | ||||||
| 	return newIndexAccessingChunkIterator(c.len(), &doubleDeltaEncodedIndexAccessor{ | 	return newIndexAccessingChunkIterator(c.Len(), &doubleDeltaEncodedIndexAccessor{ | ||||||
| 		c:      *c, | 		c:      *c, | ||||||
| 		baseT:  c.baseTime(), | 		baseT:  c.baseTime(), | ||||||
| 		baseΔT: c.baseTimeDelta(), | 		baseΔT: c.baseTimeDelta(), | ||||||
|  | @ -279,7 +279,7 @@ func (c doubleDeltaEncodedChunk) Encoding() Encoding { return DoubleDelta } | ||||||
| 
 | 
 | ||||||
| // Utilization implements chunk.
 | // Utilization implements chunk.
 | ||||||
| func (c doubleDeltaEncodedChunk) Utilization() float64 { | func (c doubleDeltaEncodedChunk) Utilization() float64 { | ||||||
| 	return float64(len(c)) / float64(cap(c)) | 	return float64(len(c)-doubleDeltaHeaderIsIntOffset-1) / float64(cap(c)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c doubleDeltaEncodedChunk) baseTime() model.Time { | func (c doubleDeltaEncodedChunk) baseTime() model.Time { | ||||||
|  | @ -336,7 +336,8 @@ func (c doubleDeltaEncodedChunk) sampleSize() int { | ||||||
| 	return int(c.timeBytes() + c.valueBytes()) | 	return int(c.timeBytes() + c.valueBytes()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c doubleDeltaEncodedChunk) len() int { | // Len implements Chunk. Runs in constant time.
 | ||||||
|  | func (c doubleDeltaEncodedChunk) Len() int { | ||||||
| 	if len(c) <= doubleDeltaHeaderIsIntOffset+1 { | 	if len(c) <= doubleDeltaHeaderIsIntOffset+1 { | ||||||
| 		return 0 | 		return 0 | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -328,6 +328,15 @@ func (c varbitChunk) Utilization() float64 { | ||||||
| 	return math.Min(float64(c.nextSampleOffset()/8+15)/float64(cap(c)), 1) | 	return math.Min(float64(c.nextSampleOffset()/8+15)/float64(cap(c)), 1) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Len implements chunk.  Runs in O(n).
 | ||||||
|  | func (c varbitChunk) Len() int { | ||||||
|  | 	it := c.NewIterator() | ||||||
|  | 	i := 0 | ||||||
|  | 	for ; it.Scan(); i++ { | ||||||
|  | 	} | ||||||
|  | 	return i | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // FirstTime implements chunk.
 | // FirstTime implements chunk.
 | ||||||
| func (c varbitChunk) FirstTime() model.Time { | func (c varbitChunk) FirstTime() model.Time { | ||||||
| 	return model.Time( | 	return model.Time( | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue