2019-01-08 23:03:34 +00:00
|
|
|
package tsm1
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2019-08-21 15:46:43 +00:00
|
|
|
"context"
|
2019-09-19 12:48:06 +00:00
|
|
|
"fmt"
|
2019-01-08 23:03:34 +00:00
|
|
|
"math"
|
2019-08-30 15:41:47 +00:00
|
|
|
"strings"
|
2019-01-08 23:32:43 +00:00
|
|
|
"sync"
|
2019-09-19 12:48:06 +00:00
|
|
|
"time"
|
2019-01-08 23:03:34 +00:00
|
|
|
|
2019-08-21 15:46:43 +00:00
|
|
|
"github.com/influxdata/influxdb/kit/tracing"
|
2019-01-08 00:37:16 +00:00
|
|
|
"github.com/influxdata/influxdb/models"
|
2019-01-14 10:56:43 +00:00
|
|
|
"github.com/influxdata/influxdb/tsdb"
|
2019-01-08 23:03:34 +00:00
|
|
|
"github.com/influxdata/influxql"
|
|
|
|
)
|
|
|
|
|
2019-04-11 04:53:06 +00:00
|
|
|
// DeletePrefixRange removes all TSM data belonging to a bucket, and removes all index
|
2019-01-11 19:28:46 +00:00
|
|
|
// and series file data associated with the bucket. The provided time range ensures
|
|
|
|
// that only bucket data for that range is removed.
|
2019-08-21 15:46:43 +00:00
|
|
|
func (e *Engine) DeletePrefixRange(rootCtx context.Context, name []byte, min, max int64, pred Predicate) error {
|
|
|
|
span, ctx := tracing.StartSpanFromContext(rootCtx)
|
2019-09-19 12:48:06 +00:00
|
|
|
span.LogKV("name_prefix", fmt.Sprintf("%x", name),
|
|
|
|
"min", time.Unix(0, min), "max", time.Unix(0, max),
|
|
|
|
"has_pred", pred != nil,
|
|
|
|
)
|
2019-08-21 15:46:43 +00:00
|
|
|
defer span.Finish()
|
2019-01-08 23:03:34 +00:00
|
|
|
// TODO(jeff): we need to block writes to this prefix while deletes are in progress
|
|
|
|
// otherwise we can end up in a situation where we have staged data in the cache or
|
|
|
|
// WAL that was deleted from the index, or worse. This needs to happen at a higher
|
|
|
|
// layer.
|
|
|
|
|
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
|
|
|
// TODO(jeff): ensure the engine is not closed while we're running this. At least
|
|
|
|
// now we know that the series file or index won't be closed out from underneath
|
|
|
|
// of us.
|
|
|
|
|
2019-01-08 23:03:34 +00:00
|
|
|
// Ensure that the index does not compact away the measurement or series we're
|
|
|
|
// going to delete before we're done with them.
|
2019-08-21 15:46:43 +00:00
|
|
|
span, _ = tracing.StartSpanFromContextWithOperationName(rootCtx, "disable index compactions")
|
2019-01-08 23:03:34 +00:00
|
|
|
e.index.DisableCompactions()
|
|
|
|
defer e.index.EnableCompactions()
|
|
|
|
e.index.Wait()
|
2019-08-21 15:46:43 +00:00
|
|
|
span.Finish()
|
2019-01-08 23:03:34 +00:00
|
|
|
|
2019-01-09 22:24:26 +00:00
|
|
|
// Disable and abort running compactions so that tombstones added existing tsm
|
|
|
|
// files don't get removed. This would cause deleted measurements/series to
|
|
|
|
// re-appear once the compaction completed. We only disable the level compactions
|
|
|
|
// so that snapshotting does not stop while writing out tombstones. If it is stopped,
|
|
|
|
// and writing tombstones takes a long time, writes can get rejected due to the cache
|
|
|
|
// filling up.
|
2019-08-21 15:46:43 +00:00
|
|
|
span, _ = tracing.StartSpanFromContextWithOperationName(rootCtx, "disable tsm compactions")
|
2019-01-09 22:24:26 +00:00
|
|
|
e.disableLevelCompactions(true)
|
|
|
|
defer e.enableLevelCompactions(true)
|
2019-08-21 15:46:43 +00:00
|
|
|
span.Finish()
|
2019-01-09 22:24:26 +00:00
|
|
|
|
2019-08-21 15:46:43 +00:00
|
|
|
span, _ = tracing.StartSpanFromContextWithOperationName(rootCtx, "disable series file compactions")
|
2019-01-09 22:24:26 +00:00
|
|
|
e.sfile.DisableCompactions()
|
|
|
|
defer e.sfile.EnableCompactions()
|
2019-08-21 15:46:43 +00:00
|
|
|
span.Finish()
|
2019-01-09 22:24:26 +00:00
|
|
|
|
2019-01-08 23:03:34 +00:00
|
|
|
// TODO(jeff): are the query language values still a thing?
|
|
|
|
// Min and max time in the engine are slightly different from the query language values.
|
|
|
|
if min == influxql.MinTime {
|
|
|
|
min = math.MinInt64
|
|
|
|
}
|
|
|
|
if max == influxql.MaxTime {
|
|
|
|
max = math.MaxInt64
|
|
|
|
}
|
|
|
|
|
2019-01-08 23:32:43 +00:00
|
|
|
// Run the delete on each TSM file in parallel and keep track of possibly dead keys.
|
|
|
|
|
|
|
|
// TODO(jeff): keep a set of keys for each file to avoid contention.
|
|
|
|
// TODO(jeff): come up with a better way to figure out what keys we need to delete
|
|
|
|
// from the index.
|
|
|
|
|
|
|
|
var possiblyDead struct {
|
|
|
|
sync.RWMutex
|
|
|
|
keys map[string]struct{}
|
|
|
|
}
|
|
|
|
possiblyDead.keys = make(map[string]struct{})
|
|
|
|
|
2019-01-08 23:03:34 +00:00
|
|
|
if err := e.FileStore.Apply(func(r TSMFile) error {
|
2019-08-21 15:46:43 +00:00
|
|
|
// TODO(edd): tracing this deep down is currently speculative, so I have
|
|
|
|
// not added the tracing into the TSMReader API.
|
|
|
|
span, _ := tracing.StartSpanFromContextWithOperationName(rootCtx, "TSMFile delete prefix")
|
2019-08-30 10:48:58 +00:00
|
|
|
span.LogKV("file_path", r.Path())
|
2019-08-21 15:46:43 +00:00
|
|
|
defer span.Finish()
|
|
|
|
|
2019-04-12 19:36:17 +00:00
|
|
|
return r.DeletePrefix(name, min, max, pred, func(key []byte) {
|
2019-01-08 23:32:43 +00:00
|
|
|
possiblyDead.Lock()
|
|
|
|
possiblyDead.keys[string(key)] = struct{}{}
|
|
|
|
possiblyDead.Unlock()
|
|
|
|
})
|
2019-01-08 23:03:34 +00:00
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-08-30 10:48:58 +00:00
|
|
|
span, _ = tracing.StartSpanFromContextWithOperationName(rootCtx, "Cache find delete keys")
|
|
|
|
span.LogKV("cache_size", e.Cache.Size())
|
|
|
|
var keysChecked int // For tracing information.
|
2019-01-08 23:03:34 +00:00
|
|
|
// ApplySerialEntryFn cannot return an error in this invocation.
|
2019-08-30 15:41:47 +00:00
|
|
|
nameStr := string(name)
|
|
|
|
_ = e.Cache.ApplyEntryFn(func(k string, _ *entry) error {
|
2019-08-30 10:48:58 +00:00
|
|
|
keysChecked++
|
2019-08-30 15:41:47 +00:00
|
|
|
if !strings.HasPrefix(k, nameStr) {
|
2019-04-29 16:28:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
2019-08-30 15:41:47 +00:00
|
|
|
// TODO(edd): either use an unsafe conversion to []byte, or add a MatchesString
|
|
|
|
// method to tsm1.Predicate.
|
|
|
|
if pred != nil && !pred.Matches([]byte(k)) {
|
2019-04-29 16:28:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
2019-01-09 22:24:26 +00:00
|
|
|
|
2019-04-29 16:28:34 +00:00
|
|
|
// we have to double check every key in the cache because maybe
|
|
|
|
// it exists in the index but not yet on disk.
|
2019-08-30 15:41:47 +00:00
|
|
|
possiblyDead.keys[k] = struct{}{}
|
2019-04-29 16:28:34 +00:00
|
|
|
|
2019-01-08 23:03:34 +00:00
|
|
|
return nil
|
|
|
|
})
|
2019-08-30 10:48:58 +00:00
|
|
|
span.LogKV("cache_cardinality", keysChecked)
|
|
|
|
span.Finish()
|
2019-01-08 23:03:34 +00:00
|
|
|
|
2019-08-30 10:48:58 +00:00
|
|
|
// Delete from the cache (traced in cache).
|
2019-08-30 15:41:47 +00:00
|
|
|
e.Cache.DeleteBucketRange(ctx, nameStr, min, max, pred)
|
2019-01-08 23:03:34 +00:00
|
|
|
|
2019-01-08 23:32:43 +00:00
|
|
|
// Now that all of the data is purged, we need to find if some keys are fully deleted
|
|
|
|
// and if so, remove them from the index.
|
|
|
|
if err := e.FileStore.Apply(func(r TSMFile) error {
|
2019-08-21 15:46:43 +00:00
|
|
|
// TODO(edd): tracing this deep down is currently speculative, so I have
|
|
|
|
// not added the tracing into the Engine API.
|
|
|
|
span, _ := tracing.StartSpanFromContextWithOperationName(rootCtx, "TSMFile determine fully deleted")
|
2019-08-30 10:48:58 +00:00
|
|
|
span.LogKV("file_path", r.Path())
|
2019-08-21 15:46:43 +00:00
|
|
|
defer span.Finish()
|
|
|
|
|
2019-01-08 23:32:43 +00:00
|
|
|
possiblyDead.RLock()
|
|
|
|
defer possiblyDead.RUnlock()
|
|
|
|
|
2019-08-30 10:48:58 +00:00
|
|
|
var keysChecked int
|
2019-01-11 19:28:46 +00:00
|
|
|
iter := r.Iterator(name)
|
2019-01-08 23:32:43 +00:00
|
|
|
for i := 0; iter.Next(); i++ {
|
|
|
|
key := iter.Key()
|
2019-01-11 19:28:46 +00:00
|
|
|
if !bytes.HasPrefix(key, name) {
|
2019-01-08 23:32:43 +00:00
|
|
|
break
|
|
|
|
}
|
2019-04-29 16:28:34 +00:00
|
|
|
if pred != nil && !pred.Matches(key) {
|
|
|
|
continue
|
|
|
|
}
|
2019-01-08 23:32:43 +00:00
|
|
|
|
|
|
|
// TODO(jeff): benchmark the locking here.
|
|
|
|
if i%1024 == 0 { // allow writes to proceed.
|
|
|
|
possiblyDead.RUnlock()
|
|
|
|
possiblyDead.RLock()
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := possiblyDead.keys[string(key)]; ok {
|
|
|
|
possiblyDead.RUnlock()
|
|
|
|
possiblyDead.Lock()
|
|
|
|
delete(possiblyDead.keys, string(key))
|
|
|
|
possiblyDead.Unlock()
|
|
|
|
possiblyDead.RLock()
|
|
|
|
}
|
|
|
|
}
|
2019-08-30 10:48:58 +00:00
|
|
|
span.LogKV("keys_checked", keysChecked)
|
2019-01-08 23:32:43 +00:00
|
|
|
return iter.Err()
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-08-30 10:48:58 +00:00
|
|
|
span, _ = tracing.StartSpanFromContextWithOperationName(rootCtx, "Cache find delete keys")
|
|
|
|
span.LogKV("cache_size", e.Cache.Size())
|
|
|
|
keysChecked = 0
|
2019-02-04 17:16:38 +00:00
|
|
|
// ApplySerialEntryFn cannot return an error in this invocation.
|
2019-08-30 15:41:47 +00:00
|
|
|
_ = e.Cache.ApplyEntryFn(func(k string, _ *entry) error {
|
2019-08-30 10:48:58 +00:00
|
|
|
keysChecked++
|
2019-08-30 15:41:47 +00:00
|
|
|
if !strings.HasPrefix(k, nameStr) {
|
2019-04-29 16:28:34 +00:00
|
|
|
return nil
|
2019-02-04 17:16:38 +00:00
|
|
|
}
|
2019-08-30 15:41:47 +00:00
|
|
|
// TODO(edd): either use an unsafe conversion to []byte, or add a MatchesString
|
|
|
|
// method to tsm1.Predicate.
|
|
|
|
if pred != nil && !pred.Matches([]byte(k)) {
|
2019-04-29 16:28:34 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-08-30 15:41:47 +00:00
|
|
|
delete(possiblyDead.keys, k)
|
2019-02-04 17:16:38 +00:00
|
|
|
return nil
|
|
|
|
})
|
2019-08-30 10:48:58 +00:00
|
|
|
span.LogKV("cache_cardinality", keysChecked)
|
|
|
|
span.Finish()
|
2019-02-04 17:16:38 +00:00
|
|
|
|
2019-01-08 23:32:43 +00:00
|
|
|
if len(possiblyDead.keys) > 0 {
|
|
|
|
buf := make([]byte, 1024)
|
|
|
|
|
|
|
|
// TODO(jeff): all of these methods have possible errors which opens us to partial
|
|
|
|
// failure scenarios. we need to either ensure that partial errors here are ok or
|
|
|
|
// do something to fix it.
|
|
|
|
// TODO(jeff): it's also important that all of the deletes happen atomically with
|
|
|
|
// the deletes of the data in the tsm files.
|
|
|
|
|
2019-01-14 10:56:43 +00:00
|
|
|
// In this case the entire measurement (bucket) can be removed from the index.
|
2019-04-29 16:28:34 +00:00
|
|
|
if min == math.MinInt64 && max == math.MaxInt64 && pred == nil {
|
2019-01-18 17:28:58 +00:00
|
|
|
// The TSI index and Series File do not store series data in escaped form.
|
|
|
|
name = models.UnescapeMeasurement(name)
|
|
|
|
|
2019-01-14 10:56:43 +00:00
|
|
|
// Build up a set of series IDs that we need to remove from the series file.
|
|
|
|
set := tsdb.NewSeriesIDSet()
|
|
|
|
itr, err := e.index.MeasurementSeriesIDIterator(name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var elem tsdb.SeriesIDElem
|
|
|
|
for elem, err = itr.Next(); err != nil; elem, err = itr.Next() {
|
|
|
|
if elem.SeriesID.IsZero() {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
set.AddNoLock(elem.SeriesID)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if err := itr.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove the measurement from the index before the series file.
|
2019-08-21 15:46:43 +00:00
|
|
|
span, _ = tracing.StartSpanFromContextWithOperationName(rootCtx, "TSI drop measurement")
|
2019-09-19 12:48:06 +00:00
|
|
|
span.LogKV("measurement_name", fmt.Sprintf("%x", name))
|
2019-01-14 10:56:43 +00:00
|
|
|
if err := e.index.DropMeasurement(name); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-08-21 15:46:43 +00:00
|
|
|
span.Finish()
|
2019-01-14 10:56:43 +00:00
|
|
|
|
|
|
|
// Iterate over the series ids we previously extracted from the index
|
|
|
|
// and remove from the series file.
|
2019-08-30 10:48:58 +00:00
|
|
|
span, _ = tracing.StartSpanFromContextWithOperationName(rootCtx, "SFile Delete Series IDs")
|
2019-09-19 12:48:06 +00:00
|
|
|
span.LogKV("measurement_name", fmt.Sprintf("%x", name), "series_id_set_size", set.Cardinality())
|
2019-01-14 10:56:43 +00:00
|
|
|
set.ForEachNoLock(func(id tsdb.SeriesID) {
|
|
|
|
if err = e.sfile.DeleteSeriesID(id); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
})
|
2019-08-21 15:46:43 +00:00
|
|
|
span.Finish()
|
2019-01-14 10:56:43 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is the slow path, when not dropping the entire bucket (measurement)
|
2019-08-21 15:46:43 +00:00
|
|
|
span, _ = tracing.StartSpanFromContextWithOperationName(rootCtx, "TSI/SFile Delete keys")
|
2019-09-19 12:48:06 +00:00
|
|
|
span.LogKV("measurement_name", fmt.Sprintf("%x", name), "keys_to_delete", len(possiblyDead.keys))
|
2019-01-08 23:32:43 +00:00
|
|
|
for key := range possiblyDead.keys {
|
|
|
|
// TODO(jeff): ugh reduce copies here
|
|
|
|
keyb := []byte(key)
|
2019-01-09 17:56:10 +00:00
|
|
|
keyb, _ = SeriesAndFieldFromCompositeKey(keyb)
|
2019-01-08 23:32:43 +00:00
|
|
|
|
|
|
|
name, tags := models.ParseKeyBytes(keyb)
|
|
|
|
sid := e.sfile.SeriesID(name, tags, buf)
|
|
|
|
if sid.IsZero() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-01-14 10:56:43 +00:00
|
|
|
if err := e.index.DropSeries(sid, keyb, true); err != nil {
|
2019-01-08 23:32:43 +00:00
|
|
|
return err
|
|
|
|
}
|
2019-01-11 19:28:46 +00:00
|
|
|
|
2019-01-14 10:56:43 +00:00
|
|
|
if err := e.sfile.DeleteSeriesID(sid); err != nil {
|
2019-01-11 19:28:46 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2019-08-21 15:46:43 +00:00
|
|
|
span.Finish()
|
2019-01-08 23:32:43 +00:00
|
|
|
}
|
2019-01-08 23:03:34 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|