1
0
mirror of https://github.com/Oxalide/vsphere-influxdb-go.git synced 2023-10-10 13:36:51 +02:00
vsphere-influxdb-go/vendor/github.com/influxdata/influxdb/tsdb/index/tsi1/tag_block.go
2017-10-25 20:52:40 +00:00

753 lines
18 KiB
Go

package tsi1
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"github.com/influxdata/influxdb/pkg/rhh"
)
// TagBlockVersion is the version of the tag block.
const TagBlockVersion = 1
// Tag key flag constants.
const (
TagKeyTombstoneFlag = 0x01
)
// Tag value flag constants.
const (
TagValueTombstoneFlag = 0x01
)
// TagBlock variable size constants.
const (
// TagBlock key block fields.
TagKeyNSize = 8
TagKeyOffsetSize = 8
// TagBlock value block fields.
TagValueNSize = 8
TagValueOffsetSize = 8
)
// TagBlock errors.
var (
ErrUnsupportedTagBlockVersion = errors.New("unsupported tag block version")
ErrTagBlockSizeMismatch = errors.New("tag block size mismatch")
)
// TagBlock represents tag key/value block for a single measurement.
type TagBlock struct {
data []byte
valueData []byte
keyData []byte
hashData []byte
version int // tag block version
}
// Version returns the encoding version parsed from the data.
// Only valid after UnmarshalBinary() has been successfully invoked.
func (blk *TagBlock) Version() int { return blk.version }
// UnmarshalBinary unpacks data into the tag block. Tag block is not copied so data
// should be retained and unchanged after being passed into this function.
func (blk *TagBlock) UnmarshalBinary(data []byte) error {
// Read trailer.
t, err := ReadTagBlockTrailer(data)
if err != nil {
return err
}
// Verify data size is correct.
if int64(len(data)) != t.Size {
return ErrTagBlockSizeMismatch
}
// Save data section.
blk.valueData = data[t.ValueData.Offset:]
blk.valueData = blk.valueData[:t.ValueData.Size]
// Save key data section.
blk.keyData = data[t.KeyData.Offset:]
blk.keyData = blk.keyData[:t.KeyData.Size]
// Save hash index block.
blk.hashData = data[t.HashIndex.Offset:]
blk.hashData = blk.hashData[:t.HashIndex.Size]
// Save entire block.
blk.data = data
return nil
}
// TagKeyElem returns an element for a tag key.
// Returns an element with a nil key if not found.
func (blk *TagBlock) TagKeyElem(key []byte) TagKeyElem {
keyN := int64(binary.BigEndian.Uint64(blk.hashData[:TagKeyNSize]))
hash := rhh.HashKey(key)
pos := hash % keyN
// Track current distance
var d int64
for {
// Find offset of tag key.
offset := binary.BigEndian.Uint64(blk.hashData[TagKeyNSize+(pos*TagKeyOffsetSize):])
if offset == 0 {
return nil
}
// Parse into element.
var e TagBlockKeyElem
e.unmarshal(blk.data[offset:], blk.data)
// Return if keys match.
if bytes.Equal(e.key, key) {
return &e
}
// Check if we've exceeded the probe distance.
if d > rhh.Dist(rhh.HashKey(e.key), pos, keyN) {
return nil
}
// Move position forward.
pos = (pos + 1) % keyN
d++
if d > keyN {
return nil
}
}
}
// TagValueElem returns an element for a tag value.
func (blk *TagBlock) TagValueElem(key, value []byte) TagValueElem {
// Find key element, exit if not found.
kelem, _ := blk.TagKeyElem(key).(*TagBlockKeyElem)
if kelem == nil {
return nil
}
// Slice hash index data.
hashData := kelem.hashIndex.buf
valueN := int64(binary.BigEndian.Uint64(hashData[:TagValueNSize]))
hash := rhh.HashKey(value)
pos := hash % valueN
// Track current distance
var d int64
for {
// Find offset of tag value.
offset := binary.BigEndian.Uint64(hashData[TagValueNSize+(pos*TagValueOffsetSize):])
if offset == 0 {
return nil
}
// Parse into element.
var e TagBlockValueElem
e.unmarshal(blk.data[offset:])
// Return if values match.
if bytes.Equal(e.value, value) {
return &e
}
// Check if we've exceeded the probe distance.
max := rhh.Dist(rhh.HashKey(e.value), pos, valueN)
if d > max {
return nil
}
// Move position forward.
pos = (pos + 1) % valueN
d++
if d > valueN {
return nil
}
}
}
// TagKeyIterator returns an iterator over all the keys in the block.
func (blk *TagBlock) TagKeyIterator() TagKeyIterator {
return &tagBlockKeyIterator{
blk: blk,
keyData: blk.keyData,
}
}
// tagBlockKeyIterator represents an iterator over all keys in a TagBlock.
type tagBlockKeyIterator struct {
blk *TagBlock
keyData []byte
e TagBlockKeyElem
}
// Next returns the next element in the iterator.
func (itr *tagBlockKeyIterator) Next() TagKeyElem {
// Exit when there is no data left.
if len(itr.keyData) == 0 {
return nil
}
// Unmarshal next element & move data forward.
itr.e.unmarshal(itr.keyData, itr.blk.data)
itr.keyData = itr.keyData[itr.e.size:]
assert(len(itr.e.Key()) > 0, "invalid zero-length tag key")
return &itr.e
}
// tagBlockValueIterator represents an iterator over all values for a tag key.
type tagBlockValueIterator struct {
data []byte
e TagBlockValueElem
}
// Next returns the next element in the iterator.
func (itr *tagBlockValueIterator) Next() TagValueElem {
// Exit when there is no data left.
if len(itr.data) == 0 {
return nil
}
// Unmarshal next element & move data forward.
itr.e.unmarshal(itr.data)
itr.data = itr.data[itr.e.size:]
assert(len(itr.e.Value()) > 0, "invalid zero-length tag value")
return &itr.e
}
// TagBlockKeyElem represents a tag key element in a TagBlock.
type TagBlockKeyElem struct {
flag byte
key []byte
// Value data
data struct {
offset uint64
size uint64
buf []byte
}
// Value hash index data
hashIndex struct {
offset uint64
size uint64
buf []byte
}
size int
// Reusable iterator.
itr tagBlockValueIterator
}
// Deleted returns true if the key has been tombstoned.
func (e *TagBlockKeyElem) Deleted() bool { return (e.flag & TagKeyTombstoneFlag) != 0 }
// Key returns the key name of the element.
func (e *TagBlockKeyElem) Key() []byte { return e.key }
// TagValueIterator returns an iterator over the key's values.
func (e *TagBlockKeyElem) TagValueIterator() TagValueIterator {
return &tagBlockValueIterator{data: e.data.buf}
}
// unmarshal unmarshals buf into e.
// The data argument represents the entire block data.
func (e *TagBlockKeyElem) unmarshal(buf, data []byte) {
start := len(buf)
// Parse flag data.
e.flag, buf = buf[0], buf[1:]
// Parse data offset/size.
e.data.offset, buf = binary.BigEndian.Uint64(buf), buf[8:]
e.data.size, buf = binary.BigEndian.Uint64(buf), buf[8:]
// Slice data.
e.data.buf = data[e.data.offset:]
e.data.buf = e.data.buf[:e.data.size]
// Parse hash index offset/size.
e.hashIndex.offset, buf = binary.BigEndian.Uint64(buf), buf[8:]
e.hashIndex.size, buf = binary.BigEndian.Uint64(buf), buf[8:]
// Slice hash index data.
e.hashIndex.buf = data[e.hashIndex.offset:]
e.hashIndex.buf = e.hashIndex.buf[:e.hashIndex.size]
// Parse key.
n, sz := binary.Uvarint(buf)
e.key, buf = buf[sz:sz+int(n)], buf[int(n)+sz:]
// Save length of elem.
e.size = start - len(buf)
}
// TagBlockValueElem represents a tag value element.
type TagBlockValueElem struct {
flag byte
value []byte
series struct {
n uint32 // Series count
data []byte // Raw series data
}
size int
}
// Deleted returns true if the element has been tombstoned.
func (e *TagBlockValueElem) Deleted() bool { return (e.flag & TagValueTombstoneFlag) != 0 }
// Value returns the value for the element.
func (e *TagBlockValueElem) Value() []byte { return e.value }
// SeriesN returns the series count.
func (e *TagBlockValueElem) SeriesN() uint32 { return e.series.n }
// SeriesData returns the raw series data.
func (e *TagBlockValueElem) SeriesData() []byte { return e.series.data }
// SeriesID returns series ID at an index.
func (e *TagBlockValueElem) SeriesID(i int) uint32 {
return binary.BigEndian.Uint32(e.series.data[i*SeriesIDSize:])
}
// SeriesIDs returns a list decoded series ids.
func (e *TagBlockValueElem) SeriesIDs() []uint32 {
a := make([]uint32, 0, e.series.n)
var prev uint32
for data := e.series.data; len(data) > 0; {
delta, n := binary.Uvarint(data)
data = data[n:]
seriesID := prev + uint32(delta)
a = append(a, seriesID)
prev = seriesID
}
return a
}
// Size returns the size of the element.
func (e *TagBlockValueElem) Size() int { return e.size }
// unmarshal unmarshals buf into e.
func (e *TagBlockValueElem) unmarshal(buf []byte) {
start := len(buf)
// Parse flag data.
e.flag, buf = buf[0], buf[1:]
// Parse value.
sz, n := binary.Uvarint(buf)
e.value, buf = buf[n:n+int(sz)], buf[n+int(sz):]
// Parse series count.
v, n := binary.Uvarint(buf)
e.series.n = uint32(v)
buf = buf[n:]
// Parse data block size.
sz, n = binary.Uvarint(buf)
buf = buf[n:]
// Save reference to series data.
e.series.data = buf[:sz]
buf = buf[sz:]
// Save length of elem.
e.size = start - len(buf)
}
// TagBlockTrailerSize is the total size of the on-disk trailer.
const TagBlockTrailerSize = 0 +
8 + 8 + // value data offset/size
8 + 8 + // key data offset/size
8 + 8 + // hash index offset/size
8 + // size
2 // version
// TagBlockTrailer represents meta data at the end of a TagBlock.
type TagBlockTrailer struct {
Version int // Encoding version
Size int64 // Total size w/ trailer
// Offset & size of value data section.
ValueData struct {
Offset int64
Size int64
}
// Offset & size of key data section.
KeyData struct {
Offset int64
Size int64
}
// Offset & size of hash map section.
HashIndex struct {
Offset int64
Size int64
}
}
// WriteTo writes the trailer to w.
func (t *TagBlockTrailer) WriteTo(w io.Writer) (n int64, err error) {
// Write data info.
if err := writeUint64To(w, uint64(t.ValueData.Offset), &n); err != nil {
return n, err
} else if err := writeUint64To(w, uint64(t.ValueData.Size), &n); err != nil {
return n, err
}
// Write key data info.
if err := writeUint64To(w, uint64(t.KeyData.Offset), &n); err != nil {
return n, err
} else if err := writeUint64To(w, uint64(t.KeyData.Size), &n); err != nil {
return n, err
}
// Write hash index info.
if err := writeUint64To(w, uint64(t.HashIndex.Offset), &n); err != nil {
return n, err
} else if err := writeUint64To(w, uint64(t.HashIndex.Size), &n); err != nil {
return n, err
}
// Write total size & encoding version.
if err := writeUint64To(w, uint64(t.Size), &n); err != nil {
return n, err
} else if err := writeUint16To(w, IndexFileVersion, &n); err != nil {
return n, err
}
return n, nil
}
// ReadTagBlockTrailer returns the tag block trailer from data.
func ReadTagBlockTrailer(data []byte) (TagBlockTrailer, error) {
var t TagBlockTrailer
// Read version.
t.Version = int(binary.BigEndian.Uint16(data[len(data)-2:]))
if t.Version != TagBlockVersion {
return t, ErrUnsupportedTagBlockVersion
}
// Slice trailer data.
buf := data[len(data)-TagBlockTrailerSize:]
// Read data section info.
t.ValueData.Offset, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
t.ValueData.Size, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
// Read key section info.
t.KeyData.Offset, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
t.KeyData.Size, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
// Read hash section info.
t.HashIndex.Offset, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
t.HashIndex.Size, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
// Read total size.
t.Size, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
return t, nil
}
// TagBlockEncoder encodes a tags to a TagBlock section.
type TagBlockEncoder struct {
w io.Writer
buf bytes.Buffer
// Track value offsets.
offsets *rhh.HashMap
// Track bytes written, sections.
n int64
trailer TagBlockTrailer
// Track tag keys.
keys []tagKeyEncodeEntry
}
// NewTagBlockEncoder returns a new TagBlockEncoder.
func NewTagBlockEncoder(w io.Writer) *TagBlockEncoder {
return &TagBlockEncoder{
w: w,
offsets: rhh.NewHashMap(rhh.Options{LoadFactor: LoadFactor}),
trailer: TagBlockTrailer{
Version: TagBlockVersion,
},
}
}
// N returns the number of bytes written.
func (enc *TagBlockEncoder) N() int64 { return enc.n }
// EncodeKey writes a tag key to the underlying writer.
func (enc *TagBlockEncoder) EncodeKey(key []byte, deleted bool) error {
// An initial empty byte must be written.
if err := enc.ensureHeaderWritten(); err != nil {
return err
}
// Verify key is lexicographically after previous key.
if len(enc.keys) > 0 {
prev := enc.keys[len(enc.keys)-1].key
if cmp := bytes.Compare(prev, key); cmp == 1 {
return fmt.Errorf("tag key out of order: prev=%s, new=%s", prev, key)
} else if cmp == 0 {
return fmt.Errorf("tag key already encoded: %s", key)
}
}
// Flush values section for key.
if err := enc.flushValueHashIndex(); err != nil {
return err
}
// Append key on to the end of the key list.
entry := tagKeyEncodeEntry{
key: key,
deleted: deleted,
}
entry.data.offset = enc.n
enc.keys = append(enc.keys, entry)
return nil
}
// EncodeValue writes a tag value to the underlying writer.
// The tag key must be lexicographical sorted after the previous encoded tag key.
func (enc *TagBlockEncoder) EncodeValue(value []byte, deleted bool, seriesIDs []uint32) error {
if len(enc.keys) == 0 {
return fmt.Errorf("tag key must be encoded before encoding values")
} else if len(value) == 0 {
return fmt.Errorf("zero length tag value not allowed")
}
// Save offset to hash map.
enc.offsets.Put(value, enc.n)
// Write flag.
if err := writeUint8To(enc.w, encodeTagValueFlag(deleted), &enc.n); err != nil {
return err
}
// Write value.
if err := writeUvarintTo(enc.w, uint64(len(value)), &enc.n); err != nil {
return err
} else if err := writeTo(enc.w, value, &enc.n); err != nil {
return err
}
// Build series data in buffer.
enc.buf.Reset()
var prev uint32
for _, seriesID := range seriesIDs {
delta := seriesID - prev
var buf [binary.MaxVarintLen32]byte
i := binary.PutUvarint(buf[:], uint64(delta))
if _, err := enc.buf.Write(buf[:i]); err != nil {
return err
}
prev = seriesID
}
// Write series count.
if err := writeUvarintTo(enc.w, uint64(len(seriesIDs)), &enc.n); err != nil {
return err
}
// Write data size & buffer.
if err := writeUvarintTo(enc.w, uint64(enc.buf.Len()), &enc.n); err != nil {
return err
}
nn, err := enc.buf.WriteTo(enc.w)
if enc.n += nn; err != nil {
return err
}
return nil
}
// Close flushes the trailer of the encoder to the writer.
func (enc *TagBlockEncoder) Close() error {
// Flush last value set.
if err := enc.ensureHeaderWritten(); err != nil {
return err
} else if err := enc.flushValueHashIndex(); err != nil {
return err
}
// Save ending position of entire data block.
enc.trailer.ValueData.Size = enc.n - enc.trailer.ValueData.Offset
// Write key block to point to value blocks.
if err := enc.encodeTagKeyBlock(); err != nil {
return err
}
// Compute total size w/ trailer.
enc.trailer.Size = enc.n + TagBlockTrailerSize
// Write trailer.
nn, err := enc.trailer.WriteTo(enc.w)
enc.n += nn
if err != nil {
return err
}
return nil
}
// ensureHeaderWritten writes a single byte to offset the rest of the block.
func (enc *TagBlockEncoder) ensureHeaderWritten() error {
if enc.n > 0 {
return nil
} else if _, err := enc.w.Write([]byte{0}); err != nil {
return err
}
enc.n++
enc.trailer.ValueData.Offset = enc.n
return nil
}
// flushValueHashIndex builds writes the hash map at the end of a value set.
func (enc *TagBlockEncoder) flushValueHashIndex() error {
// Ignore if no keys have been written.
if len(enc.keys) == 0 {
return nil
}
key := &enc.keys[len(enc.keys)-1]
// Save size of data section.
key.data.size = enc.n - key.data.offset
// Encode hash map length.
key.hashIndex.offset = enc.n
if err := writeUint64To(enc.w, uint64(enc.offsets.Cap()), &enc.n); err != nil {
return err
}
// Encode hash map offset entries.
for i := int64(0); i < enc.offsets.Cap(); i++ {
_, v := enc.offsets.Elem(i)
offset, _ := v.(int64)
if err := writeUint64To(enc.w, uint64(offset), &enc.n); err != nil {
return err
}
}
key.hashIndex.size = enc.n - key.hashIndex.offset
// Clear offsets.
enc.offsets = rhh.NewHashMap(rhh.Options{LoadFactor: LoadFactor})
return nil
}
// encodeTagKeyBlock encodes the keys section to the writer.
func (enc *TagBlockEncoder) encodeTagKeyBlock() error {
offsets := rhh.NewHashMap(rhh.Options{Capacity: int64(len(enc.keys)), LoadFactor: LoadFactor})
// Encode key list in sorted order.
enc.trailer.KeyData.Offset = enc.n
for i := range enc.keys {
entry := &enc.keys[i]
// Save current offset so we can use it in the hash index.
offsets.Put(entry.key, enc.n)
if err := writeUint8To(enc.w, encodeTagKeyFlag(entry.deleted), &enc.n); err != nil {
return err
}
// Write value data offset & size.
if err := writeUint64To(enc.w, uint64(entry.data.offset), &enc.n); err != nil {
return err
} else if err := writeUint64To(enc.w, uint64(entry.data.size), &enc.n); err != nil {
return err
}
// Write value hash index offset & size.
if err := writeUint64To(enc.w, uint64(entry.hashIndex.offset), &enc.n); err != nil {
return err
} else if err := writeUint64To(enc.w, uint64(entry.hashIndex.size), &enc.n); err != nil {
return err
}
// Write key length and data.
if err := writeUvarintTo(enc.w, uint64(len(entry.key)), &enc.n); err != nil {
return err
} else if err := writeTo(enc.w, entry.key, &enc.n); err != nil {
return err
}
}
enc.trailer.KeyData.Size = enc.n - enc.trailer.KeyData.Offset
// Encode hash map length.
enc.trailer.HashIndex.Offset = enc.n
if err := writeUint64To(enc.w, uint64(offsets.Cap()), &enc.n); err != nil {
return err
}
// Encode hash map offset entries.
for i := int64(0); i < offsets.Cap(); i++ {
_, v := offsets.Elem(i)
offset, _ := v.(int64)
if err := writeUint64To(enc.w, uint64(offset), &enc.n); err != nil {
return err
}
}
enc.trailer.HashIndex.Size = enc.n - enc.trailer.HashIndex.Offset
return nil
}
type tagKeyEncodeEntry struct {
key []byte
deleted bool
data struct {
offset int64
size int64
}
hashIndex struct {
offset int64
size int64
}
}
func encodeTagKeyFlag(deleted bool) byte {
var flag byte
if deleted {
flag |= TagKeyTombstoneFlag
}
return flag
}
func encodeTagValueFlag(deleted bool) byte {
var flag byte
if deleted {
flag |= TagValueTombstoneFlag
}
return flag
}