Fix performance of dropping large number of keys
parent
7a9019cf41
commit
9d82e24ca0
|
@ -272,7 +272,7 @@ func TestKeyIterator_TSM_MultipleKeysDeleted(t *testing.T) {
|
|||
}
|
||||
|
||||
r1 := MustTSMReader(dir, 1, points1)
|
||||
r1.Delete("cpu,host=A#!~#value")
|
||||
r1.Delete([]string{"cpu,host=A#!~#value"})
|
||||
|
||||
v2 := tsm1.NewValue(time.Unix(1, 0), float64(1))
|
||||
v3 := tsm1.NewValue(time.Unix(1, 0), float64(1))
|
||||
|
@ -283,7 +283,7 @@ func TestKeyIterator_TSM_MultipleKeysDeleted(t *testing.T) {
|
|||
}
|
||||
|
||||
r2 := MustTSMReader(dir, 1, points2)
|
||||
r2.Delete("cpu,host=A#!~#count")
|
||||
r2.Delete([]string{"cpu,host=A#!~#count"})
|
||||
|
||||
iter, err := tsm1.NewTSMKeyIterator(r1, r2)
|
||||
if err != nil {
|
||||
|
|
|
@ -128,8 +128,8 @@ type TSMIndex interface {
|
|||
// Add records a new block entry for a key in the index.
|
||||
Add(key string, blockType byte, minTime, maxTime time.Time, offset int64, size uint32)
|
||||
|
||||
// Delete removes the given key from the index.
|
||||
Delete(key string)
|
||||
// Delete removes the given keys from the index.
|
||||
Delete(keys []string)
|
||||
|
||||
// Contains return true if the given key exists in the index.
|
||||
Contains(key string) bool
|
||||
|
@ -292,11 +292,13 @@ func (d *directIndex) ContainsValue(key string, t time.Time) bool {
|
|||
return d.Entry(key, t) != nil
|
||||
}
|
||||
|
||||
func (d *directIndex) Delete(key string) {
|
||||
func (d *directIndex) Delete(keys []string) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
delete(d.blocks, key)
|
||||
for _, k := range keys {
|
||||
delete(d.blocks, k)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *directIndex) Keys() []string {
|
||||
|
@ -627,14 +629,20 @@ func (d *indirectIndex) KeyCount() int {
|
|||
return len(d.offsets)
|
||||
}
|
||||
|
||||
func (d *indirectIndex) Delete(key string) {
|
||||
func (d *indirectIndex) Delete(keys []string) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
lookup := map[string]struct{}{}
|
||||
for _, k := range keys {
|
||||
lookup[k] = struct{}{}
|
||||
}
|
||||
|
||||
var offsets []int32
|
||||
for _, offset := range d.offsets {
|
||||
_, indexKey, _ := readKey(d.b[offset:])
|
||||
if key == indexKey {
|
||||
|
||||
if _, ok := lookup[indexKey]; ok {
|
||||
continue
|
||||
}
|
||||
offsets = append(offsets, int32(offset))
|
||||
|
@ -934,9 +942,7 @@ func (t *TSMReader) applyTombstones() error {
|
|||
}
|
||||
|
||||
// Update our index
|
||||
for _, tombstone := range tombstones {
|
||||
t.index.Delete(tombstone)
|
||||
}
|
||||
t.index.Delete(tombstones)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1015,12 +1021,12 @@ func (t *TSMReader) ContainsValue(key string, ts time.Time) bool {
|
|||
return t.index.ContainsValue(key, ts)
|
||||
}
|
||||
|
||||
func (t *TSMReader) Delete(key string) error {
|
||||
if err := t.tombstoner.Add(key); err != nil {
|
||||
func (t *TSMReader) Delete(keys []string) error {
|
||||
if err := t.tombstoner.Add(keys); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.index.Delete(key)
|
||||
t.index.Delete(keys)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -794,7 +794,7 @@ func TestTSMReader_MMAP_Tombstone(t *testing.T) {
|
|||
t.Fatalf("unexpected error created reader: %v", err)
|
||||
}
|
||||
|
||||
if err := r.Delete("mem"); err != nil {
|
||||
if err := r.Delete([]string{"mem"}); err != nil {
|
||||
t.Fatalf("unexpected error deleting: %v", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -260,18 +260,22 @@ func (e *DevEngine) DeleteSeries(seriesKeys []string) error {
|
|||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
|
||||
// keyMap is used to see if a given key should be deleted. seriesKey
|
||||
// are the measurement + tagset (minus separate & field)
|
||||
keyMap := map[string]struct{}{}
|
||||
for _, k := range seriesKeys {
|
||||
keyMap[k] = struct{}{}
|
||||
}
|
||||
|
||||
var deleteKeys []string
|
||||
// go through the keys in the file store
|
||||
for _, k := range e.FileStore.Keys() {
|
||||
seriesKey, _ := seriesAndFieldFromCompositeKey(k)
|
||||
if _, ok := keyMap[seriesKey]; ok {
|
||||
e.FileStore.Delete(k)
|
||||
deleteKeys = append(deleteKeys, k)
|
||||
}
|
||||
}
|
||||
e.FileStore.Delete(deleteKeys)
|
||||
|
||||
// find the keys in the cache and remove them
|
||||
walKeys := make([]string, 0)
|
||||
|
|
|
@ -47,8 +47,8 @@ type TSMFile interface {
|
|||
// an error is returned.
|
||||
Type(key string) (byte, error)
|
||||
|
||||
// Delete removes the key from the set of keys available in this file.
|
||||
Delete(key string) error
|
||||
// Delete removes the keys from the set of keys available in this file.
|
||||
Delete(keys []string) error
|
||||
|
||||
// HasTombstones returns true if file contains values that have been deleted.
|
||||
HasTombstones() bool
|
||||
|
@ -186,17 +186,15 @@ func (f *FileStore) Type(key string) (byte, error) {
|
|||
return 0, fmt.Errorf("unknown type for %v", key)
|
||||
}
|
||||
|
||||
func (f *FileStore) Delete(key string) error {
|
||||
func (f *FileStore) Delete(keys []string) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
f.lastModified = time.Now()
|
||||
|
||||
for _, file := range f.files {
|
||||
if file.Contains(key) {
|
||||
if err := file.Delete(key); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := file.Delete(keys); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -537,7 +537,7 @@ func TestFileStore_Open_Deleted(t *testing.T) {
|
|||
t.Fatalf("file count mismatch: got %v, exp %v", got, exp)
|
||||
}
|
||||
|
||||
if err := fs.Delete("cpu,host=server2!~#!value"); err != nil {
|
||||
if err := fs.Delete([]string{"cpu,host=server2!~#!value"}); err != nil {
|
||||
fatal(t, "deleting", err)
|
||||
}
|
||||
|
||||
|
@ -574,7 +574,7 @@ func TestFileStore_Delete(t *testing.T) {
|
|||
t.Fatalf("key length mismatch: got %v, exp %v", got, exp)
|
||||
}
|
||||
|
||||
if err := fs.Delete("cpu,host=server2!~#!value"); err != nil {
|
||||
if err := fs.Delete([]string{"cpu,host=server2!~#!value"}); err != nil {
|
||||
fatal(t, "deleting", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ type Tombstoner struct {
|
|||
Path string
|
||||
}
|
||||
|
||||
func (t *Tombstoner) Add(key string) error {
|
||||
func (t *Tombstoner) Add(keys []string) error {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
|
@ -31,7 +31,9 @@ func (t *Tombstoner) Add(key string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
tombstones = append(tombstones, key)
|
||||
for _, k := range keys {
|
||||
tombstones = append(tombstones, k)
|
||||
}
|
||||
|
||||
return t.writeTombstone(tombstones)
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ func TestTombstoner_Add(t *testing.T) {
|
|||
t.Fatalf("length mismatch: got %v, exp %v", got, exp)
|
||||
}
|
||||
|
||||
ts.Add("foo")
|
||||
ts.Add([]string{"foo"})
|
||||
|
||||
entries, err = ts.ReadAll()
|
||||
if err != nil {
|
||||
|
@ -61,7 +61,7 @@ func TestTombstoner_Delete(t *testing.T) {
|
|||
f := MustTempFile(dir)
|
||||
ts := &tsm1.Tombstoner{Path: f.Name()}
|
||||
|
||||
ts.Add("foo")
|
||||
ts.Add([]string{"foo"})
|
||||
|
||||
// Use a new Tombstoner to verify values are persisted
|
||||
ts = &tsm1.Tombstoner{Path: f.Name()}
|
||||
|
|
|
@ -519,8 +519,8 @@ func (w *DeleteWALEntry) UnmarshalBinary(b []byte) error {
|
|||
func (w *DeleteWALEntry) Encode(dst []byte) ([]byte, error) {
|
||||
var n int
|
||||
for _, k := range w.Keys {
|
||||
if len(dst)+1 > len(dst)-n {
|
||||
grow := make([]byte, defaultBufLen)
|
||||
if len(dst[:n])+1+len(k) > len(dst) {
|
||||
grow := make([]byte, len(dst)*2)
|
||||
dst = append(dst, grow...)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue