2018-10-01 11:08:33 +00:00
|
|
|
package tsi1
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"sync"
|
|
|
|
"unsafe"
|
|
|
|
|
2019-01-08 00:37:16 +00:00
|
|
|
"github.com/influxdata/influxdb/models"
|
storage: fix problems with keeping resources alive
This commit adds the pkg/lifecycle.Resource to help manage opening,
closing, and leasing out references to some resource. A resource
cannot be closed until all acquired references have been released.
If the debug_ref tag is enabled, all resource acquisitions keep
track of the stack trace that created them and have a finalizer
associated with them to print on stderr if they are leaked. It also
registers a handler on SIGUSR2 to dump all of the currently live
resources.
Having resources tracked in a uniform way with a data type allows us
to do more sophisticated tracking with the debug_ref tag, as well.
For example, we could panic the process if a resource cannot be
closed within a certain time frame, or attempt to figure out the
DAG of resource ownership dynamically.
This commit also fixes many issues around resources, correctness
during error scenarios, reporting of errors, idempotency of
close, tracking of memory for some data structures, resource leaks
in tests, and out of order dependency closes in tests.
2019-02-25 23:51:08 +00:00
|
|
|
"github.com/influxdata/influxdb/pkg/lifecycle"
|
2019-01-08 00:37:16 +00:00
|
|
|
"github.com/influxdata/influxdb/pkg/mmap"
|
|
|
|
"github.com/influxdata/influxdb/tsdb"
|
2020-03-12 18:32:52 +00:00
|
|
|
"github.com/influxdata/influxdb/tsdb/seriesfile"
|
2018-10-01 11:08:33 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// IndexFileVersion is the current TSI1 index file version.
|
|
|
|
const IndexFileVersion = 1
|
|
|
|
|
|
|
|
// FileSignature represents a magic number at the header of the index file.
|
|
|
|
const FileSignature = "TSI1"
|
|
|
|
|
|
|
|
// IndexFile field size constants.
|
|
|
|
const (
|
|
|
|
// IndexFile trailer fields
|
|
|
|
IndexFileVersionSize = 2
|
|
|
|
|
|
|
|
// IndexFileTrailerSize is the size of the trailer. Currently 82 bytes.
|
|
|
|
IndexFileTrailerSize = IndexFileVersionSize +
|
|
|
|
8 + 8 + // measurement block offset + size
|
|
|
|
8 + 8 + // series id set offset + size
|
|
|
|
8 + 8 + // tombstone series id set offset + size
|
2018-10-31 21:19:17 +00:00
|
|
|
// legacy sketch info. we used to have HLL sketches, but they were
|
|
|
|
// removed. we keep the offset and length bytes in the trailer so
|
|
|
|
// that we don't have to do a migration, but they are unused.
|
|
|
|
8 + 8 + 8 + 8 +
|
2018-10-01 11:08:33 +00:00
|
|
|
0
|
|
|
|
)
|
|
|
|
|
|
|
|
// IndexFile errors.
|
|
|
|
var (
|
|
|
|
ErrInvalidIndexFile = errors.New("invalid index file")
|
|
|
|
ErrUnsupportedIndexFileVersion = errors.New("unsupported index file version")
|
|
|
|
)
|
|
|
|
|
|
|
|
// IndexFile represents a collection of measurement, tag, and series data.
|
|
|
|
type IndexFile struct {
|
|
|
|
data []byte
|
|
|
|
|
storage: fix problems with keeping resources alive
This commit adds the pkg/lifecycle.Resource to help manage opening,
closing, and leasing out references to some resource. A resource
cannot be closed until all acquired references have been released.
If the debug_ref tag is enabled, all resource acquisitions keep
track of the stack trace that created them and have a finalizer
associated with them to print on stderr if they are leaked. It also
registers a handler on SIGUSR2 to dump all of the currently live
resources.
Having resources tracked in a uniform way with a data type allows us
to do more sophisticated tracking with the debug_ref tag, as well.
For example, we could panic the process if a resource cannot be
closed within a certain time frame, or attempt to figure out the
DAG of resource ownership dynamically.
This commit also fixes many issues around resources, correctness
during error scenarios, reporting of errors, idempotency of
close, tracking of memory for some data structures, resource leaks
in tests, and out of order dependency closes in tests.
2019-02-25 23:51:08 +00:00
|
|
|
// Lifecycle tracking
|
|
|
|
res lifecycle.Resource
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
// Components
|
2020-03-12 18:32:52 +00:00
|
|
|
sfile *seriesfile.SeriesFile
|
storage: fix problems with keeping resources alive
This commit adds the pkg/lifecycle.Resource to help manage opening,
closing, and leasing out references to some resource. A resource
cannot be closed until all acquired references have been released.
If the debug_ref tag is enabled, all resource acquisitions keep
track of the stack trace that created them and have a finalizer
associated with them to print on stderr if they are leaked. It also
registers a handler on SIGUSR2 to dump all of the currently live
resources.
Having resources tracked in a uniform way with a data type allows us
to do more sophisticated tracking with the debug_ref tag, as well.
For example, we could panic the process if a resource cannot be
closed within a certain time frame, or attempt to figure out the
DAG of resource ownership dynamically.
This commit also fixes many issues around resources, correctness
during error scenarios, reporting of errors, idempotency of
close, tracking of memory for some data structures, resource leaks
in tests, and out of order dependency closes in tests.
2019-02-25 23:51:08 +00:00
|
|
|
sfileref *lifecycle.Reference
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
tblks map[string]*TagBlock // tag blocks by measurement name
|
|
|
|
mblk MeasurementBlock
|
|
|
|
|
|
|
|
// Raw series set data.
|
|
|
|
seriesIDSetData []byte
|
|
|
|
tombstoneSeriesIDSetData []byte
|
|
|
|
|
|
|
|
// Sortable identifier & filepath to the log file.
|
|
|
|
level int
|
|
|
|
id int
|
|
|
|
|
2018-09-28 12:47:08 +00:00
|
|
|
mu sync.RWMutex
|
2018-10-01 11:08:33 +00:00
|
|
|
// Compaction tracking.
|
|
|
|
compacting bool
|
|
|
|
|
|
|
|
// Path to data file.
|
|
|
|
path string
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewIndexFile returns a new instance of IndexFile.
|
2020-03-12 18:32:52 +00:00
|
|
|
func NewIndexFile(sfile *seriesfile.SeriesFile) *IndexFile {
|
2018-10-01 11:08:33 +00:00
|
|
|
return &IndexFile{
|
2018-09-28 12:47:08 +00:00
|
|
|
sfile: sfile,
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// bytes estimates the memory footprint of this IndexFile, in bytes.
|
|
|
|
func (f *IndexFile) bytes() int {
|
|
|
|
var b int
|
|
|
|
// Do not count f.data contents because it is mmap'd
|
storage: fix problems with keeping resources alive
This commit adds the pkg/lifecycle.Resource to help manage opening,
closing, and leasing out references to some resource. A resource
cannot be closed until all acquired references have been released.
If the debug_ref tag is enabled, all resource acquisitions keep
track of the stack trace that created them and have a finalizer
associated with them to print on stderr if they are leaked. It also
registers a handler on SIGUSR2 to dump all of the currently live
resources.
Having resources tracked in a uniform way with a data type allows us
to do more sophisticated tracking with the debug_ref tag, as well.
For example, we could panic the process if a resource cannot be
closed within a certain time frame, or attempt to figure out the
DAG of resource ownership dynamically.
This commit also fixes many issues around resources, correctness
during error scenarios, reporting of errors, idempotency of
close, tracking of memory for some data structures, resource leaks
in tests, and out of order dependency closes in tests.
2019-02-25 23:51:08 +00:00
|
|
|
b += int(unsafe.Sizeof(f.data))
|
|
|
|
b += int(unsafe.Sizeof(f.res))
|
2018-10-01 11:08:33 +00:00
|
|
|
b += int(unsafe.Sizeof(f.sfile))
|
storage: fix problems with keeping resources alive
This commit adds the pkg/lifecycle.Resource to help manage opening,
closing, and leasing out references to some resource. A resource
cannot be closed until all acquired references have been released.
If the debug_ref tag is enabled, all resource acquisitions keep
track of the stack trace that created them and have a finalizer
associated with them to print on stderr if they are leaked. It also
registers a handler on SIGUSR2 to dump all of the currently live
resources.
Having resources tracked in a uniform way with a data type allows us
to do more sophisticated tracking with the debug_ref tag, as well.
For example, we could panic the process if a resource cannot be
closed within a certain time frame, or attempt to figure out the
DAG of resource ownership dynamically.
This commit also fixes many issues around resources, correctness
during error scenarios, reporting of errors, idempotency of
close, tracking of memory for some data structures, resource leaks
in tests, and out of order dependency closes in tests.
2019-02-25 23:51:08 +00:00
|
|
|
b += int(unsafe.Sizeof(f.sfileref))
|
2018-10-01 11:08:33 +00:00
|
|
|
// Do not count SeriesFile because it belongs to the code that constructed this IndexFile.
|
|
|
|
b += int(unsafe.Sizeof(f.tblks))
|
|
|
|
for k, v := range f.tblks {
|
|
|
|
// Do not count TagBlock contents, they all reference f.data
|
|
|
|
b += int(unsafe.Sizeof(k)) + len(k)
|
|
|
|
b += int(unsafe.Sizeof(*v))
|
|
|
|
}
|
|
|
|
b += int(unsafe.Sizeof(f.mblk)) + f.mblk.bytes()
|
|
|
|
b += int(unsafe.Sizeof(f.seriesIDSetData) + unsafe.Sizeof(f.tombstoneSeriesIDSetData))
|
|
|
|
// Do not count contents of seriesIDSetData or tombstoneSeriesIDSetData: references f.data
|
|
|
|
b += int(unsafe.Sizeof(f.level) + unsafe.Sizeof(f.id))
|
|
|
|
b += 24 // mu RWMutex is 24 bytes
|
|
|
|
b += int(unsafe.Sizeof(f.compacting))
|
|
|
|
b += int(unsafe.Sizeof(f.path)) + len(f.path)
|
storage: fix problems with keeping resources alive
This commit adds the pkg/lifecycle.Resource to help manage opening,
closing, and leasing out references to some resource. A resource
cannot be closed until all acquired references have been released.
If the debug_ref tag is enabled, all resource acquisitions keep
track of the stack trace that created them and have a finalizer
associated with them to print on stderr if they are leaked. It also
registers a handler on SIGUSR2 to dump all of the currently live
resources.
Having resources tracked in a uniform way with a data type allows us
to do more sophisticated tracking with the debug_ref tag, as well.
For example, we could panic the process if a resource cannot be
closed within a certain time frame, or attempt to figure out the
DAG of resource ownership dynamically.
This commit also fixes many issues around resources, correctness
during error scenarios, reporting of errors, idempotency of
close, tracking of memory for some data structures, resource leaks
in tests, and out of order dependency closes in tests.
2019-02-25 23:51:08 +00:00
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open memory maps the data file at the file's path.
|
storage: fix problems with keeping resources alive
This commit adds the pkg/lifecycle.Resource to help manage opening,
closing, and leasing out references to some resource. A resource
cannot be closed until all acquired references have been released.
If the debug_ref tag is enabled, all resource acquisitions keep
track of the stack trace that created them and have a finalizer
associated with them to print on stderr if they are leaked. It also
registers a handler on SIGUSR2 to dump all of the currently live
resources.
Having resources tracked in a uniform way with a data type allows us
to do more sophisticated tracking with the debug_ref tag, as well.
For example, we could panic the process if a resource cannot be
closed within a certain time frame, or attempt to figure out the
DAG of resource ownership dynamically.
This commit also fixes many issues around resources, correctness
during error scenarios, reporting of errors, idempotency of
close, tracking of memory for some data structures, resource leaks
in tests, and out of order dependency closes in tests.
2019-02-25 23:51:08 +00:00
|
|
|
func (f *IndexFile) Open() (err error) {
|
2018-10-01 11:08:33 +00:00
|
|
|
defer func() {
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
err = fmt.Errorf("[Index file: %s] %v", f.path, err)
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
storage: fix problems with keeping resources alive
This commit adds the pkg/lifecycle.Resource to help manage opening,
closing, and leasing out references to some resource. A resource
cannot be closed until all acquired references have been released.
If the debug_ref tag is enabled, all resource acquisitions keep
track of the stack trace that created them and have a finalizer
associated with them to print on stderr if they are leaked. It also
registers a handler on SIGUSR2 to dump all of the currently live
resources.
Having resources tracked in a uniform way with a data type allows us
to do more sophisticated tracking with the debug_ref tag, as well.
For example, we could panic the process if a resource cannot be
closed within a certain time frame, or attempt to figure out the
DAG of resource ownership dynamically.
This commit also fixes many issues around resources, correctness
during error scenarios, reporting of errors, idempotency of
close, tracking of memory for some data structures, resource leaks
in tests, and out of order dependency closes in tests.
2019-02-25 23:51:08 +00:00
|
|
|
// Try to acquire a reference to the series file.
|
|
|
|
f.sfileref, err = f.sfile.Acquire()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
// Extract identifier from path name.
|
|
|
|
f.id, f.level = ParseFilename(f.Path())
|
|
|
|
|
|
|
|
data, err := mmap.Map(f.Path(), 0)
|
|
|
|
if err != nil {
|
tsi1: partition close deadlock
When a tsi1 partition closes, it waits on the wait group for compactions
and then acquires the lock. Unfortunately, a compaction may start in the
mean time, holding on to some resources. Then, close will attempt to
close those resources while holding the lock. That will block until
the compaction has finished, but it also needs to acquire the lock
in order to finish, leading to deadlock.
One cannot just move the wait group wait into the lock because, once
again, the compaction must acquire the lock before finishing. Compaction
can't finish before acquiring the lock because then it might be operating
on an invalid resource.
This change splits the locks into two: one to protect just against
concurrent Open and Close calls, and one to protect all of the other
state. We then just close the partition, acquire the lock, then free
the resources. Starting a compaction requires acquiring a resource
to the partition itself, so that it can't start one after it has
started closing.
This change also introduces a cancellation channel into a reference
to a resource that is closed when the resource is being closed, allowing
processes that have acquired a reference to clean up quicker if someone
is trying to close the resource.
2019-04-02 19:00:56 +00:00
|
|
|
f.sfileref.Release()
|
2018-10-01 11:08:33 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
storage: fix problems with keeping resources alive
This commit adds the pkg/lifecycle.Resource to help manage opening,
closing, and leasing out references to some resource. A resource
cannot be closed until all acquired references have been released.
If the debug_ref tag is enabled, all resource acquisitions keep
track of the stack trace that created them and have a finalizer
associated with them to print on stderr if they are leaked. It also
registers a handler on SIGUSR2 to dump all of the currently live
resources.
Having resources tracked in a uniform way with a data type allows us
to do more sophisticated tracking with the debug_ref tag, as well.
For example, we could panic the process if a resource cannot be
closed within a certain time frame, or attempt to figure out the
DAG of resource ownership dynamically.
This commit also fixes many issues around resources, correctness
during error scenarios, reporting of errors, idempotency of
close, tracking of memory for some data structures, resource leaks
in tests, and out of order dependency closes in tests.
2019-02-25 23:51:08 +00:00
|
|
|
if err := f.UnmarshalBinary(data); err != nil {
|
tsi1: partition close deadlock
When a tsi1 partition closes, it waits on the wait group for compactions
and then acquires the lock. Unfortunately, a compaction may start in the
mean time, holding on to some resources. Then, close will attempt to
close those resources while holding the lock. That will block until
the compaction has finished, but it also needs to acquire the lock
in order to finish, leading to deadlock.
One cannot just move the wait group wait into the lock because, once
again, the compaction must acquire the lock before finishing. Compaction
can't finish before acquiring the lock because then it might be operating
on an invalid resource.
This change splits the locks into two: one to protect just against
concurrent Open and Close calls, and one to protect all of the other
state. We then just close the partition, acquire the lock, then free
the resources. Starting a compaction requires acquiring a resource
to the partition itself, so that it can't start one after it has
started closing.
This change also introduces a cancellation channel into a reference
to a resource that is closed when the resource is being closed, allowing
processes that have acquired a reference to clean up quicker if someone
is trying to close the resource.
2019-04-02 19:00:56 +00:00
|
|
|
f.sfileref.Release()
|
storage: fix problems with keeping resources alive
This commit adds the pkg/lifecycle.Resource to help manage opening,
closing, and leasing out references to some resource. A resource
cannot be closed until all acquired references have been released.
If the debug_ref tag is enabled, all resource acquisitions keep
track of the stack trace that created them and have a finalizer
associated with them to print on stderr if they are leaked. It also
registers a handler on SIGUSR2 to dump all of the currently live
resources.
Having resources tracked in a uniform way with a data type allows us
to do more sophisticated tracking with the debug_ref tag, as well.
For example, we could panic the process if a resource cannot be
closed within a certain time frame, or attempt to figure out the
DAG of resource ownership dynamically.
This commit also fixes many issues around resources, correctness
during error scenarios, reporting of errors, idempotency of
close, tracking of memory for some data structures, resource leaks
in tests, and out of order dependency closes in tests.
2019-02-25 23:51:08 +00:00
|
|
|
f.Close()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// The resource is now open
|
|
|
|
f.res.Open()
|
|
|
|
|
|
|
|
return nil
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Close unmaps the data file.
|
|
|
|
func (f *IndexFile) Close() error {
|
storage: fix problems with keeping resources alive
This commit adds the pkg/lifecycle.Resource to help manage opening,
closing, and leasing out references to some resource. A resource
cannot be closed until all acquired references have been released.
If the debug_ref tag is enabled, all resource acquisitions keep
track of the stack trace that created them and have a finalizer
associated with them to print on stderr if they are leaked. It also
registers a handler on SIGUSR2 to dump all of the currently live
resources.
Having resources tracked in a uniform way with a data type allows us
to do more sophisticated tracking with the debug_ref tag, as well.
For example, we could panic the process if a resource cannot be
closed within a certain time frame, or attempt to figure out the
DAG of resource ownership dynamically.
This commit also fixes many issues around resources, correctness
during error scenarios, reporting of errors, idempotency of
close, tracking of memory for some data structures, resource leaks
in tests, and out of order dependency closes in tests.
2019-02-25 23:51:08 +00:00
|
|
|
// Close the resource and wait for any references.
|
|
|
|
f.res.Close()
|
|
|
|
|
|
|
|
if f.sfileref != nil {
|
|
|
|
f.sfileref.Release()
|
|
|
|
f.sfileref = nil
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
f.sfile = nil
|
|
|
|
f.tblks = nil
|
|
|
|
f.mblk = MeasurementBlock{}
|
|
|
|
return mmap.Unmap(f.data)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ID returns the file sequence identifier.
|
|
|
|
func (f *IndexFile) ID() int { return f.id }
|
|
|
|
|
|
|
|
// Path returns the file path.
|
|
|
|
func (f *IndexFile) Path() string { return f.path }
|
|
|
|
|
|
|
|
// SetPath sets the file's path.
|
|
|
|
func (f *IndexFile) SetPath(path string) { f.path = path }
|
|
|
|
|
|
|
|
// Level returns the compaction level for the file.
|
|
|
|
func (f *IndexFile) Level() int { return f.level }
|
|
|
|
|
storage: fix problems with keeping resources alive
This commit adds the pkg/lifecycle.Resource to help manage opening,
closing, and leasing out references to some resource. A resource
cannot be closed until all acquired references have been released.
If the debug_ref tag is enabled, all resource acquisitions keep
track of the stack trace that created them and have a finalizer
associated with them to print on stderr if they are leaked. It also
registers a handler on SIGUSR2 to dump all of the currently live
resources.
Having resources tracked in a uniform way with a data type allows us
to do more sophisticated tracking with the debug_ref tag, as well.
For example, we could panic the process if a resource cannot be
closed within a certain time frame, or attempt to figure out the
DAG of resource ownership dynamically.
This commit also fixes many issues around resources, correctness
during error scenarios, reporting of errors, idempotency of
close, tracking of memory for some data structures, resource leaks
in tests, and out of order dependency closes in tests.
2019-02-25 23:51:08 +00:00
|
|
|
// Acquire adds a reference count to the file.
|
|
|
|
func (f *IndexFile) Acquire() (*lifecycle.Reference, error) {
|
|
|
|
return f.res.Acquire()
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// Size returns the size of the index file, in bytes.
|
|
|
|
func (f *IndexFile) Size() int64 { return int64(len(f.data)) }
|
|
|
|
|
|
|
|
// Compacting returns true if the file is being compacted.
|
|
|
|
func (f *IndexFile) Compacting() bool {
|
|
|
|
f.mu.RLock()
|
|
|
|
v := f.compacting
|
|
|
|
f.mu.RUnlock()
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalBinary opens an index from data.
|
|
|
|
// The byte slice is retained so it must be kept open.
|
|
|
|
func (f *IndexFile) UnmarshalBinary(data []byte) error {
|
|
|
|
// Ensure magic number exists at the beginning.
|
|
|
|
if len(data) < len(FileSignature) {
|
|
|
|
return io.ErrShortBuffer
|
|
|
|
} else if !bytes.Equal(data[:len(FileSignature)], []byte(FileSignature)) {
|
|
|
|
return ErrInvalidIndexFile
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read index file trailer.
|
|
|
|
t, err := ReadIndexFileTrailer(data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Slice series set data.
|
|
|
|
f.seriesIDSetData = data[t.SeriesIDSet.Offset : t.SeriesIDSet.Offset+t.SeriesIDSet.Size]
|
|
|
|
f.tombstoneSeriesIDSetData = data[t.TombstoneSeriesIDSet.Offset : t.TombstoneSeriesIDSet.Offset+t.TombstoneSeriesIDSet.Size]
|
|
|
|
|
|
|
|
// Unmarshal measurement block.
|
2018-09-28 12:47:08 +00:00
|
|
|
if err := f.mblk.UnmarshalBinary(data[t.MeasurementBlock.Offset:][:t.MeasurementBlock.Size]); err != nil {
|
2018-10-01 11:08:33 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unmarshal each tag block.
|
|
|
|
f.tblks = make(map[string]*TagBlock)
|
|
|
|
itr := f.mblk.Iterator()
|
|
|
|
|
|
|
|
for m := itr.Next(); m != nil; m = itr.Next() {
|
|
|
|
e := m.(*MeasurementBlockElem)
|
|
|
|
|
|
|
|
// Slice measurement block data.
|
|
|
|
buf := data[e.tagBlock.offset:]
|
|
|
|
buf = buf[:e.tagBlock.size]
|
|
|
|
|
|
|
|
// Unmarshal measurement block.
|
|
|
|
var tblk TagBlock
|
|
|
|
if err := tblk.UnmarshalBinary(buf); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
f.tblks[string(e.name)] = &tblk
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save reference to entire data block.
|
|
|
|
f.data = data
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *IndexFile) SeriesIDSet() (*tsdb.SeriesIDSet, error) {
|
|
|
|
ss := tsdb.NewSeriesIDSet()
|
|
|
|
if err := ss.UnmarshalBinary(f.seriesIDSetData); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return ss, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *IndexFile) TombstoneSeriesIDSet() (*tsdb.SeriesIDSet, error) {
|
|
|
|
ss := tsdb.NewSeriesIDSet()
|
2018-09-28 12:47:08 +00:00
|
|
|
if err := ss.UnmarshalBinaryUnsafe(f.tombstoneSeriesIDSetData); err != nil {
|
2018-10-01 11:08:33 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return ss, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Measurement returns a measurement element.
|
|
|
|
func (f *IndexFile) Measurement(name []byte) MeasurementElem {
|
|
|
|
e, ok := f.mblk.Elem(name)
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &e
|
|
|
|
}
|
|
|
|
|
|
|
|
// MeasurementN returns the number of measurements in the file.
|
|
|
|
func (f *IndexFile) MeasurementN() (n uint64) {
|
|
|
|
mitr := f.mblk.Iterator()
|
|
|
|
for me := mitr.Next(); me != nil; me = mitr.Next() {
|
|
|
|
n++
|
|
|
|
}
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
// MeasurementHasSeries returns true if a measurement has any non-tombstoned series.
|
|
|
|
func (f *IndexFile) MeasurementHasSeries(ss *tsdb.SeriesIDSet, name []byte) (ok bool) {
|
|
|
|
e, ok := f.mblk.Elem(name)
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
var exists bool
|
|
|
|
e.ForEachSeriesID(func(id tsdb.SeriesID) error {
|
|
|
|
if ss.Contains(id) {
|
|
|
|
exists = true
|
|
|
|
return errors.New("done")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return exists
|
|
|
|
}
|
|
|
|
|
|
|
|
// TagValueIterator returns a value iterator for a tag key and a flag
|
|
|
|
// indicating if a tombstone exists on the measurement or key.
|
|
|
|
func (f *IndexFile) TagValueIterator(name, key []byte) TagValueIterator {
|
|
|
|
tblk := f.tblks[string(name)]
|
|
|
|
if tblk == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find key element.
|
|
|
|
ke := tblk.TagKeyElem(key)
|
|
|
|
if ke == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Merge all value series iterators together.
|
|
|
|
return ke.TagValueIterator()
|
|
|
|
}
|
|
|
|
|
|
|
|
// TagKeySeriesIDIterator returns a series iterator for a tag key and a flag
|
|
|
|
// indicating if a tombstone exists on the measurement or key.
|
2019-11-12 13:17:38 +00:00
|
|
|
func (f *IndexFile) TagKeySeriesIDIterator(name, key []byte) (tsdb.SeriesIDIterator, error) {
|
2018-10-01 11:08:33 +00:00
|
|
|
tblk := f.tblks[string(name)]
|
|
|
|
if tblk == nil {
|
2019-11-12 13:17:38 +00:00
|
|
|
return nil, nil
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Find key element.
|
|
|
|
ke := tblk.TagKeyElem(key)
|
|
|
|
if ke == nil {
|
2019-11-12 13:17:38 +00:00
|
|
|
return nil, nil
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Merge all value series iterators together.
|
|
|
|
vitr := ke.TagValueIterator()
|
2019-11-12 13:17:38 +00:00
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
var itrs []tsdb.SeriesIDIterator
|
|
|
|
for ve := vitr.Next(); ve != nil; ve = vitr.Next() {
|
2019-11-12 13:17:38 +00:00
|
|
|
tblk, ok := ve.(*TagBlockValueElem)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("got type %T for iterator, expected %T", ve, TagBlockValueElem{})
|
|
|
|
}
|
|
|
|
|
|
|
|
ss, err := tblk.SeriesIDSet()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
itrs = append(itrs, tsdb.NewSeriesIDSetIterator(ss))
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
2019-11-12 13:17:38 +00:00
|
|
|
return tsdb.MergeSeriesIDIterators(itrs...), nil
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TagValueSeriesIDSet returns a series id set for a tag value.
|
|
|
|
func (f *IndexFile) TagValueSeriesIDSet(name, key, value []byte) (*tsdb.SeriesIDSet, error) {
|
|
|
|
tblk := f.tblks[string(name)]
|
|
|
|
if tblk == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find value element.
|
|
|
|
var valueElem TagBlockValueElem
|
|
|
|
if !tblk.DecodeTagValueElem(key, value, &valueElem) {
|
|
|
|
return nil, nil
|
|
|
|
} else if valueElem.SeriesN() == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return valueElem.SeriesIDSet()
|
|
|
|
}
|
|
|
|
|
|
|
|
// TagKey returns a tag key.
|
|
|
|
func (f *IndexFile) TagKey(name, key []byte) TagKeyElem {
|
|
|
|
tblk := f.tblks[string(name)]
|
|
|
|
if tblk == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return tblk.TagKeyElem(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TagValue returns a tag value.
|
|
|
|
func (f *IndexFile) TagValue(name, key, value []byte) TagValueElem {
|
|
|
|
tblk := f.tblks[string(name)]
|
|
|
|
if tblk == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return tblk.TagValueElem(key, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasSeries returns flags indicating if the series exists and if it is tombstoned.
|
|
|
|
func (f *IndexFile) HasSeries(name []byte, tags models.Tags, buf []byte) (exists, tombstoned bool) {
|
|
|
|
return f.sfile.HasSeries(name, tags, buf), false // TODO(benbjohnson): series tombstone
|
|
|
|
}
|
|
|
|
|
|
|
|
// TagValueElem returns an element for a measurement/tag/value.
|
|
|
|
func (f *IndexFile) TagValueElem(name, key, value []byte) TagValueElem {
|
|
|
|
tblk, ok := f.tblks[string(name)]
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return tblk.TagValueElem(key, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MeasurementIterator returns an iterator over all measurements.
|
|
|
|
func (f *IndexFile) MeasurementIterator() MeasurementIterator {
|
|
|
|
return f.mblk.Iterator()
|
|
|
|
}
|
|
|
|
|
|
|
|
// TagKeyIterator returns an iterator over all tag keys for a measurement.
|
|
|
|
func (f *IndexFile) TagKeyIterator(name []byte) TagKeyIterator {
|
|
|
|
blk := f.tblks[string(name)]
|
|
|
|
if blk == nil {
|
|
|
|
return nil
|
|
|
|
}
|
storage: fix problems with keeping resources alive
This commit adds the pkg/lifecycle.Resource to help manage opening,
closing, and leasing out references to some resource. A resource
cannot be closed until all acquired references have been released.
If the debug_ref tag is enabled, all resource acquisitions keep
track of the stack trace that created them and have a finalizer
associated with them to print on stderr if they are leaked. It also
registers a handler on SIGUSR2 to dump all of the currently live
resources.
Having resources tracked in a uniform way with a data type allows us
to do more sophisticated tracking with the debug_ref tag, as well.
For example, we could panic the process if a resource cannot be
closed within a certain time frame, or attempt to figure out the
DAG of resource ownership dynamically.
This commit also fixes many issues around resources, correctness
during error scenarios, reporting of errors, idempotency of
close, tracking of memory for some data structures, resource leaks
in tests, and out of order dependency closes in tests.
2019-02-25 23:51:08 +00:00
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
return blk.TagKeyIterator()
|
|
|
|
}
|
|
|
|
|
|
|
|
// MeasurementSeriesIDIterator returns an iterator over a measurement's series.
|
|
|
|
func (f *IndexFile) MeasurementSeriesIDIterator(name []byte) tsdb.SeriesIDIterator {
|
|
|
|
return f.mblk.SeriesIDIterator(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadIndexFileTrailer returns the index file trailer from data.
|
|
|
|
func ReadIndexFileTrailer(data []byte) (IndexFileTrailer, error) {
|
|
|
|
var t IndexFileTrailer
|
|
|
|
|
|
|
|
// Read version.
|
|
|
|
t.Version = int(binary.BigEndian.Uint16(data[len(data)-IndexFileVersionSize:]))
|
|
|
|
if t.Version != IndexFileVersion {
|
|
|
|
return t, ErrUnsupportedIndexFileVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
// Slice trailer data.
|
|
|
|
buf := data[len(data)-IndexFileTrailerSize:]
|
|
|
|
|
|
|
|
// Read measurement block info.
|
|
|
|
t.MeasurementBlock.Offset, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
|
|
|
|
t.MeasurementBlock.Size, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
|
|
|
|
|
|
|
|
// Read series id set info.
|
|
|
|
t.SeriesIDSet.Offset, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
|
|
|
|
t.SeriesIDSet.Size, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
|
|
|
|
|
|
|
|
// Read series tombstone id set info.
|
|
|
|
t.TombstoneSeriesIDSet.Offset, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
|
|
|
|
t.TombstoneSeriesIDSet.Size, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
|
|
|
|
|
2018-10-26 04:07:30 +00:00
|
|
|
// Skip over any legacy sketch data.
|
|
|
|
buf = buf[8*4:]
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
if len(buf) != 2 { // Version field still in buffer.
|
|
|
|
return t, fmt.Errorf("unread %d bytes left unread in trailer", len(buf)-2)
|
|
|
|
}
|
|
|
|
return t, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// IndexFileTrailer represents meta data written to the end of the index file.
|
|
|
|
type IndexFileTrailer struct {
|
|
|
|
Version int
|
|
|
|
|
|
|
|
MeasurementBlock struct {
|
|
|
|
Offset int64
|
|
|
|
Size int64
|
|
|
|
}
|
|
|
|
|
|
|
|
SeriesIDSet struct {
|
|
|
|
Offset int64
|
|
|
|
Size int64
|
|
|
|
}
|
|
|
|
|
|
|
|
TombstoneSeriesIDSet struct {
|
|
|
|
Offset int64
|
|
|
|
Size int64
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WriteTo writes the trailer to w.
|
|
|
|
func (t *IndexFileTrailer) WriteTo(w io.Writer) (n int64, err error) {
|
|
|
|
// Write measurement block info.
|
|
|
|
if err := writeUint64To(w, uint64(t.MeasurementBlock.Offset), &n); err != nil {
|
|
|
|
return n, err
|
|
|
|
} else if err := writeUint64To(w, uint64(t.MeasurementBlock.Size), &n); err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write series id set info.
|
|
|
|
if err := writeUint64To(w, uint64(t.SeriesIDSet.Offset), &n); err != nil {
|
|
|
|
return n, err
|
|
|
|
} else if err := writeUint64To(w, uint64(t.SeriesIDSet.Size), &n); err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write tombstone series id set info.
|
|
|
|
if err := writeUint64To(w, uint64(t.TombstoneSeriesIDSet.Offset), &n); err != nil {
|
|
|
|
return n, err
|
|
|
|
} else if err := writeUint64To(w, uint64(t.TombstoneSeriesIDSet.Size), &n); err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
2018-10-26 04:07:30 +00:00
|
|
|
// Write legacy sketch info.
|
|
|
|
for i := 0; i < 4; i++ {
|
|
|
|
if err := writeUint64To(w, 0, &n); err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Write index file encoding version.
|
|
|
|
if err := writeUint16To(w, IndexFileVersion, &n); err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FormatIndexFileName generates an index filename for the given index.
|
|
|
|
func FormatIndexFileName(id, level int) string {
|
|
|
|
return fmt.Sprintf("L%d-%08d%s", level, id, IndexFileExt)
|
|
|
|
}
|