2017-04-10 11:59:45 -07:00
|
|
|
// Copyright 2017 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.
|
|
|
|
|
2017-05-02 16:32:09 -07:00
|
|
|
// The code in this file was largely written by Damian Gryski as part of
|
|
|
|
// https://github.com/dgryski/go-tsz and published under the license below.
|
2018-04-08 02:28:30 -07:00
|
|
|
// It was modified to accommodate reading from byte slices without modifying
|
2019-10-10 02:47:30 -07:00
|
|
|
// the underlying bytes, which would panic when reading from mmap'd
|
2017-05-02 16:32:09 -07:00
|
|
|
// read-only byte slices.
|
2017-04-28 02:34:35 -07:00
|
|
|
|
|
|
|
// Copyright (c) 2015,2016 Damian Gryski <damian@gryski.com>
|
|
|
|
// All rights reserved.
|
|
|
|
|
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions are met:
|
|
|
|
|
|
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer.
|
|
|
|
//
|
|
|
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
|
|
// and/or other materials provided with the distribution.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
|
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
|
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
2017-11-30 06:34:49 -08:00
|
|
|
package chunkenc
|
2016-11-15 01:33:34 -08:00
|
|
|
|
|
|
|
import (
|
2016-11-29 13:02:58 -08:00
|
|
|
"encoding/binary"
|
2016-11-15 01:33:34 -08:00
|
|
|
"math"
|
2017-11-08 14:18:33 -08:00
|
|
|
"math/bits"
|
2021-06-29 01:37:41 -07:00
|
|
|
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 06:57:07 -07:00
|
|
|
"github.com/prometheus/prometheus/model/histogram"
|
2016-11-15 01:33:34 -08:00
|
|
|
)
|
|
|
|
|
2020-01-23 23:44:52 -08:00
|
|
|
const (
|
|
|
|
chunkCompactCapacityThreshold = 32
|
|
|
|
)
|
|
|
|
|
2016-11-15 01:33:34 -08:00
|
|
|
// XORChunk holds XOR encoded sample data.
|
|
|
|
type XORChunk struct {
|
2019-02-13 14:41:12 -08:00
|
|
|
b bstream
|
2016-11-15 01:33:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewXORChunk returns a new chunk with XOR encoding of the given size.
|
2016-12-31 01:10:27 -08:00
|
|
|
func NewXORChunk() *XORChunk {
|
2016-12-09 11:45:46 -08:00
|
|
|
b := make([]byte, 2, 128)
|
2019-02-13 14:41:12 -08:00
|
|
|
return &XORChunk{b: bstream{stream: b, count: 0}}
|
2016-11-20 07:14:21 -08:00
|
|
|
}
|
|
|
|
|
2017-03-19 09:05:01 -07:00
|
|
|
// Encoding returns the encoding type.
|
2016-12-09 11:45:46 -08:00
|
|
|
func (c *XORChunk) Encoding() Encoding {
|
|
|
|
return EncXOR
|
|
|
|
}
|
|
|
|
|
2016-11-29 13:02:58 -08:00
|
|
|
// Bytes returns the underlying byte slice of the chunk.
|
|
|
|
func (c *XORChunk) Bytes() []byte {
|
2017-01-02 01:34:55 -08:00
|
|
|
return c.b.bytes()
|
2016-11-15 01:33:34 -08:00
|
|
|
}
|
|
|
|
|
2017-07-12 09:31:26 -07:00
|
|
|
// NumSamples returns the number of samples in the chunk.
|
|
|
|
func (c *XORChunk) NumSamples() int {
|
|
|
|
return int(binary.BigEndian.Uint16(c.Bytes()))
|
|
|
|
}
|
|
|
|
|
2021-07-05 06:27:46 -07:00
|
|
|
// Compact implements the Chunk interface.
|
2020-01-23 23:44:52 -08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-15 01:33:34 -08:00
|
|
|
// Appender implements the Chunk interface.
|
2016-11-29 13:02:58 -08:00
|
|
|
func (c *XORChunk) Appender() (Appender, error) {
|
2019-07-09 02:49:34 -07:00
|
|
|
it := c.iterator(nil)
|
2016-11-29 13:02:58 -08:00
|
|
|
|
|
|
|
// To get an appender we must know the state it would have if we had
|
|
|
|
// appended all existing data from scratch.
|
|
|
|
// We iterate through the end and populate via the iterator's state.
|
|
|
|
for it.Next() {
|
|
|
|
}
|
|
|
|
if err := it.Err(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-11-30 14:01:01 -08:00
|
|
|
a := &xorAppender{
|
2019-02-13 14:41:12 -08:00
|
|
|
b: &c.b,
|
2016-11-29 13:02:58 -08:00
|
|
|
t: it.t,
|
|
|
|
v: it.val,
|
|
|
|
tDelta: it.tDelta,
|
|
|
|
leading: it.leading,
|
|
|
|
trailing: it.trailing,
|
2016-11-30 14:01:01 -08:00
|
|
|
}
|
2021-10-15 11:33:14 -07:00
|
|
|
if it.numTotal == 0 {
|
2016-11-30 14:01:01 -08:00
|
|
|
a.leading = 0xff
|
|
|
|
}
|
|
|
|
return a, nil
|
2016-11-29 13:02:58 -08:00
|
|
|
}
|
|
|
|
|
2019-07-09 02:49:34 -07:00
|
|
|
func (c *XORChunk) iterator(it Iterator) *xorIterator {
|
2016-11-29 13:02:58 -08:00
|
|
|
// Should iterators guarantee to act on a copy of the data so it doesn't lock append?
|
|
|
|
// When using striped locks to guard access to chunks, probably yes.
|
|
|
|
// Could only copy data if the chunk is not completed yet.
|
2019-07-09 02:49:34 -07:00
|
|
|
if xorIter, ok := it.(*xorIterator); ok {
|
|
|
|
xorIter.Reset(c.b.bytes())
|
|
|
|
return xorIter
|
|
|
|
}
|
2016-11-29 13:02:58 -08:00
|
|
|
return &xorIterator{
|
2019-07-09 02:49:34 -07:00
|
|
|
// The first 2 bytes contain chunk headers.
|
|
|
|
// We skip that for actual samples.
|
2016-12-09 11:45:46 -08:00
|
|
|
br: newBReader(c.b.bytes()[2:]),
|
2017-01-02 01:34:55 -08:00
|
|
|
numTotal: binary.BigEndian.Uint16(c.b.bytes()),
|
2020-03-24 13:15:47 -07:00
|
|
|
t: math.MinInt64,
|
2016-11-29 13:02:58 -08:00
|
|
|
}
|
2016-11-15 01:33:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Iterator implements the Chunk interface.
|
2019-07-09 02:49:34 -07:00
|
|
|
func (c *XORChunk) Iterator(it Iterator) Iterator {
|
|
|
|
return c.iterator(it)
|
2016-11-15 01:33:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
type xorAppender struct {
|
2016-11-29 13:02:58 -08:00
|
|
|
b *bstream
|
2016-11-15 01:33:34 -08:00
|
|
|
|
2016-11-20 07:14:21 -08:00
|
|
|
t int64
|
|
|
|
v float64
|
|
|
|
tDelta uint64
|
2016-11-20 05:33:00 -08:00
|
|
|
|
|
|
|
leading uint8
|
|
|
|
trailing uint8
|
2016-11-15 01:33:34 -08:00
|
|
|
}
|
|
|
|
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 06:57:07 -07:00
|
|
|
func (a *xorAppender) AppendHistogram(t int64, h histogram.Histogram) {
|
|
|
|
panic("appended a histogram to an xor chunk")
|
2021-06-29 01:37:41 -07:00
|
|
|
}
|
|
|
|
|
2016-12-31 01:10:27 -08:00
|
|
|
func (a *xorAppender) Append(t int64, v float64) {
|
2016-11-20 07:14:21 -08:00
|
|
|
var tDelta uint64
|
2017-01-02 01:34:55 -08:00
|
|
|
num := binary.BigEndian.Uint16(a.b.bytes())
|
2016-11-20 07:14:21 -08:00
|
|
|
|
2017-01-02 01:34:55 -08:00
|
|
|
if num == 0 {
|
2016-11-30 13:10:10 -08:00
|
|
|
buf := make([]byte, binary.MaxVarintLen64)
|
|
|
|
for _, b := range buf[:binary.PutVarint(buf, t)] {
|
|
|
|
a.b.writeByte(b)
|
|
|
|
}
|
2016-11-29 13:02:58 -08:00
|
|
|
a.b.writeBits(math.Float64bits(v), 64)
|
2016-11-20 05:33:00 -08:00
|
|
|
|
2017-01-02 01:34:55 -08:00
|
|
|
} else if num == 1 {
|
2016-11-20 07:14:21 -08:00
|
|
|
tDelta = uint64(t - a.t)
|
2016-11-30 13:10:10 -08:00
|
|
|
|
|
|
|
buf := make([]byte, binary.MaxVarintLen64)
|
|
|
|
for _, b := range buf[:binary.PutUvarint(buf, tDelta)] {
|
|
|
|
a.b.writeByte(b)
|
|
|
|
}
|
|
|
|
|
2016-11-20 07:14:21 -08:00
|
|
|
a.writeVDelta(v)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
tDelta = uint64(t - a.t)
|
2016-11-20 05:33:00 -08:00
|
|
|
dod := int64(tDelta - a.tDelta)
|
|
|
|
|
|
|
|
// Gorilla has a max resolution of seconds, Prometheus milliseconds.
|
|
|
|
// Thus we use higher value range steps with larger bit size.
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 06:57:07 -07:00
|
|
|
//
|
|
|
|
// TODO(beorn7): This seems to needlessly jump to large bit
|
|
|
|
// sizes even for very small deviations from zero. Timestamp
|
|
|
|
// compression can probably benefit from some smaller bit
|
|
|
|
// buckets. See also what was done for histogram encoding in
|
|
|
|
// varbit.go.
|
2016-11-20 05:33:00 -08:00
|
|
|
switch {
|
|
|
|
case dod == 0:
|
2016-11-29 13:02:58 -08:00
|
|
|
a.b.writeBit(zero)
|
2016-12-07 06:37:37 -08:00
|
|
|
case bitRange(dod, 14):
|
2021-07-05 07:39:24 -07:00
|
|
|
a.b.writeBits(0b10, 2)
|
2016-11-29 13:02:58 -08:00
|
|
|
a.b.writeBits(uint64(dod), 14)
|
2016-12-07 06:37:37 -08:00
|
|
|
case bitRange(dod, 17):
|
2021-07-05 07:39:24 -07:00
|
|
|
a.b.writeBits(0b110, 3)
|
2016-11-29 13:02:58 -08:00
|
|
|
a.b.writeBits(uint64(dod), 17)
|
2016-12-07 06:37:37 -08:00
|
|
|
case bitRange(dod, 20):
|
2021-07-05 07:39:24 -07:00
|
|
|
a.b.writeBits(0b1110, 4)
|
2016-11-29 13:02:58 -08:00
|
|
|
a.b.writeBits(uint64(dod), 20)
|
2016-11-20 05:33:00 -08:00
|
|
|
default:
|
2021-07-05 07:39:24 -07:00
|
|
|
a.b.writeBits(0b1111, 4)
|
2016-11-29 13:02:58 -08:00
|
|
|
a.b.writeBits(uint64(dod), 64)
|
2016-11-15 01:33:34 -08:00
|
|
|
}
|
2016-11-20 05:33:00 -08:00
|
|
|
|
|
|
|
a.writeVDelta(v)
|
2016-11-15 01:33:34 -08:00
|
|
|
}
|
|
|
|
|
2016-11-20 05:33:00 -08:00
|
|
|
a.t = t
|
|
|
|
a.v = v
|
2017-01-02 01:34:55 -08:00
|
|
|
binary.BigEndian.PutUint16(a.b.bytes(), num+1)
|
2016-11-20 07:14:21 -08:00
|
|
|
a.tDelta = tDelta
|
2016-11-15 01:33:34 -08:00
|
|
|
}
|
|
|
|
|
2016-12-07 06:37:37 -08:00
|
|
|
func bitRange(x int64, nbits uint8) bool {
|
|
|
|
return -((1<<(nbits-1))-1) <= x && x <= 1<<(nbits-1)
|
|
|
|
}
|
|
|
|
|
2016-11-20 05:33:00 -08:00
|
|
|
func (a *xorAppender) writeVDelta(v float64) {
|
|
|
|
vDelta := math.Float64bits(v) ^ math.Float64bits(a.v)
|
|
|
|
|
|
|
|
if vDelta == 0 {
|
2016-11-29 13:02:58 -08:00
|
|
|
a.b.writeBit(zero)
|
2016-11-20 05:33:00 -08:00
|
|
|
return
|
|
|
|
}
|
2016-11-29 13:02:58 -08:00
|
|
|
a.b.writeBit(one)
|
2016-11-20 05:33:00 -08:00
|
|
|
|
2017-11-08 14:18:33 -08:00
|
|
|
leading := uint8(bits.LeadingZeros64(vDelta))
|
|
|
|
trailing := uint8(bits.TrailingZeros64(vDelta))
|
2016-11-20 05:33:00 -08:00
|
|
|
|
2016-11-30 14:01:01 -08:00
|
|
|
// Clamp number of leading zeros to avoid overflow when encoding.
|
2016-11-20 05:33:00 -08:00
|
|
|
if leading >= 32 {
|
|
|
|
leading = 31
|
|
|
|
}
|
|
|
|
|
2016-11-30 14:01:01 -08:00
|
|
|
if a.leading != 0xff && leading >= a.leading && trailing >= a.trailing {
|
2016-11-29 13:02:58 -08:00
|
|
|
a.b.writeBit(zero)
|
|
|
|
a.b.writeBits(vDelta>>a.trailing, 64-int(a.leading)-int(a.trailing))
|
2016-11-20 05:33:00 -08:00
|
|
|
} else {
|
|
|
|
a.leading, a.trailing = leading, trailing
|
|
|
|
|
2016-11-29 13:02:58 -08:00
|
|
|
a.b.writeBit(one)
|
|
|
|
a.b.writeBits(uint64(leading), 5)
|
2016-11-20 05:33:00 -08:00
|
|
|
|
|
|
|
// Note that if leading == trailing == 0, then sigbits == 64. But that value doesn't actually fit into the 6 bits we have.
|
|
|
|
// Luckily, we never need to encode 0 significant bits, since that would put us in the other case (vdelta == 0).
|
|
|
|
// So instead we write out a 0 and adjust it back to 64 on unpacking.
|
|
|
|
sigbits := 64 - leading - trailing
|
2016-11-29 13:02:58 -08:00
|
|
|
a.b.writeBits(uint64(sigbits), 6)
|
|
|
|
a.b.writeBits(vDelta>>trailing, int(sigbits))
|
2016-11-20 05:33:00 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-15 01:33:34 -08:00
|
|
|
type xorIterator struct {
|
2020-06-15 08:44:40 -07:00
|
|
|
br bstreamReader
|
2016-11-20 05:33:00 -08:00
|
|
|
numTotal uint16
|
|
|
|
numRead uint16
|
|
|
|
|
|
|
|
t int64
|
|
|
|
val float64
|
|
|
|
|
|
|
|
leading uint8
|
|
|
|
trailing uint8
|
|
|
|
|
2016-11-29 13:02:58 -08:00
|
|
|
tDelta uint64
|
2016-11-20 05:33:00 -08:00
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
2020-02-06 07:58:38 -08:00
|
|
|
func (it *xorIterator) Seek(t int64) bool {
|
|
|
|
if it.err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
for t > it.t || it.numRead == 0 {
|
|
|
|
if !it.Next() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2017-01-02 04:27:52 -08:00
|
|
|
func (it *xorIterator) At() (int64, float64) {
|
2016-11-20 05:33:00 -08:00
|
|
|
return it.t, it.val
|
|
|
|
}
|
|
|
|
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 06:57:07 -07:00
|
|
|
func (it *xorIterator) AtHistogram() (int64, histogram.Histogram) {
|
2021-06-29 07:38:46 -07:00
|
|
|
panic("cannot call xorIterator.AtHistogram().")
|
|
|
|
}
|
|
|
|
|
2021-06-30 07:48:13 -07:00
|
|
|
func (it *xorIterator) ChunkEncoding() Encoding {
|
|
|
|
return EncXOR
|
|
|
|
}
|
|
|
|
|
2016-11-29 13:02:58 -08:00
|
|
|
func (it *xorIterator) Err() error {
|
|
|
|
return it.err
|
|
|
|
}
|
|
|
|
|
2019-07-09 02:49:34 -07:00
|
|
|
func (it *xorIterator) Reset(b []byte) {
|
|
|
|
// The first 2 bytes contain chunk headers.
|
|
|
|
// We skip that for actual samples.
|
|
|
|
it.br = newBReader(b[2:])
|
|
|
|
it.numTotal = binary.BigEndian.Uint16(b)
|
|
|
|
|
|
|
|
it.numRead = 0
|
|
|
|
it.t = 0
|
|
|
|
it.val = 0
|
|
|
|
it.leading = 0
|
|
|
|
it.trailing = 0
|
|
|
|
it.tDelta = 0
|
|
|
|
it.err = nil
|
|
|
|
}
|
|
|
|
|
2016-11-29 13:02:58 -08:00
|
|
|
func (it *xorIterator) Next() bool {
|
2016-11-20 05:33:00 -08:00
|
|
|
if it.err != nil || it.numRead == it.numTotal {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if it.numRead == 0 {
|
2018-11-21 23:51:57 -08:00
|
|
|
t, err := binary.ReadVarint(&it.br)
|
2016-11-20 05:33:00 -08:00
|
|
|
if err != nil {
|
|
|
|
it.err = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
v, err := it.br.readBits(64)
|
|
|
|
if err != nil {
|
|
|
|
it.err = err
|
|
|
|
return false
|
|
|
|
}
|
2018-03-21 13:23:47 -07:00
|
|
|
it.t = t
|
2016-11-20 05:33:00 -08:00
|
|
|
it.val = math.Float64frombits(v)
|
|
|
|
|
|
|
|
it.numRead++
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if it.numRead == 1 {
|
2018-11-21 23:51:57 -08:00
|
|
|
tDelta, err := binary.ReadUvarint(&it.br)
|
2016-11-20 05:33:00 -08:00
|
|
|
if err != nil {
|
|
|
|
it.err = err
|
|
|
|
return false
|
|
|
|
}
|
2016-11-29 13:02:58 -08:00
|
|
|
it.tDelta = tDelta
|
|
|
|
it.t = it.t + int64(it.tDelta)
|
2016-11-20 05:33:00 -08:00
|
|
|
|
2016-11-29 13:02:58 -08:00
|
|
|
return it.readValue()
|
2016-11-20 05:33:00 -08:00
|
|
|
}
|
|
|
|
|
2016-11-29 13:02:58 -08:00
|
|
|
var d byte
|
2016-11-20 05:33:00 -08:00
|
|
|
// read delta-of-delta
|
|
|
|
for i := 0; i < 4; i++ {
|
|
|
|
d <<= 1
|
2020-06-15 08:44:40 -07:00
|
|
|
bit, err := it.br.readBitFast()
|
|
|
|
if err != nil {
|
|
|
|
bit, err = it.br.readBit()
|
|
|
|
}
|
2016-11-20 05:33:00 -08:00
|
|
|
if err != nil {
|
|
|
|
it.err = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if bit == zero {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
d |= 1
|
|
|
|
}
|
2016-11-29 13:02:58 -08:00
|
|
|
var sz uint8
|
|
|
|
var dod int64
|
2016-11-20 05:33:00 -08:00
|
|
|
switch d {
|
2021-07-05 07:39:24 -07:00
|
|
|
case 0b0:
|
2016-11-20 05:33:00 -08:00
|
|
|
// dod == 0
|
2021-07-05 07:39:24 -07:00
|
|
|
case 0b10:
|
2016-11-20 05:33:00 -08:00
|
|
|
sz = 14
|
2021-07-05 07:39:24 -07:00
|
|
|
case 0b110:
|
2016-11-20 05:33:00 -08:00
|
|
|
sz = 17
|
2021-07-05 07:39:24 -07:00
|
|
|
case 0b1110:
|
2016-11-20 05:33:00 -08:00
|
|
|
sz = 20
|
2021-07-05 07:39:24 -07:00
|
|
|
case 0b1111:
|
2020-06-15 08:44:40 -07:00
|
|
|
// Do not use fast because it's very unlikely it will succeed.
|
2016-11-20 05:33:00 -08:00
|
|
|
bits, err := it.br.readBits(64)
|
|
|
|
if err != nil {
|
|
|
|
it.err = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-11-29 13:02:58 -08:00
|
|
|
dod = int64(bits)
|
2016-11-20 05:33:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if sz != 0 {
|
2020-06-15 08:44:40 -07:00
|
|
|
bits, err := it.br.readBitsFast(sz)
|
|
|
|
if err != nil {
|
|
|
|
bits, err = it.br.readBits(sz)
|
|
|
|
}
|
2016-11-20 05:33:00 -08:00
|
|
|
if err != nil {
|
|
|
|
it.err = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if bits > (1 << (sz - 1)) {
|
|
|
|
// or something
|
|
|
|
bits = bits - (1 << sz)
|
|
|
|
}
|
2016-11-29 13:02:58 -08:00
|
|
|
dod = int64(bits)
|
2016-11-20 05:33:00 -08:00
|
|
|
}
|
|
|
|
|
2016-11-29 13:02:58 -08:00
|
|
|
it.tDelta = uint64(int64(it.tDelta) + dod)
|
|
|
|
it.t = it.t + int64(it.tDelta)
|
2016-11-20 05:33:00 -08:00
|
|
|
|
2016-11-29 13:02:58 -08:00
|
|
|
return it.readValue()
|
|
|
|
}
|
2016-11-20 05:33:00 -08:00
|
|
|
|
2016-11-29 13:02:58 -08:00
|
|
|
func (it *xorIterator) readValue() bool {
|
2020-06-15 08:44:40 -07:00
|
|
|
bit, err := it.br.readBitFast()
|
|
|
|
if err != nil {
|
|
|
|
bit, err = it.br.readBit()
|
|
|
|
}
|
2016-11-20 05:33:00 -08:00
|
|
|
if err != nil {
|
|
|
|
it.err = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if bit == zero {
|
|
|
|
// it.val = it.val
|
|
|
|
} else {
|
2020-06-15 08:44:40 -07:00
|
|
|
bit, err := it.br.readBitFast()
|
|
|
|
if err != nil {
|
|
|
|
bit, err = it.br.readBit()
|
|
|
|
}
|
2016-11-29 13:02:58 -08:00
|
|
|
if err != nil {
|
2016-11-20 05:33:00 -08:00
|
|
|
it.err = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if bit == zero {
|
|
|
|
// reuse leading/trailing zero bits
|
|
|
|
// it.leading, it.trailing = it.leading, it.trailing
|
|
|
|
} else {
|
2020-06-15 08:44:40 -07:00
|
|
|
bits, err := it.br.readBitsFast(5)
|
|
|
|
if err != nil {
|
|
|
|
bits, err = it.br.readBits(5)
|
|
|
|
}
|
2016-11-20 05:33:00 -08:00
|
|
|
if err != nil {
|
|
|
|
it.err = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
it.leading = uint8(bits)
|
|
|
|
|
2020-06-15 08:44:40 -07:00
|
|
|
bits, err = it.br.readBitsFast(6)
|
|
|
|
if err != nil {
|
|
|
|
bits, err = it.br.readBits(6)
|
|
|
|
}
|
2016-11-20 05:33:00 -08:00
|
|
|
if err != nil {
|
|
|
|
it.err = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
mbits := uint8(bits)
|
|
|
|
// 0 significant bits here means we overflowed and we actually need 64; see comment in encoder
|
|
|
|
if mbits == 0 {
|
|
|
|
mbits = 64
|
|
|
|
}
|
|
|
|
it.trailing = 64 - it.leading - mbits
|
|
|
|
}
|
|
|
|
|
2020-06-15 08:44:40 -07:00
|
|
|
mbits := 64 - it.leading - it.trailing
|
|
|
|
bits, err := it.br.readBitsFast(mbits)
|
|
|
|
if err != nil {
|
|
|
|
bits, err = it.br.readBits(mbits)
|
|
|
|
}
|
2016-11-20 05:33:00 -08:00
|
|
|
if err != nil {
|
|
|
|
it.err = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
vbits := math.Float64bits(it.val)
|
2020-06-15 08:44:40 -07:00
|
|
|
vbits ^= bits << it.trailing
|
2016-11-20 05:33:00 -08:00
|
|
|
it.val = math.Float64frombits(vbits)
|
|
|
|
}
|
|
|
|
|
|
|
|
it.numRead++
|
|
|
|
return true
|
2016-11-15 01:33:34 -08:00
|
|
|
}
|