vsphere-influxdb-go/vendor/github.com/influxdata/influxdb/tsdb/index/tsi1/index_files.go

363 lines
8.6 KiB
Go

package tsi1
import (
"bufio"
"fmt"
"io"
"os"
"sort"
"time"
"github.com/influxdata/influxdb/pkg/estimator/hll"
"github.com/influxdata/influxdb/pkg/mmap"
)
// IndexFiles represents a layered set of index files.
type IndexFiles []*IndexFile
// IDs returns the ids for all index files.
func (p IndexFiles) IDs() []int {
a := make([]int, len(p))
for i, f := range p {
a[i] = f.ID()
}
return a
}
// Retain adds a reference count to all files.
func (p IndexFiles) Retain() {
for _, f := range p {
f.Retain()
}
}
// Release removes a reference count from all files.
func (p IndexFiles) Release() {
for _, f := range p {
f.Release()
}
}
// Files returns p as a list of File objects.
func (p IndexFiles) Files() []File {
other := make([]File, len(p))
for i, f := range p {
other[i] = f
}
return other
}
// MeasurementNames returns a sorted list of all measurement names for all files.
func (p *IndexFiles) MeasurementNames() [][]byte {
itr := p.MeasurementIterator()
var names [][]byte
for e := itr.Next(); e != nil; e = itr.Next() {
names = append(names, copyBytes(e.Name()))
}
sort.Sort(byteSlices(names))
return names
}
// MeasurementIterator returns an iterator that merges measurements across all files.
func (p IndexFiles) MeasurementIterator() MeasurementIterator {
a := make([]MeasurementIterator, 0, len(p))
for i := range p {
itr := p[i].MeasurementIterator()
if itr == nil {
continue
}
a = append(a, itr)
}
return MergeMeasurementIterators(a...)
}
// TagKeyIterator returns an iterator that merges tag keys across all files.
func (p *IndexFiles) TagKeyIterator(name []byte) (TagKeyIterator, error) {
a := make([]TagKeyIterator, 0, len(*p))
for _, f := range *p {
itr := f.TagKeyIterator(name)
if itr == nil {
continue
}
a = append(a, itr)
}
return MergeTagKeyIterators(a...), nil
}
// SeriesIterator returns an iterator that merges series across all files.
func (p IndexFiles) SeriesIterator() SeriesIterator {
a := make([]SeriesIterator, 0, len(p))
for _, f := range p {
itr := f.SeriesIterator()
if itr == nil {
continue
}
a = append(a, itr)
}
return MergeSeriesIterators(a...)
}
// MeasurementSeriesIterator returns an iterator that merges series across all files.
func (p IndexFiles) MeasurementSeriesIterator(name []byte) SeriesIterator {
a := make([]SeriesIterator, 0, len(p))
for _, f := range p {
itr := f.MeasurementSeriesIterator(name)
if itr == nil {
continue
}
a = append(a, itr)
}
return MergeSeriesIterators(a...)
}
// TagValueSeriesIterator returns an iterator that merges series across all files.
func (p IndexFiles) TagValueSeriesIterator(name, key, value []byte) SeriesIterator {
a := make([]SeriesIterator, 0, len(p))
for i := range p {
itr := p[i].TagValueSeriesIterator(name, key, value)
if itr != nil {
a = append(a, itr)
}
}
return MergeSeriesIterators(a...)
}
// CompactTo merges all index files and writes them to w.
func (p IndexFiles) CompactTo(w io.Writer, m, k uint64) (n int64, err error) {
var t IndexFileTrailer
// Wrap writer in buffered I/O.
bw := bufio.NewWriter(w)
// Setup context object to track shared data for this compaction.
var info indexCompactInfo
info.tagSets = make(map[string]indexTagSetPos)
// Write magic number.
if err := writeTo(bw, []byte(FileSignature), &n); err != nil {
return n, err
}
// Write combined series list.
t.SeriesBlock.Offset = n
if err := p.writeSeriesBlockTo(bw, m, k, &info, &n); err != nil {
return n, err
}
t.SeriesBlock.Size = n - t.SeriesBlock.Offset
// Flush buffer before re-mapping.
if err := bw.Flush(); err != nil {
return n, err
}
// Open series block as memory-mapped data.
sblk, data, err := mapIndexFileSeriesBlock(w)
if data != nil {
defer mmap.Unmap(data)
}
if err != nil {
return n, err
}
info.sblk = sblk
// Write tagset blocks in measurement order.
if err := p.writeTagsetsTo(bw, &info, &n); err != nil {
return n, err
}
// Write measurement block.
t.MeasurementBlock.Offset = n
if err := p.writeMeasurementBlockTo(bw, &info, &n); err != nil {
return n, err
}
t.MeasurementBlock.Size = n - t.MeasurementBlock.Offset
// Write trailer.
nn, err := t.WriteTo(bw)
n += nn
if err != nil {
return n, err
}
// Flush file.
if err := bw.Flush(); err != nil {
return n, err
}
return n, nil
}
func (p IndexFiles) writeSeriesBlockTo(w io.Writer, m, k uint64, info *indexCompactInfo, n *int64) error {
// Estimate series cardinality.
sketch := hll.NewDefaultPlus()
for _, f := range p {
if err := f.MergeSeriesSketches(sketch, sketch); err != nil {
return err
}
}
itr := p.SeriesIterator()
enc := NewSeriesBlockEncoder(w, uint32(sketch.Count()), m, k)
// Write all series.
for e := itr.Next(); e != nil; e = itr.Next() {
if err := enc.Encode(e.Name(), e.Tags(), e.Deleted()); err != nil {
return err
}
}
// Close and flush block.
err := enc.Close()
*n += int64(enc.N())
if err != nil {
return err
}
return nil
}
func (p IndexFiles) writeTagsetsTo(w io.Writer, info *indexCompactInfo, n *int64) error {
mitr := p.MeasurementIterator()
for m := mitr.Next(); m != nil; m = mitr.Next() {
if err := p.writeTagsetTo(w, m.Name(), info, n); err != nil {
return err
}
}
return nil
}
// writeTagsetTo writes a single tagset to w and saves the tagset offset.
func (p IndexFiles) writeTagsetTo(w io.Writer, name []byte, info *indexCompactInfo, n *int64) error {
var seriesKey []byte
kitr, err := p.TagKeyIterator(name)
if err != nil {
return err
}
enc := NewTagBlockEncoder(w)
for ke := kitr.Next(); ke != nil; ke = kitr.Next() {
// Encode key.
if err := enc.EncodeKey(ke.Key(), ke.Deleted()); err != nil {
return err
}
// Iterate over tag values.
vitr := ke.TagValueIterator()
for ve := vitr.Next(); ve != nil; ve = vitr.Next() {
// Merge all series together.
sitr := p.TagValueSeriesIterator(name, ke.Key(), ve.Value())
var seriesIDs []uint32
for se := sitr.Next(); se != nil; se = sitr.Next() {
seriesID, _ := info.sblk.Offset(se.Name(), se.Tags(), seriesKey[:0])
if seriesID == 0 {
return fmt.Errorf("expected series id: %s/%s", se.Name(), se.Tags().String())
}
seriesIDs = append(seriesIDs, seriesID)
}
sort.Sort(uint32Slice(seriesIDs))
// Encode value.
if err := enc.EncodeValue(ve.Value(), ve.Deleted(), seriesIDs); err != nil {
return err
}
}
}
// Save tagset offset to measurement.
pos := info.tagSets[string(name)]
pos.offset = *n
// Flush data to writer.
err = enc.Close()
*n += enc.N()
if err != nil {
return err
}
// Save tagset size to measurement.
pos.size = *n - pos.offset
info.tagSets[string(name)] = pos
return nil
}
func (p IndexFiles) writeMeasurementBlockTo(w io.Writer, info *indexCompactInfo, n *int64) error {
var seriesKey []byte
mw := NewMeasurementBlockWriter()
// Add measurement data & compute sketches.
mitr := p.MeasurementIterator()
for m := mitr.Next(); m != nil; m = mitr.Next() {
name := m.Name()
// Look-up series ids.
itr := p.MeasurementSeriesIterator(name)
var seriesIDs []uint32
for e := itr.Next(); e != nil; e = itr.Next() {
seriesID, _ := info.sblk.Offset(e.Name(), e.Tags(), seriesKey[:0])
if seriesID == 0 {
panic(fmt.Sprintf("expected series id: %s %s", e.Name(), e.Tags().String()))
}
seriesIDs = append(seriesIDs, seriesID)
}
sort.Sort(uint32Slice(seriesIDs))
// Add measurement to writer.
pos := info.tagSets[string(name)]
mw.Add(name, m.Deleted(), pos.offset, pos.size, seriesIDs)
}
// Flush data to writer.
nn, err := mw.WriteTo(w)
*n += nn
return err
}
// Stat returns the max index file size and the total file size for all index files.
func (p IndexFiles) Stat() (*IndexFilesInfo, error) {
var info IndexFilesInfo
for _, f := range p {
fi, err := os.Stat(f.Path())
if os.IsNotExist(err) {
continue
} else if err != nil {
return nil, err
}
if fi.Size() > info.MaxSize {
info.MaxSize = fi.Size()
}
if fi.ModTime().After(info.ModTime) {
info.ModTime = fi.ModTime()
}
info.Size += fi.Size()
}
return &info, nil
}
type IndexFilesInfo struct {
MaxSize int64 // largest file size
Size int64 // total file size
ModTime time.Time // last modified
}
// indexCompactInfo is a context object used for tracking position information
// during the compaction of index files.
type indexCompactInfo struct {
// Memory-mapped series block.
// Available after the series block has been written.
sblk *SeriesBlock
// Tracks offset/size for each measurement's tagset.
tagSets map[string]indexTagSetPos
}
// indexTagSetPos stores the offset/size of tagsets.
type indexTagSetPos struct {
offset int64
size int64
}