2018-10-01 11:08:33 +00:00
|
|
|
package tsi1
|
|
|
|
|
|
|
|
import (
|
2019-05-01 01:51:22 +00:00
|
|
|
"context"
|
2018-10-01 11:08:33 +00:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2018-09-28 12:47:08 +00:00
|
|
|
"fmt"
|
2018-10-01 11:08:33 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
"unsafe"
|
|
|
|
|
2019-05-01 01:51:22 +00:00
|
|
|
"github.com/influxdata/influxdb/kit/tracing"
|
2019-01-08 00:37:16 +00:00
|
|
|
"github.com/influxdata/influxdb/logger"
|
|
|
|
"github.com/influxdata/influxdb/pkg/bytesutil"
|
2019-07-03 17:14:43 +00:00
|
|
|
"github.com/influxdata/influxdb/pkg/fs"
|
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/tsdb"
|
2020-03-12 18:32:52 +00:00
|
|
|
"github.com/influxdata/influxdb/tsdb/seriesfile"
|
2018-10-01 11:08:33 +00:00
|
|
|
"github.com/influxdata/influxql"
|
2018-12-07 13:48:43 +00:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
2018-10-01 11:08:33 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Version is the current version of the TSI index.
|
|
|
|
const Version = 1
|
|
|
|
|
|
|
|
// File extensions.
|
|
|
|
const (
|
|
|
|
LogFileExt = ".tsl"
|
|
|
|
IndexFileExt = ".tsi"
|
|
|
|
|
|
|
|
CompactingExt = ".compacting"
|
|
|
|
)
|
|
|
|
|
2018-10-15 15:13:54 +00:00
|
|
|
const (
|
|
|
|
// ManifestFileName is the name of the index manifest file.
|
|
|
|
ManifestFileName = "MANIFEST"
|
|
|
|
)
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// Partition represents a collection of layered index files and WAL.
|
|
|
|
type Partition struct {
|
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
|
|
|
// The rule to ensure no deadlocks, no resource leaks, and no use after close
|
|
|
|
// is that if the partition launches a goroutine, it must acquire a reference
|
|
|
|
// to itself first and releases it only after it has done all of its use of mu.
|
|
|
|
mu sync.RWMutex
|
|
|
|
resmu sync.Mutex // protects res Open and Close
|
|
|
|
res lifecycle.Resource
|
2018-10-01 11:08:33 +00:00
|
|
|
|
2020-03-12 18:32:52 +00:00
|
|
|
sfile *seriesfile.SeriesFile // series lookup file
|
|
|
|
sfileref *lifecycle.Reference // reference to series lookup file
|
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
|
|
|
|
|
|
|
activeLogFile *LogFile // current log file
|
|
|
|
fileSet *FileSet // current file set
|
|
|
|
seq int // file id sequence
|
2018-10-01 11:08:33 +00:00
|
|
|
|
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
|
|
|
// Running statistics
|
2018-11-29 14:13:16 +00:00
|
|
|
tracker *partitionTracker
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
// Fast series lookup of series IDs in the series file that have been present
|
|
|
|
// in this partition. This set tracks both insertions and deletions of a series.
|
|
|
|
seriesIDSet *tsdb.SeriesIDSet
|
|
|
|
|
2019-09-04 18:53:05 +00:00
|
|
|
// Stats caching
|
|
|
|
StatsTTL time.Duration
|
|
|
|
statsCache MeasurementCardinalityStats
|
|
|
|
lastStatsTime time.Time
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
// Compaction management
|
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
|
|
|
levels []CompactionLevel // compaction levels
|
|
|
|
levelCompacting []bool // level compaction status
|
|
|
|
compactionsDisabled int // counter of disables
|
2019-09-02 15:37:34 +00:00
|
|
|
currentCompactionN int // counter of in-progress compactions
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// Directory of the Partition's index files.
|
|
|
|
path string
|
|
|
|
id string // id portion of path.
|
|
|
|
|
|
|
|
// Log file compaction thresholds.
|
|
|
|
MaxLogFileSize int64
|
|
|
|
nosync bool // when true, flushing and syncing of LogFile will be disabled.
|
|
|
|
logbufferSize int // the LogFile's buffer is set to this value.
|
|
|
|
|
|
|
|
logger *zap.Logger
|
|
|
|
|
2019-04-12 21:52:56 +00:00
|
|
|
// Current size of MANIFEST. Used to determine partition size.
|
2018-10-01 11:08:33 +00:00
|
|
|
manifestSize int64
|
|
|
|
|
|
|
|
// Index's version.
|
|
|
|
version int
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewPartition returns a new instance of Partition.
|
2020-03-12 18:32:52 +00:00
|
|
|
func NewPartition(sfile *seriesfile.SeriesFile, path string) *Partition {
|
2018-11-29 14:13:16 +00:00
|
|
|
partition := &Partition{
|
2018-10-01 11:08:33 +00:00
|
|
|
path: path,
|
|
|
|
sfile: sfile,
|
|
|
|
seriesIDSet: tsdb.NewSeriesIDSet(),
|
|
|
|
|
2018-10-02 15:06:37 +00:00
|
|
|
MaxLogFileSize: DefaultMaxIndexLogFileSize,
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
logger: zap.NewNop(),
|
|
|
|
version: Version,
|
|
|
|
}
|
2018-11-29 14:13:16 +00:00
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
defaultLabels := prometheus.Labels{"index_partition": ""}
|
|
|
|
partition.tracker = newPartitionTracker(newPartitionMetrics(nil), defaultLabels)
|
2018-11-29 14:13:16 +00:00
|
|
|
return partition
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// bytes estimates the memory footprint of this Partition, in bytes.
|
|
|
|
func (p *Partition) bytes() int {
|
|
|
|
var b int
|
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
|
|
|
b += int(unsafe.Sizeof(p.mu))
|
|
|
|
b += int(unsafe.Sizeof(p.resmu))
|
|
|
|
b += int(unsafe.Sizeof(p.res))
|
|
|
|
// Do not count SeriesFile contents because it belongs to the code that constructed this Partition.
|
|
|
|
b += int(unsafe.Sizeof(p.sfile))
|
|
|
|
b += int(unsafe.Sizeof(p.sfileref))
|
2018-10-01 11:08:33 +00:00
|
|
|
b += int(unsafe.Sizeof(p.activeLogFile)) + p.activeLogFile.bytes()
|
|
|
|
b += int(unsafe.Sizeof(p.fileSet)) + p.fileSet.bytes()
|
|
|
|
b += int(unsafe.Sizeof(p.seq))
|
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
|
|
|
b += int(unsafe.Sizeof(p.tracker))
|
2018-10-01 11:08:33 +00:00
|
|
|
b += int(unsafe.Sizeof(p.seriesIDSet)) + p.seriesIDSet.Bytes()
|
|
|
|
b += int(unsafe.Sizeof(p.levels))
|
|
|
|
for _, level := range p.levels {
|
|
|
|
b += int(unsafe.Sizeof(level))
|
|
|
|
}
|
|
|
|
b += int(unsafe.Sizeof(p.levelCompacting))
|
|
|
|
for _, levelCompacting := range p.levelCompacting {
|
|
|
|
b += int(unsafe.Sizeof(levelCompacting))
|
|
|
|
}
|
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
|
|
|
b += int(unsafe.Sizeof(p.compactionsDisabled))
|
2018-10-01 11:08:33 +00:00
|
|
|
b += int(unsafe.Sizeof(p.path)) + len(p.path)
|
|
|
|
b += int(unsafe.Sizeof(p.id)) + len(p.id)
|
|
|
|
b += int(unsafe.Sizeof(p.MaxLogFileSize))
|
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
|
|
|
b += int(unsafe.Sizeof(p.nosync))
|
|
|
|
b += int(unsafe.Sizeof(p.logbufferSize))
|
2018-10-01 11:08:33 +00:00
|
|
|
b += int(unsafe.Sizeof(p.logger))
|
|
|
|
b += int(unsafe.Sizeof(p.manifestSize))
|
|
|
|
b += int(unsafe.Sizeof(p.version))
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
|
|
|
// ErrIncompatibleVersion is returned when attempting to read from an
|
|
|
|
// incompatible tsi1 manifest file.
|
|
|
|
var ErrIncompatibleVersion = errors.New("incompatible tsi1 index MANIFEST")
|
|
|
|
|
|
|
|
// Open opens the partition.
|
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 (p *Partition) Open() (err error) {
|
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
|
|
|
p.resmu.Lock()
|
|
|
|
defer p.resmu.Unlock()
|
2018-10-01 11:08:33 +00:00
|
|
|
|
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
|
|
|
if p.res.Opened() {
|
2018-10-01 11:08:33 +00:00
|
|
|
return errors.New("index partition already open")
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
p.sfileref, err = p.sfile.Acquire()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
|
|
|
p.close()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
// Validate path is correct.
|
|
|
|
p.id = filepath.Base(p.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
|
|
|
if _, err := strconv.Atoi(p.id); err != nil {
|
2018-10-01 11:08:33 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create directory if it doesn't exist.
|
|
|
|
if err := os.MkdirAll(p.path, 0777); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read manifest file.
|
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
|
|
|
m, manifestSize, err := ReadManifestFile(p.manifestPath())
|
2018-10-01 11:08:33 +00:00
|
|
|
if os.IsNotExist(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
|
|
|
m = NewManifest(p.manifestPath())
|
2018-10-01 11:08:33 +00:00
|
|
|
} else if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Set manifest size on the partition
|
|
|
|
p.manifestSize = manifestSize
|
|
|
|
|
|
|
|
// Check to see if the MANIFEST file is compatible with the current Index.
|
|
|
|
if err := m.Validate(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy compaction levels to the index.
|
|
|
|
p.levels = make([]CompactionLevel, len(m.Levels))
|
|
|
|
copy(p.levels, m.Levels)
|
|
|
|
|
|
|
|
// Set up flags to track whether a level is compacting.
|
|
|
|
p.levelCompacting = make([]bool, len(p.levels))
|
|
|
|
|
|
|
|
// Open each file in the manifest.
|
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
|
|
|
files, err := func() (files []File, err error) {
|
|
|
|
// Ensure any opened files are closed in the case of an error.
|
|
|
|
defer func() {
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != 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
|
|
|
for _, file := range files {
|
|
|
|
file.Close()
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
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
|
|
|
}()
|
|
|
|
|
|
|
|
// Open all of the files in the manifest.
|
|
|
|
for _, filename := range m.Files {
|
|
|
|
switch filepath.Ext(filename) {
|
|
|
|
case LogFileExt:
|
|
|
|
f, err := p.openLogFile(filepath.Join(p.path, filename))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
files = append(files, f)
|
2018-10-01 11:08:33 +00:00
|
|
|
|
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
|
|
|
// Make first log file active, if within threshold.
|
|
|
|
sz, _ := f.Stat()
|
|
|
|
if p.activeLogFile == nil && sz < p.MaxLogFileSize {
|
|
|
|
p.activeLogFile = f
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
|
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
|
|
|
case IndexFileExt:
|
|
|
|
f, err := p.openIndexFile(filepath.Join(p.path, filename))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
files = append(files, f)
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
}
|
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
|
|
|
|
|
|
|
return files, nil
|
|
|
|
}()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
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
|
|
|
|
|
|
|
// Place the files in a file set.
|
|
|
|
p.fileSet, err = NewFileSet(p.sfile, files)
|
2018-10-01 11:08:33 +00:00
|
|
|
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
|
|
|
for _, file := range files {
|
|
|
|
file.Close()
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set initial sequence number.
|
|
|
|
p.seq = p.fileSet.MaxID()
|
|
|
|
|
|
|
|
// Delete any files not in the manifest.
|
|
|
|
if err := p.deleteNonManifestFiles(m); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure a log file exists.
|
|
|
|
if p.activeLogFile == nil {
|
|
|
|
if err := p.prependActiveLogFile(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-17 19:55:38 +00:00
|
|
|
// Build series existence set.
|
2018-10-01 11:08:33 +00:00
|
|
|
if err := p.buildSeriesSet(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-11-29 14:13:16 +00:00
|
|
|
p.tracker.SetSeries(p.seriesIDSet.Cardinality())
|
|
|
|
p.tracker.SetFiles(uint64(len(p.fileSet.IndexFiles())), "index")
|
|
|
|
p.tracker.SetFiles(uint64(len(p.fileSet.LogFiles())), "log")
|
|
|
|
p.tracker.SetDiskSize(uint64(p.fileSet.Size()))
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// Mark opened.
|
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
|
|
|
p.res.Open()
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// Send a compaction request on start up.
|
|
|
|
p.compact()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// openLogFile opens a log file and appends it to the index.
|
|
|
|
func (p *Partition) openLogFile(path string) (*LogFile, error) {
|
|
|
|
f := NewLogFile(p.sfile, path)
|
|
|
|
f.nosync = p.nosync
|
|
|
|
f.bufferSize = p.logbufferSize
|
|
|
|
|
|
|
|
if err := f.Open(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return f, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// openIndexFile opens a log file and appends it to the index.
|
|
|
|
func (p *Partition) openIndexFile(path string) (*IndexFile, error) {
|
|
|
|
f := NewIndexFile(p.sfile)
|
|
|
|
f.SetPath(path)
|
|
|
|
if err := f.Open(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return f, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// deleteNonManifestFiles removes all files not in the manifest.
|
|
|
|
func (p *Partition) deleteNonManifestFiles(m *Manifest) error {
|
|
|
|
dir, err := os.Open(p.path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer dir.Close()
|
|
|
|
|
|
|
|
fis, err := dir.Readdir(-1)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Loop over all files and remove any not in the manifest.
|
|
|
|
for _, fi := range fis {
|
|
|
|
filename := filepath.Base(fi.Name())
|
2019-04-12 21:52:56 +00:00
|
|
|
if filename == ManifestFileName || m.HasFile(filename) {
|
2018-10-01 11:08:33 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.RemoveAll(filename); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return dir.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Partition) buildSeriesSet() error {
|
|
|
|
p.seriesIDSet = tsdb.NewSeriesIDSet()
|
|
|
|
|
|
|
|
// Read series sets from files in reverse.
|
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
|
|
|
for i := len(p.fileSet.files) - 1; i >= 0; i-- {
|
|
|
|
f := p.fileSet.files[i]
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// Delete anything that's been tombstoned.
|
|
|
|
ts, err := f.TombstoneSeriesIDSet()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
p.seriesIDSet.Diff(ts)
|
|
|
|
|
|
|
|
// Add series created within the file.
|
|
|
|
ss, err := f.SeriesIDSet()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
p.seriesIDSet.Merge(ss)
|
|
|
|
}
|
|
|
|
|
|
|
|
return 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
|
|
|
// Close closes the partition.
|
2018-10-01 11:08:33 +00:00
|
|
|
func (p *Partition) Close() error {
|
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
|
|
|
p.resmu.Lock()
|
|
|
|
defer p.resmu.Unlock()
|
2018-10-01 11:08:33 +00:00
|
|
|
|
2019-09-02 15:37:34 +00:00
|
|
|
// Close the resource.
|
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
|
|
|
p.res.Close()
|
2019-09-02 15:37:34 +00:00
|
|
|
p.Wait()
|
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
|
|
|
|
|
|
|
// There are now no internal outstanding callers holding a reference
|
|
|
|
// so we can acquire this mutex to protect against external callers.
|
2018-10-01 11:08:33 +00:00
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
|
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
|
|
|
return p.close()
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
|
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 does the work of closing and cleaning up the partition after it
|
|
|
|
// has acquired locks and ensured no one is using it.
|
|
|
|
func (p *Partition) close() error {
|
|
|
|
// Release series file.
|
|
|
|
if p.sfileref != nil {
|
|
|
|
p.sfileref.Release()
|
|
|
|
p.sfileref = nil
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
|
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
|
|
|
// Release the file set and close all of the files.
|
|
|
|
var err error
|
|
|
|
if p.fileSet != nil {
|
|
|
|
p.fileSet.Release()
|
|
|
|
for _, file := range p.fileSet.files {
|
|
|
|
if e := file.Close(); e != nil && err == nil {
|
|
|
|
err = e
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
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
|
|
|
p.fileSet = nil
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Path returns the path to the partition.
|
|
|
|
func (p *Partition) Path() string { return p.path }
|
|
|
|
|
|
|
|
// SeriesFile returns the attached series file.
|
2020-03-12 18:32:52 +00:00
|
|
|
func (p *Partition) SeriesFile() *seriesfile.SeriesFile { return p.sfile }
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// NextSequence returns the next file identifier.
|
|
|
|
func (p *Partition) NextSequence() int {
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
return p.nextSequence()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Partition) nextSequence() int {
|
|
|
|
p.seq++
|
|
|
|
return p.seq
|
|
|
|
}
|
|
|
|
|
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
|
|
|
// manifestPath returns the path to the index's manifest file.
|
|
|
|
func (p *Partition) manifestPath() string {
|
2018-10-01 11:08:33 +00:00
|
|
|
return filepath.Join(p.path, ManifestFileName)
|
|
|
|
}
|
|
|
|
|
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
|
|
|
// Manifest returns a Manifest for the partition given a file set.
|
|
|
|
func (p *Partition) Manifest(fs *FileSet) *Manifest {
|
|
|
|
p.mu.RLock()
|
|
|
|
defer p.mu.RUnlock()
|
|
|
|
|
|
|
|
return p.manifest(fs)
|
|
|
|
}
|
|
|
|
|
|
|
|
// manifest returns a Manifest for the partition given a file set. It
|
|
|
|
// requires that at least a read lock is held.
|
|
|
|
func (p *Partition) manifest(fs *FileSet) *Manifest {
|
2018-10-01 11:08:33 +00:00
|
|
|
m := &Manifest{
|
|
|
|
Levels: p.levels,
|
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
|
|
|
Files: make([]string, len(fs.files)),
|
2018-10-01 11:08:33 +00:00
|
|
|
Version: p.version,
|
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
|
|
|
path: p.manifestPath(),
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
for j, f := range fs.files {
|
2018-10-01 11:08:33 +00:00
|
|
|
m.Files[j] = filepath.Base(f.Path())
|
|
|
|
}
|
|
|
|
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithLogger sets the logger for the index.
|
|
|
|
func (p *Partition) WithLogger(logger *zap.Logger) {
|
|
|
|
p.logger = logger.With(zap.String("index", "tsi"))
|
|
|
|
}
|
|
|
|
|
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
|
|
|
// FileSet returns a copy of the current file set. You must call Release on it when
|
|
|
|
// you are finished.
|
|
|
|
func (p *Partition) FileSet() (*FileSet, error) {
|
|
|
|
p.mu.RLock()
|
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
|
|
|
fs, err := p.fileSet.Duplicate()
|
|
|
|
p.mu.RUnlock()
|
|
|
|
return fs, err
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
// replaceFileSet is a helper to replace the file set of the partition. It releases
|
|
|
|
// the resources on the old file set before replacing it with the new one.
|
|
|
|
func (p *Partition) replaceFileSet(fs *FileSet) {
|
|
|
|
p.fileSet.Release()
|
|
|
|
p.fileSet = fs
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FileN returns the active files in the file set.
|
|
|
|
func (p *Partition) FileN() int { return len(p.fileSet.files) }
|
|
|
|
|
|
|
|
// prependActiveLogFile adds a new log file so that the current log file can be compacted.
|
|
|
|
func (p *Partition) prependActiveLogFile() error {
|
|
|
|
// Open file and insert it into the first position.
|
|
|
|
f, err := p.openLogFile(filepath.Join(p.path, FormatLogFileName(p.nextSequence())))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prepend and generate new fileset.
|
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
|
|
|
fileSet, err := p.fileSet.PrependLogFile(f)
|
|
|
|
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.Close()
|
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
|
|
|
return err
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// Write new manifest.
|
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
|
|
|
manifestSize, err := p.manifest(fileSet).Write()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
// TODO: Close index if write fails.
|
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
|
|
|
fileSet.Release()
|
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.Close()
|
2018-10-01 11:08:33 +00:00
|
|
|
return err
|
|
|
|
}
|
2018-10-15 15:13:54 +00:00
|
|
|
|
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
|
|
|
// Now that we can no longer error, update the partition state.
|
|
|
|
p.activeLogFile = f
|
|
|
|
p.replaceFileSet(fileSet)
|
2018-10-01 11:08:33 +00:00
|
|
|
p.manifestSize = manifestSize
|
2018-10-15 15:13:54 +00:00
|
|
|
|
2018-11-29 14:13:16 +00:00
|
|
|
// Set the file metrics again.
|
|
|
|
p.tracker.SetFiles(uint64(len(p.fileSet.IndexFiles())), "index")
|
|
|
|
p.tracker.SetFiles(uint64(len(p.fileSet.LogFiles())), "log")
|
|
|
|
p.tracker.SetDiskSize(uint64(p.fileSet.Size()))
|
2018-10-01 11:08:33 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ForEachMeasurementName iterates over all measurement names in the index.
|
|
|
|
func (p *Partition) ForEachMeasurementName(fn func(name []byte) error) 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
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer fs.Release()
|
|
|
|
|
|
|
|
itr := fs.MeasurementIterator()
|
|
|
|
if itr == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for e := itr.Next(); e != nil; e = itr.Next() {
|
|
|
|
if err := fn(e.Name()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MeasurementHasSeries returns true if a measurement has at least one non-tombstoned series.
|
|
|
|
func (p *Partition) MeasurementHasSeries(name []byte) (bool, 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
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
defer fs.Release()
|
|
|
|
|
|
|
|
for _, f := range fs.files {
|
|
|
|
if f.MeasurementHasSeries(p.seriesIDSet, name) {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MeasurementIterator returns an iterator over all measurement names.
|
|
|
|
func (p *Partition) MeasurementIterator() (tsdb.MeasurementIterator, 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
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, 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
|
|
|
return newFileSetMeasurementIterator(fs,
|
|
|
|
NewTSDBMeasurementIteratorAdapter(fs.MeasurementIterator())), nil
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// MeasurementExists returns true if a measurement exists.
|
|
|
|
func (p *Partition) MeasurementExists(name []byte) (bool, 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
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
defer fs.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
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
m := fs.Measurement(name)
|
|
|
|
return m != nil && !m.Deleted(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Partition) MeasurementNamesByRegex(re *regexp.Regexp) ([][]byte, 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
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer fs.Release()
|
|
|
|
|
|
|
|
itr := fs.MeasurementIterator()
|
|
|
|
if itr == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var a [][]byte
|
|
|
|
for e := itr.Next(); e != nil; e = itr.Next() {
|
|
|
|
if re.Match(e.Name()) {
|
|
|
|
// Clone bytes since they will be used after the fileset is released.
|
|
|
|
a = append(a, bytesutil.Clone(e.Name()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Partition) MeasurementSeriesIDIterator(name []byte) (tsdb.SeriesIDIterator, 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
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return newFileSetSeriesIDIterator(fs, fs.MeasurementSeriesIDIterator(name)), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DropMeasurement deletes a measurement from the index. DropMeasurement does
|
|
|
|
// not remove any series from the index directly.
|
|
|
|
func (p *Partition) DropMeasurement(name []byte) 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
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer fs.Release()
|
|
|
|
|
|
|
|
// Delete all keys and values.
|
|
|
|
if kitr := fs.TagKeyIterator(name); kitr != nil {
|
|
|
|
for k := kitr.Next(); k != nil; k = kitr.Next() {
|
|
|
|
// Delete key if not already deleted.
|
|
|
|
if !k.Deleted() {
|
|
|
|
if err := func() error {
|
|
|
|
p.mu.RLock()
|
|
|
|
defer p.mu.RUnlock()
|
2020-03-18 21:47:35 +00:00
|
|
|
return p.activeLogFile.DeleteTagKeyNoSync(name, k.Key())
|
2018-10-01 11:08:33 +00:00
|
|
|
}(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete each value in key.
|
|
|
|
if vitr := k.TagValueIterator(); vitr != nil {
|
|
|
|
for v := vitr.Next(); v != nil; v = vitr.Next() {
|
|
|
|
if !v.Deleted() {
|
|
|
|
if err := func() error {
|
|
|
|
p.mu.RLock()
|
|
|
|
defer p.mu.RUnlock()
|
2020-03-18 21:47:35 +00:00
|
|
|
return p.activeLogFile.DeleteTagValueNoSync(name, k.Key(), v.Value())
|
2018-10-01 11:08:33 +00:00
|
|
|
}(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete all series.
|
2019-01-14 12:16:17 +00:00
|
|
|
// TODO(edd): it's not clear to me why we have to delete all series IDs from
|
|
|
|
// the index when we could just mark the measurement as deleted.
|
2018-10-01 11:08:33 +00:00
|
|
|
if itr := fs.MeasurementSeriesIDIterator(name); itr != nil {
|
|
|
|
defer itr.Close()
|
2019-01-14 12:16:17 +00:00
|
|
|
|
|
|
|
// 1024 is assuming that typically a bucket (measurement) will have at least
|
|
|
|
// 1024 series in it.
|
|
|
|
all := make([]tsdb.SeriesID, 0, 1024)
|
2018-10-01 11:08:33 +00:00
|
|
|
for {
|
|
|
|
elem, err := itr.Next()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if elem.SeriesID.IsZero() {
|
|
|
|
break
|
|
|
|
}
|
2019-01-14 12:16:17 +00:00
|
|
|
all = append(all, elem.SeriesID)
|
2019-01-11 19:28:46 +00:00
|
|
|
|
|
|
|
// Update series set.
|
|
|
|
p.seriesIDSet.Remove(elem.SeriesID)
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
2019-01-11 19:28:46 +00:00
|
|
|
|
2019-01-14 12:16:17 +00:00
|
|
|
if err := p.activeLogFile.DeleteSeriesIDList(all); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
p.tracker.AddSeriesDropped(uint64(len(all)))
|
|
|
|
p.tracker.SubSeries(uint64(len(all)))
|
2019-01-11 19:28:46 +00:00
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
if err = itr.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mark measurement as deleted.
|
|
|
|
if err := func() error {
|
|
|
|
p.mu.RLock()
|
|
|
|
defer p.mu.RUnlock()
|
|
|
|
return p.activeLogFile.DeleteMeasurement(name)
|
|
|
|
}(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-03-18 21:47:35 +00:00
|
|
|
// Ensure log is flushed & synced.
|
|
|
|
if err := func() error {
|
|
|
|
p.mu.RLock()
|
|
|
|
defer p.mu.RUnlock()
|
|
|
|
return p.activeLogFile.FlushAndSync()
|
|
|
|
}(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
// Check if the log file needs to be swapped.
|
|
|
|
if err := p.CheckLogFile(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// createSeriesListIfNotExists creates a list of series if they doesn't exist in
|
|
|
|
// bulk.
|
2018-09-28 12:47:08 +00:00
|
|
|
func (p *Partition) createSeriesListIfNotExists(collection *tsdb.SeriesCollection) ([]tsdb.SeriesID, error) {
|
2018-10-01 11:08:33 +00:00
|
|
|
// Is there anything to do? The partition may have been sent an empty batch.
|
|
|
|
if collection.Length() == 0 {
|
2018-09-28 12:47:08 +00:00
|
|
|
return nil, nil
|
|
|
|
} else if len(collection.Names) != len(collection.Tags) {
|
|
|
|
return nil, fmt.Errorf("uneven batch, partition %s sent %d names and %d tags", p.id, len(collection.Names), len(collection.Tags))
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
// Ensure fileset cannot change during insert.
|
|
|
|
now := time.Now()
|
|
|
|
p.mu.RLock()
|
|
|
|
|
|
|
|
// Try to acquire a resource on the active log file
|
|
|
|
res, err := p.activeLogFile.Acquire()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != 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
|
|
|
p.mu.RUnlock()
|
2018-09-28 12:47:08 +00:00
|
|
|
return nil, err
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Insert series into log file.
|
2018-09-28 12:47:08 +00:00
|
|
|
ids, err := p.activeLogFile.AddSeriesList(p.seriesIDSet, collection)
|
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
|
|
|
|
|
|
|
// Release our resources.
|
|
|
|
res.Release()
|
|
|
|
p.mu.RUnlock()
|
|
|
|
|
|
|
|
// Check the error from insert.
|
2018-09-28 12:47:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
2018-09-28 12:47:08 +00:00
|
|
|
if err := p.CheckLogFile(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-11-29 14:13:16 +00:00
|
|
|
|
|
|
|
// NOTE(edd): if this becomes expensive then we can move the count into the
|
|
|
|
// log file.
|
|
|
|
var totalNew uint64
|
|
|
|
for _, id := range ids {
|
|
|
|
if !id.IsZero() {
|
|
|
|
totalNew++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if totalNew > 0 {
|
|
|
|
p.tracker.AddSeriesCreated(totalNew, time.Since(now))
|
|
|
|
p.tracker.AddSeries(totalNew)
|
2018-11-29 16:04:52 +00:00
|
|
|
p.mu.RLock()
|
2018-11-29 14:13:16 +00:00
|
|
|
p.tracker.SetDiskSize(uint64(p.fileSet.Size()))
|
2018-11-29 16:04:52 +00:00
|
|
|
p.mu.RUnlock()
|
2018-11-29 14:13:16 +00:00
|
|
|
}
|
2018-09-28 12:47:08 +00:00
|
|
|
return ids, nil
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 21:47:35 +00:00
|
|
|
// DropSeries removes the provided set of series id from the index.
|
|
|
|
func (p *Partition) DropSeries(ids []tsdb.SeriesID) error {
|
|
|
|
// Count total affected series.
|
|
|
|
var n uint64
|
|
|
|
for _, id := range ids {
|
|
|
|
if p.seriesIDSet.Contains(id) {
|
|
|
|
n++
|
|
|
|
}
|
2018-10-15 15:13:54 +00:00
|
|
|
}
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
// Delete series from index.
|
2020-03-18 21:47:35 +00:00
|
|
|
if err := p.activeLogFile.DeleteSeriesIDs(ids); err != nil {
|
2018-10-01 11:08:33 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-10-15 15:13:54 +00:00
|
|
|
// Update series set.
|
2020-03-18 21:47:35 +00:00
|
|
|
for _, id := range ids {
|
|
|
|
p.seriesIDSet.Remove(id)
|
|
|
|
}
|
|
|
|
p.tracker.AddSeriesDropped(n)
|
|
|
|
p.tracker.SubSeries(n)
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// Swap log file, if necessary.
|
|
|
|
return p.CheckLogFile()
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasTagKey returns true if tag key exists.
|
|
|
|
func (p *Partition) HasTagKey(name, key []byte) (bool, 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
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
defer fs.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
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
return fs.HasTagKey(name, key), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasTagValue returns true if tag value exists.
|
|
|
|
func (p *Partition) HasTagValue(name, key, value []byte) (bool, 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
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
defer fs.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
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
return fs.HasTagValue(name, key, value), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TagKeyIterator returns an iterator for all keys across a single measurement.
|
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 (p *Partition) TagKeyIterator(name []byte) (tsdb.TagKeyIterator, error) {
|
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != 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
|
|
|
return nil, err
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
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
|
|
|
return newFileSetTagKeyIterator(fs,
|
|
|
|
NewTSDBTagKeyIteratorAdapter(fs.TagKeyIterator(name))), nil
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TagValueIterator returns an iterator for all values across a single key.
|
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 (p *Partition) TagValueIterator(name, key []byte) (tsdb.TagValueIterator, error) {
|
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != 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
|
|
|
return nil, err
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
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
|
|
|
return newFileSetTagValueIterator(fs,
|
|
|
|
NewTSDBTagValueIteratorAdapter(fs.TagValueIterator(name, key))), nil
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TagKeySeriesIDIterator returns a series iterator for all values across a single key.
|
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 (p *Partition) TagKeySeriesIDIterator(name, key []byte) (tsdb.SeriesIDIterator, error) {
|
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != 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
|
|
|
return nil, err
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
2019-11-12 13:17:38 +00:00
|
|
|
|
|
|
|
itr, err := fs.TagKeySeriesIDIterator(name, key)
|
|
|
|
if err != nil {
|
|
|
|
fs.Release()
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return newFileSetSeriesIDIterator(fs, itr), nil
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TagValueSeriesIDIterator returns a series iterator for a single key value.
|
|
|
|
func (p *Partition) TagValueSeriesIDIterator(name, key, value []byte) (tsdb.SeriesIDIterator, 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
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
itr, err := fs.TagValueSeriesIDIterator(name, key, value)
|
|
|
|
if err != nil {
|
|
|
|
fs.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
|
|
|
return nil, err
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
return newFileSetSeriesIDIterator(fs, itr), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MeasurementTagKeysByExpr extracts the tag keys wanted by the expression.
|
|
|
|
func (p *Partition) MeasurementTagKeysByExpr(name []byte, expr influxql.Expr) (map[string]struct{}, 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
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer fs.Release()
|
|
|
|
|
|
|
|
return fs.MeasurementTagKeysByExpr(name, expr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ForEachMeasurementTagKey iterates over all tag keys in a measurement.
|
|
|
|
func (p *Partition) ForEachMeasurementTagKey(name []byte, fn func(key []byte) error) 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
|
|
|
fs, err := p.FileSet()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer fs.Release()
|
|
|
|
|
|
|
|
itr := fs.TagKeyIterator(name)
|
|
|
|
if itr == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for e := itr.Next(); e != nil; e = itr.Next() {
|
|
|
|
if err := fn(e.Key()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TagKeyCardinality always returns zero.
|
|
|
|
// It is not possible to determine cardinality of tags across index files.
|
|
|
|
func (p *Partition) TagKeyCardinality(name, key []byte) int {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Partition) SetFieldName(measurement []byte, name string) {}
|
|
|
|
func (p *Partition) RemoveShard(shardID uint64) {}
|
|
|
|
func (p *Partition) AssignShard(k string, shardID uint64) {}
|
|
|
|
|
|
|
|
// Compact requests a compaction of log files.
|
|
|
|
func (p *Partition) Compact() {
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
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
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
p.compact()
|
|
|
|
}
|
|
|
|
|
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
|
|
|
// DisableCompactions stops any compactions from starting until a call to EnableCompactions.
|
2018-10-01 11:08:33 +00:00
|
|
|
func (p *Partition) DisableCompactions() {
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
|
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
|
|
|
p.compactionsDisabled++
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
// EnableCompactions allows compactions to proceed again after a call to DisableCompactions.
|
2018-10-01 11:08:33 +00:00
|
|
|
func (p *Partition) EnableCompactions() {
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
|
|
|
|
p.compactionsDisabled--
|
|
|
|
}
|
|
|
|
|
2019-09-02 15:37:34 +00:00
|
|
|
// CurrentCompactionN returns the number of compactions currently running.
|
|
|
|
func (p *Partition) CurrentCompactionN() int {
|
|
|
|
p.mu.RLock()
|
|
|
|
defer p.mu.RUnlock()
|
|
|
|
return p.currentCompactionN
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait will block until all compactions are finished.
|
|
|
|
// Must only be called while they are disabled.
|
|
|
|
func (p *Partition) Wait() {
|
2019-09-04 16:38:13 +00:00
|
|
|
if p.CurrentCompactionN() == 0 { // Is it possible to immediately return?
|
|
|
|
return
|
|
|
|
}
|
2019-09-02 15:37:34 +00:00
|
|
|
|
2019-09-04 16:38:13 +00:00
|
|
|
ticker := time.NewTicker(10 * time.Millisecond)
|
|
|
|
defer ticker.Stop()
|
2019-09-02 15:37:34 +00:00
|
|
|
for range ticker.C {
|
|
|
|
if p.CurrentCompactionN() == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// compact compacts continguous groups of files that are not currently compacting.
|
|
|
|
func (p *Partition) compact() {
|
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
|
|
|
if p.compactionsDisabled > 0 {
|
|
|
|
p.logger.Error("Cannot start a compaction while disabled")
|
2018-10-01 11:08:33 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
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
|
|
|
fs, err := p.fileSet.Duplicate()
|
|
|
|
if err != nil {
|
|
|
|
p.logger.Error("Attempt to compact while partition is closing", zap.Error(err))
|
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
|
|
|
return
|
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
|
|
|
defer fs.Release()
|
|
|
|
|
|
|
|
// Iterate over each level we are going to compact.
|
|
|
|
// We skip the first level (0) because it is log files and they are compacted separately.
|
|
|
|
// We skip the last level because the files have no higher level to compact into.
|
|
|
|
minLevel, maxLevel := 1, len(p.levels)-2
|
|
|
|
for level := minLevel; level <= maxLevel; level++ {
|
|
|
|
// Skip level if it is currently compacting.
|
|
|
|
if p.levelCompacting[level] {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collect contiguous files from the end of the level.
|
|
|
|
files := fs.LastContiguousIndexFilesByLevel(level)
|
|
|
|
if len(files) < 2 {
|
|
|
|
continue
|
|
|
|
} else if len(files) > MaxIndexMergeCount {
|
|
|
|
files = files[len(files)-MaxIndexMergeCount:]
|
|
|
|
}
|
|
|
|
|
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
|
|
|
// We intend to do a compaction. Acquire a resource to do so.
|
|
|
|
ref, err := p.res.Acquire()
|
|
|
|
if err != nil {
|
|
|
|
p.logger.Error("Attempt to compact while partition is closing", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
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 references to the files to keep them alive through compaction.
|
|
|
|
frefs, err := IndexFiles(files).Acquire()
|
|
|
|
if err != nil {
|
|
|
|
p.logger.Error("Attempt to compact a file that is closed", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// Mark the level as compacting.
|
|
|
|
p.levelCompacting[level] = true
|
|
|
|
|
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
|
|
|
// Start compacting in a separate goroutine.
|
2019-09-02 15:37:34 +00:00
|
|
|
p.currentCompactionN++
|
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
|
|
|
go func(level int) {
|
|
|
|
// Compact to a new level.
|
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
|
|
|
p.compactToLevel(files, frefs, level+1, ref.Closing())
|
2018-10-01 11:08:33 +00:00
|
|
|
|
2019-09-02 15:37:34 +00:00
|
|
|
// Ensure references are released.
|
|
|
|
frefs.Release()
|
|
|
|
ref.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
|
|
|
// Ensure compaction lock for the level is released.
|
|
|
|
p.mu.Lock()
|
|
|
|
p.levelCompacting[level] = false
|
2019-09-02 15:37:34 +00:00
|
|
|
p.currentCompactionN--
|
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
|
|
|
p.mu.Unlock()
|
2018-10-01 11:08:33 +00:00
|
|
|
|
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
|
|
|
// Check for new compactions
|
|
|
|
p.Compact()
|
|
|
|
}(level)
|
2018-10-01 11:08:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// compactToLevel compacts a set of files into a new file. Replaces old files with
|
|
|
|
// compacted file on successful completion. This runs in a separate goroutine.
|
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 (p *Partition) compactToLevel(files []*IndexFile, frefs lifecycle.References,
|
|
|
|
level int, interrupt <-chan struct{}) {
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
assert(len(files) >= 2, "at least two index files are required for compaction")
|
|
|
|
assert(level > 0, "cannot compact level zero")
|
2018-11-29 15:15:20 +00:00
|
|
|
|
2018-11-29 14:13:16 +00:00
|
|
|
var err error
|
|
|
|
var start time.Time
|
|
|
|
|
|
|
|
p.tracker.IncActiveCompaction(level)
|
|
|
|
// Set the relevant metrics at the end of any compaction.
|
|
|
|
defer func() {
|
2018-11-29 16:09:38 +00:00
|
|
|
p.mu.RLock()
|
|
|
|
defer p.mu.RUnlock()
|
2018-11-29 14:13:16 +00:00
|
|
|
p.tracker.SetFiles(uint64(len(p.fileSet.IndexFiles())), "index")
|
|
|
|
p.tracker.SetFiles(uint64(len(p.fileSet.LogFiles())), "log")
|
|
|
|
p.tracker.SetDiskSize(uint64(p.fileSet.Size()))
|
|
|
|
p.tracker.DecActiveCompaction(level)
|
|
|
|
|
|
|
|
success := err == nil
|
|
|
|
p.tracker.CompactionAttempted(level, success, time.Since(start))
|
|
|
|
}()
|
2018-11-29 15:15:20 +00:00
|
|
|
|
2019-05-01 01:51:22 +00:00
|
|
|
span, ctx := tracing.StartSpanFromContext(context.Background())
|
|
|
|
defer span.Finish()
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
// Build a logger for this compaction.
|
2019-05-01 01:51:22 +00:00
|
|
|
log, logEnd := logger.NewOperation(ctx, p.logger, "TSI level compaction", "tsi1_compact_to_level", zap.Int("tsi1_level", level))
|
2018-10-01 11:08:33 +00:00
|
|
|
defer logEnd()
|
|
|
|
|
|
|
|
// Check for cancellation.
|
|
|
|
select {
|
|
|
|
case <-interrupt:
|
|
|
|
log.Error("Cannot begin compaction", zap.Error(ErrCompactionInterrupted))
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
// Track time to compact.
|
2018-11-29 14:13:16 +00:00
|
|
|
start = time.Now()
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// Create new index file.
|
|
|
|
path := filepath.Join(p.path, FormatIndexFileName(p.NextSequence(), level))
|
2018-11-29 14:13:16 +00:00
|
|
|
var f *os.File
|
2019-07-08 17:01:42 +00:00
|
|
|
if f, err = fs.CreateFile(path); err != nil {
|
2018-10-01 11:08:33 +00:00
|
|
|
log.Error("Cannot create compaction files", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
log.Info("Performing full compaction",
|
|
|
|
zap.String("src", joinIntSlice(IndexFiles(files).IDs(), ",")),
|
|
|
|
zap.String("dst", path),
|
|
|
|
)
|
|
|
|
|
|
|
|
// Compact all index files to new index file.
|
|
|
|
lvl := p.levels[level]
|
2018-11-29 14:13:16 +00:00
|
|
|
var n int64
|
|
|
|
if n, err = IndexFiles(files).CompactTo(f, p.sfile, lvl.M, lvl.K, interrupt); err != nil {
|
2018-10-01 11:08:33 +00:00
|
|
|
log.Error("Cannot compact index files", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close file.
|
2018-11-29 14:13:16 +00:00
|
|
|
if err = f.Close(); err != nil {
|
2018-10-01 11:08:33 +00:00
|
|
|
log.Error("Error closing index file", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reopen as an index file.
|
|
|
|
file := NewIndexFile(p.sfile)
|
|
|
|
file.SetPath(path)
|
2018-11-29 14:13:16 +00:00
|
|
|
if err = file.Open(); err != nil {
|
2018-10-01 11:08:33 +00:00
|
|
|
log.Error("Cannot open new index file", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain lock to swap in index file and write manifest.
|
2018-11-29 14:13:16 +00:00
|
|
|
if err = func() error {
|
2018-10-01 11:08:33 +00:00
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
|
|
|
|
// Replace previous files with new index file.
|
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
|
|
|
fileSet, err := p.fileSet.MustReplace(IndexFiles(files).Files(), file)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// Write new manifest.
|
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
|
|
|
manifestSize, err := p.manifest(fileSet).Write()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
// TODO: Close index if write fails.
|
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
|
|
|
fileSet.Release()
|
2018-10-01 11:08:33 +00:00
|
|
|
return err
|
|
|
|
}
|
2018-10-15 15:13:54 +00:00
|
|
|
|
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
|
|
|
// Now that we can no longer error, update the local state.
|
|
|
|
p.replaceFileSet(fileSet)
|
2018-10-01 11:08:33 +00:00
|
|
|
p.manifestSize = manifestSize
|
2018-10-15 15:13:54 +00:00
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
return nil
|
|
|
|
}(); err != nil {
|
2019-04-12 21:52:56 +00:00
|
|
|
log.Error("Cannot write manifest", zap.Error(err))
|
2018-10-01 11:08:33 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
elapsed := time.Since(start)
|
|
|
|
log.Info("Full compaction complete",
|
|
|
|
zap.String("path", path),
|
|
|
|
logger.DurationLiteral("elapsed", elapsed),
|
|
|
|
zap.Int64("bytes", n),
|
|
|
|
zap.Int("kb_per_sec", int(float64(n)/elapsed.Seconds())/1024),
|
|
|
|
)
|
|
|
|
|
|
|
|
// Release old files.
|
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
|
|
|
frefs.Release()
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// Close and delete all old index files.
|
|
|
|
for _, f := range files {
|
|
|
|
log.Info("Removing index file", zap.String("path", f.Path()))
|
|
|
|
|
2018-11-29 14:13:16 +00:00
|
|
|
if err = f.Close(); err != nil {
|
2018-10-01 11:08:33 +00:00
|
|
|
log.Error("Cannot close index file", zap.Error(err))
|
|
|
|
return
|
2018-11-29 14:13:16 +00:00
|
|
|
} else if err = os.Remove(f.Path()); err != nil {
|
2018-10-01 11:08:33 +00:00
|
|
|
log.Error("Cannot remove index file", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Partition) CheckLogFile() error {
|
|
|
|
// Check log file size under read lock.
|
2018-10-05 11:44:06 +00:00
|
|
|
p.mu.RLock()
|
|
|
|
size := p.activeLogFile.Size()
|
|
|
|
p.mu.RUnlock()
|
|
|
|
|
|
|
|
if size < p.MaxLogFileSize {
|
2018-10-01 11:08:33 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// If file size exceeded then recheck under write lock and swap files.
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
return p.checkLogFile()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Partition) checkLogFile() error {
|
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
|
|
|
if p.compactionsDisabled > 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Acquire a reference to hold the partition open.
|
|
|
|
ref, err := p.res.Acquire()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
if p.activeLogFile.Size() < p.MaxLogFileSize {
|
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
|
|
|
ref.Release()
|
2018-10-01 11:08:33 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-05-01 01:51:22 +00:00
|
|
|
span, ctx := tracing.StartSpanFromContext(context.Background())
|
|
|
|
defer span.Finish()
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
// Swap current log file.
|
|
|
|
logFile := p.activeLogFile
|
|
|
|
|
|
|
|
// Open new log file and insert it into the first position.
|
|
|
|
if err := p.prependActiveLogFile(); 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
|
|
|
ref.Release()
|
2018-10-01 11:08:33 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Begin compacting in a background goroutine.
|
2019-09-02 15:37:34 +00:00
|
|
|
p.currentCompactionN++
|
2018-10-01 11:08:33 +00:00
|
|
|
go func() {
|
2019-05-01 01:51:22 +00:00
|
|
|
p.compactLogFile(ctx, logFile, ref.Closing())
|
2019-09-02 15:37:34 +00:00
|
|
|
ref.Release() // release our reference
|
|
|
|
|
|
|
|
p.mu.Lock()
|
|
|
|
p.currentCompactionN-- // compaction is now complete
|
|
|
|
p.mu.Unlock()
|
|
|
|
|
|
|
|
p.Compact() // check for new compactions
|
2018-10-01 11:08:33 +00:00
|
|
|
}()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// compactLogFile compacts f into a tsi file. The new file will share the
|
|
|
|
// same identifier but will have a ".tsi" extension. Once the log file is
|
|
|
|
// compacted then the manifest is updated and the log file is discarded.
|
2019-05-01 01:51:22 +00:00
|
|
|
func (p *Partition) compactLogFile(ctx context.Context, logFile *LogFile, interrupt <-chan struct{}) {
|
2018-11-29 16:09:38 +00:00
|
|
|
defer func() {
|
|
|
|
p.mu.RLock()
|
|
|
|
defer p.mu.RUnlock()
|
|
|
|
p.tracker.SetFiles(uint64(len(p.fileSet.IndexFiles())), "index")
|
|
|
|
p.tracker.SetFiles(uint64(len(p.fileSet.LogFiles())), "log")
|
|
|
|
p.tracker.SetDiskSize(uint64(p.fileSet.Size()))
|
|
|
|
}()
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
start := time.Now()
|
|
|
|
|
|
|
|
// Retrieve identifier from current path.
|
|
|
|
id := logFile.ID()
|
|
|
|
assert(id != 0, "cannot parse log file id: %s", logFile.Path())
|
|
|
|
|
|
|
|
// Build a logger for this compaction.
|
2019-05-01 01:51:22 +00:00
|
|
|
log, logEnd := logger.NewOperation(ctx, p.logger, "TSI log compaction", "tsi1_compact_log_file", zap.Int("tsi1_log_file_id", id))
|
2018-10-01 11:08:33 +00:00
|
|
|
defer logEnd()
|
|
|
|
|
|
|
|
// Create new index file.
|
|
|
|
path := filepath.Join(p.path, FormatIndexFileName(id, 1))
|
2019-07-08 17:01:42 +00:00
|
|
|
f, err := fs.CreateFile(path)
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Error("Cannot create index file", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
// Compact log file to new index file.
|
|
|
|
lvl := p.levels[1]
|
|
|
|
n, err := logFile.CompactTo(f, lvl.M, lvl.K, interrupt)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Cannot compact log file", zap.Error(err), zap.String("path", logFile.Path()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close file.
|
|
|
|
if err := f.Close(); err != nil {
|
|
|
|
log.Error("Cannot close log file", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reopen as an index file.
|
|
|
|
file := NewIndexFile(p.sfile)
|
|
|
|
file.SetPath(path)
|
|
|
|
if err := file.Open(); err != nil {
|
|
|
|
log.Error("Cannot open compacted index file", zap.Error(err), zap.String("path", file.Path()))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain lock to swap in index file and write manifest.
|
|
|
|
if err := func() error {
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
|
|
|
|
// Replace previous log file with index file.
|
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
|
|
|
fileSet, err := p.fileSet.MustReplace([]File{logFile}, file)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-10-01 11:08:33 +00:00
|
|
|
|
|
|
|
// Write new manifest.
|
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
|
|
|
manifestSize, err := p.manifest(fileSet).Write()
|
2018-10-01 11:08:33 +00:00
|
|
|
if err != nil {
|
|
|
|
// TODO: Close index if write fails.
|
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
|
|
|
fileSet.Release()
|
2018-10-01 11:08:33 +00:00
|
|
|
return err
|
|
|
|
}
|
2018-10-15 15:13:54 +00:00
|
|
|
|
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
|
|
|
// Now that we can no longer error, update the local state.
|
|
|
|
p.replaceFileSet(fileSet)
|
2018-10-01 11:08:33 +00:00
|
|
|
p.manifestSize = manifestSize
|
2018-10-15 15:13:54 +00:00
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
return nil
|
|
|
|
}(); err != nil {
|
2018-10-15 15:13:54 +00:00
|
|
|
log.Error("Cannot update manifest or stats", zap.Error(err))
|
2018-10-01 11:08:33 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
elapsed := time.Since(start)
|
|
|
|
log.Info("Log file compacted",
|
|
|
|
logger.DurationLiteral("elapsed", elapsed),
|
|
|
|
zap.Int64("bytes", n),
|
|
|
|
zap.Int("kb_per_sec", int(float64(n)/elapsed.Seconds())/1024),
|
|
|
|
)
|
|
|
|
|
|
|
|
// Closing the log file will automatically wait until the ref count is zero.
|
|
|
|
if err := logFile.Close(); err != nil {
|
|
|
|
log.Error("Cannot close log file", zap.Error(err))
|
|
|
|
return
|
|
|
|
} else if err := os.Remove(logFile.Path()); err != nil {
|
|
|
|
log.Error("Cannot remove log file", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-15 15:13:54 +00:00
|
|
|
// MeasurementCardinalityStats returns cardinality stats for all measurements.
|
2019-09-04 18:53:05 +00:00
|
|
|
func (p *Partition) MeasurementCardinalityStats() (MeasurementCardinalityStats, error) {
|
2018-10-15 15:13:54 +00:00
|
|
|
p.mu.RLock()
|
|
|
|
defer p.mu.RUnlock()
|
|
|
|
|
2019-09-04 18:53:05 +00:00
|
|
|
// Return cached version, if enabled and the TTL is less than the last cache time.
|
|
|
|
if p.StatsTTL > 0 && !p.lastStatsTime.IsZero() && time.Since(p.lastStatsTime) < p.StatsTTL {
|
|
|
|
return p.statsCache.Clone(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// If cache is unavailable then generate fresh stats.
|
|
|
|
stats, err := p.measurementCardinalityStats()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-10-15 15:13:54 +00:00
|
|
|
|
2019-09-04 18:53:05 +00:00
|
|
|
// Cache the stats if enabled.
|
|
|
|
if p.StatsTTL > 0 {
|
|
|
|
p.statsCache = stats
|
|
|
|
p.lastStatsTime = time.Now()
|
2018-10-15 15:13:54 +00:00
|
|
|
}
|
|
|
|
|
2019-09-04 18:53:05 +00:00
|
|
|
return stats, nil
|
|
|
|
}
|
2019-04-12 21:52:56 +00:00
|
|
|
|
2019-09-04 18:53:05 +00:00
|
|
|
func (p *Partition) measurementCardinalityStats() (MeasurementCardinalityStats, error) {
|
2019-04-12 21:52:56 +00:00
|
|
|
fs, err := p.fileSet.Duplicate()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer fs.Release()
|
|
|
|
|
2019-09-04 18:53:05 +00:00
|
|
|
stats := make(MeasurementCardinalityStats)
|
|
|
|
mitr := fs.MeasurementIterator()
|
|
|
|
if mitr == nil {
|
|
|
|
return stats, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
// Iterate over each measurement and set cardinality.
|
|
|
|
mm := mitr.Next()
|
|
|
|
if mm == nil {
|
|
|
|
return stats, nil
|
|
|
|
}
|
2019-04-12 21:52:56 +00:00
|
|
|
|
2019-09-04 18:53:05 +00:00
|
|
|
// Obtain all series for measurement.
|
|
|
|
sitr := fs.MeasurementSeriesIDIterator(mm.Name())
|
|
|
|
if sitr == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// All iterators should be series id set iterators except legacy 1.x data.
|
|
|
|
// Skip if it does not conform as aggregation would be too slow.
|
|
|
|
ssitr, ok := sitr.(tsdb.SeriesIDSetIterator)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Intersect with partition set to ensure deleted series are removed.
|
|
|
|
set := p.seriesIDSet.And(ssitr.SeriesIDSet())
|
|
|
|
cardinality := int(set.Cardinality())
|
|
|
|
if cardinality == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set cardinality for the given measurement.
|
|
|
|
stats[string(mm.Name())] = cardinality
|
|
|
|
}
|
2019-04-12 21:52:56 +00:00
|
|
|
}
|
|
|
|
|
2018-11-29 14:13:16 +00:00
|
|
|
type partitionTracker struct {
|
|
|
|
metrics *partitionMetrics
|
2018-12-07 13:48:43 +00:00
|
|
|
labels prometheus.Labels
|
2018-12-10 15:01:34 +00:00
|
|
|
enabled bool // Allows tracker to be disabled.
|
2018-11-29 14:13:16 +00:00
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
func newPartitionTracker(metrics *partitionMetrics, defaultLabels prometheus.Labels) *partitionTracker {
|
2018-11-29 14:13:16 +00:00
|
|
|
return &partitionTracker{
|
|
|
|
metrics: metrics,
|
2018-12-07 13:48:43 +00:00
|
|
|
labels: defaultLabels,
|
2018-12-10 15:01:34 +00:00
|
|
|
enabled: true,
|
2018-11-29 14:13:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
// Labels returns a copy of labels for use with index partition metrics.
|
|
|
|
func (t *partitionTracker) Labels() prometheus.Labels {
|
|
|
|
l := make(map[string]string, len(t.labels))
|
|
|
|
for k, v := range t.labels {
|
|
|
|
l[k] = v
|
|
|
|
}
|
|
|
|
return l
|
|
|
|
}
|
|
|
|
|
2018-11-29 14:13:16 +00:00
|
|
|
// AddSeriesCreated increases the number of series created in the partition by n
|
|
|
|
// and sets a sample of the time taken to create a series.
|
|
|
|
func (t *partitionTracker) AddSeriesCreated(n uint64, d time.Duration) {
|
2018-12-10 15:01:34 +00:00
|
|
|
if !t.enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
labels := t.Labels()
|
2018-11-29 14:13:16 +00:00
|
|
|
t.metrics.SeriesCreated.With(labels).Add(float64(n))
|
|
|
|
|
|
|
|
if n == 0 {
|
|
|
|
return // Nothing to record
|
|
|
|
}
|
|
|
|
|
|
|
|
perseries := d.Seconds() / float64(n)
|
|
|
|
t.metrics.SeriesCreatedDuration.With(labels).Observe(perseries)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddSeriesDropped increases the number of series dropped in the partition by n.
|
|
|
|
func (t *partitionTracker) AddSeriesDropped(n uint64) {
|
2018-12-10 15:01:34 +00:00
|
|
|
if !t.enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
labels := t.Labels()
|
2018-11-29 14:13:16 +00:00
|
|
|
t.metrics.SeriesDropped.With(labels).Add(float64(n))
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetSeries sets the number of series in the partition.
|
|
|
|
func (t *partitionTracker) SetSeries(n uint64) {
|
2018-12-10 15:01:34 +00:00
|
|
|
if !t.enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
labels := t.Labels()
|
2018-11-29 14:13:16 +00:00
|
|
|
t.metrics.Series.With(labels).Set(float64(n))
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddSeries increases the number of series in the partition by n.
|
|
|
|
func (t *partitionTracker) AddSeries(n uint64) {
|
2018-12-10 15:01:34 +00:00
|
|
|
if !t.enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
labels := t.Labels()
|
2018-11-29 14:13:16 +00:00
|
|
|
t.metrics.Series.With(labels).Add(float64(n))
|
|
|
|
}
|
|
|
|
|
|
|
|
// SubSeries decreases the number of series in the partition by n.
|
|
|
|
func (t *partitionTracker) SubSeries(n uint64) {
|
2018-12-10 15:01:34 +00:00
|
|
|
if !t.enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
labels := t.Labels()
|
2018-11-29 14:13:16 +00:00
|
|
|
t.metrics.Series.With(labels).Sub(float64(n))
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetMeasurements sets the number of measurements in the partition.
|
|
|
|
func (t *partitionTracker) SetMeasurements(n uint64) {
|
2018-12-10 15:01:34 +00:00
|
|
|
if !t.enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
labels := t.Labels()
|
2018-11-29 14:13:16 +00:00
|
|
|
t.metrics.Measurements.With(labels).Set(float64(n))
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddMeasurements increases the number of measurements in the partition by n.
|
|
|
|
func (t *partitionTracker) AddMeasurements(n uint64) {
|
2018-12-10 15:01:34 +00:00
|
|
|
if !t.enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
labels := t.Labels()
|
2018-11-29 14:13:16 +00:00
|
|
|
t.metrics.Measurements.With(labels).Add(float64(n))
|
|
|
|
}
|
|
|
|
|
|
|
|
// SubMeasurements decreases the number of measurements in the partition by n.
|
|
|
|
func (t *partitionTracker) SubMeasurements(n uint64) {
|
2018-12-10 15:01:34 +00:00
|
|
|
if !t.enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
labels := t.Labels()
|
2018-11-29 14:13:16 +00:00
|
|
|
t.metrics.Measurements.With(labels).Sub(float64(n))
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetFiles sets the number of files in the partition.
|
|
|
|
func (t *partitionTracker) SetFiles(n uint64, typ string) {
|
2018-12-10 15:01:34 +00:00
|
|
|
if !t.enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
labels := t.Labels()
|
2018-11-29 14:13:16 +00:00
|
|
|
labels["type"] = typ
|
|
|
|
t.metrics.FilesTotal.With(labels).Set(float64(n))
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetDiskSize sets the size of files in the partition.
|
|
|
|
func (t *partitionTracker) SetDiskSize(n uint64) {
|
2018-12-10 15:01:34 +00:00
|
|
|
if !t.enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
labels := t.Labels()
|
2018-11-29 14:13:16 +00:00
|
|
|
t.metrics.DiskSize.With(labels).Set(float64(n))
|
|
|
|
}
|
|
|
|
|
|
|
|
// IncActiveCompaction increments the number of active compactions for the provided level.
|
|
|
|
func (t *partitionTracker) IncActiveCompaction(level int) {
|
2018-12-10 15:01:34 +00:00
|
|
|
if !t.enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
labels := t.Labels()
|
2018-11-29 14:13:16 +00:00
|
|
|
labels["level"] = fmt.Sprint(level)
|
|
|
|
|
|
|
|
t.metrics.CompactionsActive.With(labels).Inc()
|
|
|
|
}
|
|
|
|
|
|
|
|
// DecActiveCompaction decrements the number of active compactions for the provided level.
|
|
|
|
func (t *partitionTracker) DecActiveCompaction(level int) {
|
2018-12-10 15:01:34 +00:00
|
|
|
if !t.enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
labels := t.Labels()
|
2018-11-29 14:13:16 +00:00
|
|
|
labels["level"] = fmt.Sprint(level)
|
|
|
|
|
|
|
|
t.metrics.CompactionsActive.With(labels).Dec()
|
|
|
|
}
|
|
|
|
|
|
|
|
// CompactionAttempted updates the number of compactions attempted for the provided level.
|
|
|
|
func (t *partitionTracker) CompactionAttempted(level int, success bool, d time.Duration) {
|
2018-12-10 15:01:34 +00:00
|
|
|
if !t.enabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:48:43 +00:00
|
|
|
labels := t.Labels()
|
2018-11-29 14:13:16 +00:00
|
|
|
labels["level"] = fmt.Sprint(level)
|
|
|
|
if success {
|
|
|
|
t.metrics.CompactionDuration.With(labels).Observe(d.Seconds())
|
|
|
|
|
|
|
|
labels["status"] = "ok"
|
|
|
|
t.metrics.Compactions.With(labels).Inc()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
labels["status"] = "error"
|
|
|
|
t.metrics.Compactions.With(labels).Inc()
|
|
|
|
}
|
|
|
|
|
2018-10-01 11:08:33 +00:00
|
|
|
// unionStringSets returns the union of two sets
|
|
|
|
func unionStringSets(a, b map[string]struct{}) map[string]struct{} {
|
|
|
|
other := make(map[string]struct{})
|
|
|
|
for k := range a {
|
|
|
|
other[k] = struct{}{}
|
|
|
|
}
|
|
|
|
for k := range b {
|
|
|
|
other[k] = struct{}{}
|
|
|
|
}
|
|
|
|
return other
|
|
|
|
}
|
|
|
|
|
|
|
|
// intersectStringSets returns the intersection of two sets.
|
|
|
|
func intersectStringSets(a, b map[string]struct{}) map[string]struct{} {
|
|
|
|
if len(a) < len(b) {
|
|
|
|
a, b = b, a
|
|
|
|
}
|
|
|
|
|
|
|
|
other := make(map[string]struct{})
|
|
|
|
for k := range a {
|
|
|
|
if _, ok := b[k]; ok {
|
|
|
|
other[k] = struct{}{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return other
|
|
|
|
}
|
|
|
|
|
|
|
|
var fileIDRegex = regexp.MustCompile(`^L(\d+)-(\d+)\..+$`)
|
|
|
|
|
|
|
|
// ParseFilename extracts the numeric id from a log or index file path.
|
|
|
|
// Returns 0 if it cannot be parsed.
|
|
|
|
func ParseFilename(name string) (level, id int) {
|
|
|
|
a := fileIDRegex.FindStringSubmatch(filepath.Base(name))
|
|
|
|
if a == nil {
|
|
|
|
return 0, 0
|
|
|
|
}
|
|
|
|
|
|
|
|
level, _ = strconv.Atoi(a[1])
|
|
|
|
id, _ = strconv.Atoi(a[2])
|
|
|
|
return id, level
|
|
|
|
}
|
|
|
|
|
|
|
|
// Manifest represents the list of log & index files that make up the index.
|
|
|
|
// The files are listed in time order, not necessarily ID order.
|
|
|
|
type Manifest struct {
|
|
|
|
Levels []CompactionLevel `json:"levels,omitempty"`
|
|
|
|
Files []string `json:"files,omitempty"`
|
|
|
|
|
|
|
|
// Version should be updated whenever the TSI format has changed.
|
|
|
|
Version int `json:"version,omitempty"`
|
|
|
|
|
|
|
|
path string // location on disk of the manifest.
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewManifest returns a new instance of Manifest with default compaction levels.
|
|
|
|
func NewManifest(path string) *Manifest {
|
|
|
|
m := &Manifest{
|
|
|
|
Levels: make([]CompactionLevel, len(DefaultCompactionLevels)),
|
|
|
|
Version: Version,
|
|
|
|
path: path,
|
|
|
|
}
|
|
|
|
copy(m.Levels, DefaultCompactionLevels[:])
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasFile returns true if name is listed in the log files or index files.
|
|
|
|
func (m *Manifest) HasFile(name string) bool {
|
|
|
|
for _, filename := range m.Files {
|
|
|
|
if filename == name {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate checks if the Manifest's version is compatible with this version
|
|
|
|
// of the tsi1 index.
|
|
|
|
func (m *Manifest) Validate() error {
|
|
|
|
// If we don't have an explicit version in the manifest file then we know
|
|
|
|
// it's not compatible with the latest tsi1 Index.
|
|
|
|
if m.Version != Version {
|
|
|
|
return ErrIncompatibleVersion
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write writes the manifest file to the provided path, returning the number of
|
|
|
|
// bytes written and an error, if any.
|
|
|
|
func (m *Manifest) Write() (int64, error) {
|
|
|
|
buf, err := json.MarshalIndent(m, "", " ")
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
buf = append(buf, '\n')
|
|
|
|
|
|
|
|
if err := ioutil.WriteFile(m.path, buf, 0666); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return int64(len(buf)), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadManifestFile reads a manifest from a file path and returns the Manifest,
|
|
|
|
// the size of the manifest on disk, and any error if appropriate.
|
|
|
|
func ReadManifestFile(path string) (*Manifest, int64, error) {
|
|
|
|
buf, err := ioutil.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decode manifest.
|
|
|
|
var m Manifest
|
|
|
|
if err := json.Unmarshal(buf, &m); err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the path of the manifest.
|
|
|
|
m.path = path
|
|
|
|
return &m, int64(len(buf)), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func joinIntSlice(a []int, sep string) string {
|
|
|
|
other := make([]string, len(a))
|
|
|
|
for i := range a {
|
|
|
|
other[i] = strconv.Itoa(a[i])
|
|
|
|
}
|
|
|
|
return strings.Join(other, sep)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CompactionLevel represents a grouping of index files based on bloom filter
|
|
|
|
// settings. By having the same bloom filter settings, the filters
|
|
|
|
// can be merged and evaluated at a higher level.
|
|
|
|
type CompactionLevel struct {
|
|
|
|
// Bloom filter bit size & hash count
|
|
|
|
M uint64 `json:"m,omitempty"`
|
|
|
|
K uint64 `json:"k,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// DefaultCompactionLevels is the default settings used by the index.
|
|
|
|
var DefaultCompactionLevels = []CompactionLevel{
|
|
|
|
{M: 0, K: 0}, // L0: Log files, no filter.
|
|
|
|
{M: 1 << 25, K: 6}, // L1: Initial compaction
|
|
|
|
{M: 1 << 25, K: 6}, // L2
|
|
|
|
{M: 1 << 26, K: 6}, // L3
|
|
|
|
{M: 1 << 27, K: 6}, // L4
|
|
|
|
{M: 1 << 28, K: 6}, // L5
|
|
|
|
{M: 1 << 29, K: 6}, // L6
|
|
|
|
{M: 1 << 30, K: 6}, // L7
|
|
|
|
}
|
|
|
|
|
|
|
|
// MaxIndexMergeCount is the maximum number of files that can be merged together at once.
|
|
|
|
const MaxIndexMergeCount = 2
|
|
|
|
|
|
|
|
// MaxIndexFileSize is the maximum expected size of an index file.
|
|
|
|
const MaxIndexFileSize = 4 * (1 << 30)
|
|
|
|
|
|
|
|
// IsPartitionDir returns true if directory contains a MANIFEST file.
|
|
|
|
func IsPartitionDir(path string) (bool, error) {
|
|
|
|
if _, err := os.Stat(filepath.Join(path, ManifestFileName)); os.IsNotExist(err) {
|
|
|
|
return false, nil
|
|
|
|
} else if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}
|