From f717b3501b0d91cf280e1f930c56eda5c78de0d4 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Sun, 1 May 2022 13:17:59 +0000 Subject: [PATCH] storage: clear range keys in `Writer.Clear*Range` methods This patch automatically clears any range keys in the `Writer` methods `ClearRawRange`, `ClearMVCCRange`, and `ClearMVCCIteratorRange`. Range keys are not cleared in `ClearMVCCVersions`, since this method is specifically for clearing MVCC point key versions, and it is not possible to clear range keys between versions of the same point key. Release note: None --- pkg/storage/engine.go | 77 +++-- pkg/storage/mvcc_history_test.go | 34 +- pkg/storage/pebble.go | 15 +- pkg/storage/pebble_batch.go | 13 +- pkg/storage/sst_writer.go | 6 +- .../testdata/mvcc_histories/range_key_clear | 292 ++++++++++++++++++ 6 files changed, 372 insertions(+), 65 deletions(-) create mode 100644 pkg/storage/testdata/mvcc_histories/range_key_clear diff --git a/pkg/storage/engine.go b/pkg/storage/engine.go index f5fd079e7dd9..a9867977bfdb 100644 --- a/pkg/storage/engine.go +++ b/pkg/storage/engine.go @@ -566,27 +566,26 @@ type Writer interface { // returns. ApplyBatchRepr(repr []byte, sync bool) error - // ClearMVCC removes the item from the db with the given MVCCKey. It - // requires that the timestamp is non-empty (see - // {ClearUnversioned,ClearIntent} if the timestamp is empty). Note that - // clear actually removes entries from the storage engine, rather than - // inserting MVCC tombstones. + // ClearMVCC removes the point key from the db with the given MVCCKey. It does + // not affect range keys. It requires that the timestamp is non-empty (see + // ClearUnversioned or ClearIntent if the timestamp is empty). Note that clear + // actually removes entries from the storage engine, rather than inserting + // MVCC tombstones. // // It is safe to modify the contents of the arguments after it returns. ClearMVCC(key MVCCKey) error // ClearUnversioned removes an unversioned item from the db. It is for use // with inline metadata (not intents) and other unversioned keys (like - // Range-ID local keys). + // Range-ID local keys). It does not affect range keys. // // It is safe to modify the contents of the arguments after it returns. ClearUnversioned(key roachpb.Key) error - // ClearIntent removes an intent from the db. Unlike - // {ClearMVCC,ClearUnversioned} this is a higher-level method that may make - // changes in parts of the key space that are not only a function of the - // input, and may choose to use a single-clear under the covers. - // txnDidNotUpdateMeta allows for performance optimization when set to true, - // and has semantics defined in MVCCMetadata.TxnDidNotUpdateMeta (it can - // be conservatively set to false). + // ClearIntent removes an intent from the db. Unlike ClearMVCC and + // ClearUnversioned, this is a higher-level method that may make changes in + // parts of the key space that are not only a function of the input, and may + // choose to use a single-clear under the covers. txnDidNotUpdateMeta allows + // for performance optimization when set to true, and has semantics defined in + // MVCCMetadata.TxnDidNotUpdateMeta (it can be conservatively set to false). // // It is safe to modify the contents of the arguments after it returns. // @@ -596,29 +595,30 @@ type Writer interface { // decrease, we can stop tracking txnDidNotUpdateMeta and still optimize // ClearIntent by always doing single-clear. ClearIntent(key roachpb.Key, txnDidNotUpdateMeta bool, txnUUID uuid.UUID) error - // ClearEngineKey removes the item from the db with the given EngineKey. - // Note that clear actually removes entries from the storage engine. This is - // a general-purpose and low-level method that should be used sparingly, - // only when the other Clear* methods are not applicable. + // ClearEngineKey removes the item from the db with the given EngineKey. It + // does not affect range keys. Note that clear actually removes entries from + // the storage engine. This is a general-purpose and low-level method that + // should be used sparingly, only when the other Clear* methods are not + // applicable. It does not // // It is safe to modify the contents of the arguments after it returns. ClearEngineKey(key EngineKey) error - // ClearRawRange removes a set of entries, from start (inclusive) to end - // (exclusive). It can be applied to a range consisting of MVCCKeys or the - // more general EngineKeys -- it simply uses the roachpb.Key parameters as - // the Key field of an EngineKey. Similar to the other Clear* methods, - // this method actually removes entries from the storage engine. + // ClearRawRange removes entries from start (inclusive) to end (exclusive), + // including range keys but excluding any separated intents unless the + // separated lock table is explicitly included in the span. It can be applied + // to a range consisting of MVCCKeys or the more general EngineKeys -- it + // simply uses the roachpb.Key parameters as the Key field of an EngineKey. + // Similar to the other Clear* methods, this method actually removes entries + // from the storage engine. // // It is safe to modify the contents of the arguments after it returns. ClearRawRange(start, end roachpb.Key) error // ClearMVCCRange removes MVCC keys from start (inclusive) to end (exclusive), - // including intents. Similar to the other Clear* methods, this method - // actually removes entries from the storage engine. + // including intents and range keys. Similar to the other Clear* methods, this + // method actually removes entries from the storage engine. // // It is safe to modify the contents of the arguments after it returns. - // - // TODO(erikgrinaker): This should clear range keys too. ClearMVCCRange(start, end roachpb.Key) error // ClearMVCCVersions removes MVCC point key versions from start (inclusive) to // end (exclusive). It does not affect intents nor range keys. It is meant for @@ -629,11 +629,9 @@ type Writer interface { // It is safe to modify the contents of the arguments after it returns. ClearMVCCVersions(start, end MVCCKey) error // ClearMVCCIteratorRange removes all keys in the given span using an MVCC - // iterator and clearing individual point keys (including intents). Similar to - // the other Clear* methods, this method actually removes entries from the - // storage engine. - // - // TODO(erikgrinaker): This should clear range keys too. + // iterator and clearing individual point keys including intents. Any range + // keys in the span are also cleared. Similar to the other Clear* methods, + // this method actually removes entries from the storage engine. ClearMVCCIteratorRange(start, end roachpb.Key) error // ExperimentalClearMVCCRangeKey deletes an MVCC range key from start @@ -1077,9 +1075,10 @@ func WriteSyncNoop(ctx context.Context, eng Engine) error { } // ClearRangeWithHeuristic clears the keys from start (inclusive) to end -// (exclusive). Depending on the number of keys, it will either use ClearRawRange -// or clear individual keys. It works with EngineKeys, so don't expect it to -// find and clear separated intents if [start, end) refers to MVCC key space. +// (exclusive), including any range keys. Depending on the number of keys, it +// will either use ClearRawRange or clear individual keys. It works with +// EngineKeys, so don't expect it to find and clear separated intents if [start, +// end) refers to MVCC key space. func ClearRangeWithHeuristic(reader Reader, writer Writer, start, end roachpb.Key) error { iter := reader.NewEngineIterator(IterOptions{UpperBound: end}) defer iter.Close() @@ -1106,16 +1105,13 @@ func ClearRangeWithHeuristic(reader Reader, writer Writer, start, end roachpb.Ke for valid { count++ if count > clearRangeMinKeys { - break + return writer.ClearRawRange(start, end) } valid, err = iter.NextEngineKey() } if err != nil { return err } - if count > clearRangeMinKeys { - return writer.ClearRawRange(start, end) - } valid, err = iter.SeekEngineKeyGE(EngineKey{Key: start}) for valid { var k EngineKey @@ -1127,7 +1123,10 @@ func ClearRangeWithHeuristic(reader Reader, writer Writer, start, end roachpb.Ke } valid, err = iter.NextEngineKey() } - return err + if err != nil { + return err + } + return writer.ExperimentalClearAllMVCCRangeKeys(start, end) } var ingestDelayL0Threshold = settings.RegisterIntSetting( diff --git a/pkg/storage/mvcc_history_test.go b/pkg/storage/mvcc_history_test.go index 10fa128670a1..9dc29fd92cfa 100644 --- a/pkg/storage/mvcc_history_test.go +++ b/pkg/storage/mvcc_history_test.go @@ -83,6 +83,7 @@ import ( // merge [ts=[,]] k= v= [raw] // // clear_range k= end= +// clear_rangekey k= end= ts=[,] // // Where `` can be a simple string, or a string // prefixed by the following characters: @@ -472,17 +473,18 @@ var commands = map[string]cmd{ "resolve_intent_range": {typDataUpdate, cmdResolveIntentRange}, "check_intent": {typReadOnly, cmdCheckIntent}, - "clear_range": {typDataUpdate, cmdClearRange}, - "cput": {typDataUpdate, cmdCPut}, - "del": {typDataUpdate, cmdDelete}, - "del_range": {typDataUpdate, cmdDeleteRange}, - "del_range_ts": {typDataUpdate, cmdDeleteRangeTombstone}, - "get": {typReadOnly, cmdGet}, - "increment": {typDataUpdate, cmdIncrement}, - "merge": {typDataUpdate, cmdMerge}, - "put": {typDataUpdate, cmdPut}, - "put_rangekey": {typDataUpdate, cmdPutRangeKey}, - "scan": {typReadOnly, cmdScan}, + "clear_range": {typDataUpdate, cmdClearRange}, + "clear_rangekey": {typDataUpdate, cmdClearRangeKey}, + "cput": {typDataUpdate, cmdCPut}, + "del": {typDataUpdate, cmdDelete}, + "del_range": {typDataUpdate, cmdDeleteRange}, + "del_range_ts": {typDataUpdate, cmdDeleteRangeTombstone}, + "get": {typReadOnly, cmdGet}, + "increment": {typDataUpdate, cmdIncrement}, + "merge": {typDataUpdate, cmdMerge}, + "put": {typDataUpdate, cmdPut}, + "put_rangekey": {typDataUpdate, cmdPutRangeKey}, + "scan": {typReadOnly, cmdScan}, "iter_new": {typReadOnly, cmdIterNew}, "iter_seek_ge": {typReadOnly, cmdIterSeekGE}, @@ -689,9 +691,19 @@ func cmdCheckIntent(e *evalCtx) error { func cmdClearRange(e *evalCtx) error { key, endKey := e.getKeyRange() + if util.ConstantWithMetamorphicTestBool("clear-range-using-iterator", false) { + return e.engine.ClearMVCCIteratorRange(key, endKey) + } return e.engine.ClearMVCCRange(key, endKey) } +func cmdClearRangeKey(e *evalCtx) error { + key, endKey := e.getKeyRange() + ts := e.getTs(nil) + return e.engine.ExperimentalClearMVCCRangeKey( + MVCCRangeKey{StartKey: key, EndKey: endKey, Timestamp: ts}) +} + func cmdCPut(e *evalCtx) error { txn := e.getTxn(optional) ts := e.getTs(txn) diff --git a/pkg/storage/pebble.go b/pkg/storage/pebble.go index 193cc5843a48..863320502803 100644 --- a/pkg/storage/pebble.go +++ b/pkg/storage/pebble.go @@ -1211,7 +1211,12 @@ func (p *Pebble) SingleClearEngineKey(key EngineKey) error { // ClearRawRange implements the Engine interface. func (p *Pebble) ClearRawRange(start, end roachpb.Key) error { - return p.clearRange(MVCCKey{Key: start}, MVCCKey{Key: end}) + startBuf := EncodeMVCCKey(MVCCKey{Key: start}) + endBuf := EncodeMVCCKey(MVCCKey{Key: end}) + if err := p.db.DeleteRange(startBuf, endBuf, pebble.Sync); err != nil { + return err + } + return p.ExperimentalClearAllMVCCRangeKeys(start, end) } // ClearMVCCRange implements the Engine interface. @@ -1222,13 +1227,7 @@ func (p *Pebble) ClearMVCCRange(start, end roachpb.Key) error { // ClearMVCCVersions implements the Engine interface. func (p *Pebble) ClearMVCCVersions(start, end MVCCKey) error { - return p.clearRange(start, end) -} - -func (p *Pebble) clearRange(start, end MVCCKey) error { - bufStart := EncodeMVCCKey(start) - bufEnd := EncodeMVCCKey(end) - return p.db.DeleteRange(bufStart, bufEnd, pebble.Sync) + return p.db.DeleteRange(EncodeMVCCKey(start), EncodeMVCCKey(end), pebble.Sync) } // ClearMVCCIteratorRange implements the Engine interface. diff --git a/pkg/storage/pebble_batch.go b/pkg/storage/pebble_batch.go index 655b9c36b4f3..c70e58e90f4d 100644 --- a/pkg/storage/pebble_batch.go +++ b/pkg/storage/pebble_batch.go @@ -363,7 +363,12 @@ func (p *pebbleBatch) SingleClearEngineKey(key EngineKey) error { // ClearRawRange implements the Batch interface. func (p *pebbleBatch) ClearRawRange(start, end roachpb.Key) error { - return p.clearRange(MVCCKey{Key: start}, MVCCKey{Key: end}) + p.buf = EncodeMVCCKeyToBuf(p.buf[:0], MVCCKey{Key: start}) + endBuf := EncodeMVCCKey(MVCCKey{Key: end}) + if err := p.batch.DeleteRange(p.buf, endBuf, nil); err != nil { + return err + } + return p.ExperimentalClearAllMVCCRangeKeys(start, end) } // ClearMVCCRange implements the Batch interface. @@ -375,10 +380,6 @@ func (p *pebbleBatch) ClearMVCCRange(start, end roachpb.Key) error { // ClearMVCCVersions implements the Batch interface. func (p *pebbleBatch) ClearMVCCVersions(start, end MVCCKey) error { - return p.clearRange(start, end) -} - -func (p *pebbleBatch) clearRange(start, end MVCCKey) error { p.buf = EncodeMVCCKeyToBuf(p.buf[:0], start) buf2 := EncodeMVCCKey(end) return p.batch.DeleteRange(p.buf, buf2, nil) @@ -405,7 +406,7 @@ func (p *pebbleBatch) ClearMVCCIteratorRange(start, end roachpb.Key) error { return err } } - return nil + return p.ExperimentalClearAllMVCCRangeKeys(start, end) } // ExperimentalClearMVCCRangeKey implements the Engine interface. diff --git a/pkg/storage/sst_writer.go b/pkg/storage/sst_writer.go index be9dd72b0f1e..6fca56971ee6 100644 --- a/pkg/storage/sst_writer.go +++ b/pkg/storage/sst_writer.go @@ -128,6 +128,8 @@ func (fw *SSTWriter) Finish() error { } // ClearRawRange implements the Writer interface. +// +// TODO(erikgrinaker): This must clear range keys when SSTs support them. func (fw *SSTWriter) ClearRawRange(start, end roachpb.Key) error { return fw.clearRange(MVCCKey{Key: start}, MVCCKey{Key: end}) } @@ -153,8 +155,10 @@ func (fw *SSTWriter) ExperimentalClearMVCCRangeKey(rangeKey MVCCRangeKey) error } // ExperimentalClearAllMVCCRangeKeys implements the Writer interface. +// +// TODO(erikgrinaker): This must clear range keys when SSTs support them. func (fw *SSTWriter) ExperimentalClearAllMVCCRangeKeys(start, end roachpb.Key) error { - panic("not implemented") + return nil } func (fw *SSTWriter) clearRange(start, end MVCCKey) error { diff --git a/pkg/storage/testdata/mvcc_histories/range_key_clear b/pkg/storage/testdata/mvcc_histories/range_key_clear new file mode 100644 index 000000000000..0e81c4817236 --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/range_key_clear @@ -0,0 +1,292 @@ +# Tests MVCC range key clearing. +# +# Sets up following dataset, where x is tombstone, o-o is range tombstone, [] is intent. +# +# T +# 7 [a7] [d7] [j7] [l7] +# 6 f6 +# 5 o---------------o k5 +# 4 x x d4 f4 g4 +# 3 o-------o e3 o-------oh3 +# 2 a2 f2 g2 +# 1 o---------------------------------------o +# a b c d e f g h i j k +# +run ok +put_rangekey k=a end=k ts=1 +put_rangekey k=l end=m ts=1 +put_rangekey k=b end=d ts=3 +put k=a ts=2 v=a2 +del k=a ts=4 +del k=b ts=4 +put k=d ts=4 v=d4 +put k=e ts=3 v=e3 +put k=f ts=2 v=f2 +put k=g ts=2 v=g2 +put_rangekey k=f end=h ts=3 +put k=f ts=4 v=f4 +put k=f ts=6 v=f6 +put k=g ts=4 v=g4 +put_rangekey k=c end=g ts=5 +put k=h ts=3 v=h3 +del k=h ts=4 +put k=k ts=5 v=k5 +with t=A + txn_begin ts=7 + put k=a v=a7 + put k=d v=d7 + put k=j v=j7 + put k=l v=l7 +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=7.000000000,0 wto=false gul=0,0 +rangekey: {a-b}/[1.000000000,0] +rangekey: {b-c}/[3.000000000,0 1.000000000,0] +rangekey: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +rangekey: {d-f}/[5.000000000,0 1.000000000,0] +rangekey: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +rangekey: {g-h}/[3.000000000,0 1.000000000,0] +rangekey: {h-k}/[1.000000000,0] +rangekey: {l-m}/[1.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/7.000000000,0 -> /BYTES/a7 +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/4.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "e"/3.000000000,0 -> /BYTES/e3 +data: "f"/6.000000000,0 -> /BYTES/f6 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "f"/2.000000000,0 -> /BYTES/f2 +data: "g"/4.000000000,0 -> /BYTES/g4 +data: "g"/2.000000000,0 -> /BYTES/g2 +data: "h"/4.000000000,0 -> / +data: "h"/3.000000000,0 -> /BYTES/h3 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "j"/7.000000000,0 -> /BYTES/j7 +data: "k"/5.000000000,0 -> /BYTES/k5 +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "l"/7.000000000,0 -> /BYTES/l7 + +# Clear a few range key segments. +run ok +clear_rangekey k=f end=g ts=3 +---- +>> at end: +rangekey: {a-b}/[1.000000000,0] +rangekey: {b-c}/[3.000000000,0 1.000000000,0] +rangekey: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +rangekey: {d-g}/[5.000000000,0 1.000000000,0] +rangekey: {g-h}/[3.000000000,0 1.000000000,0] +rangekey: {h-k}/[1.000000000,0] +rangekey: {l-m}/[1.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/7.000000000,0 -> /BYTES/a7 +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/4.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "e"/3.000000000,0 -> /BYTES/e3 +data: "f"/6.000000000,0 -> /BYTES/f6 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "f"/2.000000000,0 -> /BYTES/f2 +data: "g"/4.000000000,0 -> /BYTES/g4 +data: "g"/2.000000000,0 -> /BYTES/g2 +data: "h"/4.000000000,0 -> / +data: "h"/3.000000000,0 -> /BYTES/h3 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "j"/7.000000000,0 -> /BYTES/j7 +data: "k"/5.000000000,0 -> /BYTES/k5 +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "l"/7.000000000,0 -> /BYTES/l7 + +run ok +clear_rangekey k=e end=f ts=1 +---- +>> at end: +rangekey: {a-b}/[1.000000000,0] +rangekey: {b-c}/[3.000000000,0 1.000000000,0] +rangekey: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +rangekey: {d-e}/[5.000000000,0 1.000000000,0] +rangekey: {e-f}/[5.000000000,0] +rangekey: {f-g}/[5.000000000,0 1.000000000,0] +rangekey: {g-h}/[3.000000000,0 1.000000000,0] +rangekey: {h-k}/[1.000000000,0] +rangekey: {l-m}/[1.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/7.000000000,0 -> /BYTES/a7 +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/4.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "e"/3.000000000,0 -> /BYTES/e3 +data: "f"/6.000000000,0 -> /BYTES/f6 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "f"/2.000000000,0 -> /BYTES/f2 +data: "g"/4.000000000,0 -> /BYTES/g4 +data: "g"/2.000000000,0 -> /BYTES/g2 +data: "h"/4.000000000,0 -> / +data: "h"/3.000000000,0 -> /BYTES/h3 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "j"/7.000000000,0 -> /BYTES/j7 +data: "k"/5.000000000,0 -> /BYTES/k5 +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "l"/7.000000000,0 -> /BYTES/l7 + +# Clearing segments is idempotent and works on missing segments. +run ok +clear_rangekey k=f end=g ts=3 +---- +>> at end: +rangekey: {a-b}/[1.000000000,0] +rangekey: {b-c}/[3.000000000,0 1.000000000,0] +rangekey: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +rangekey: {d-e}/[5.000000000,0 1.000000000,0] +rangekey: {e-f}/[5.000000000,0] +rangekey: {f-g}/[5.000000000,0 1.000000000,0] +rangekey: {g-h}/[3.000000000,0 1.000000000,0] +rangekey: {h-k}/[1.000000000,0] +rangekey: {l-m}/[1.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/7.000000000,0 -> /BYTES/a7 +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/4.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "e"/3.000000000,0 -> /BYTES/e3 +data: "f"/6.000000000,0 -> /BYTES/f6 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "f"/2.000000000,0 -> /BYTES/f2 +data: "g"/4.000000000,0 -> /BYTES/g4 +data: "g"/2.000000000,0 -> /BYTES/g2 +data: "h"/4.000000000,0 -> / +data: "h"/3.000000000,0 -> /BYTES/h3 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "j"/7.000000000,0 -> /BYTES/j7 +data: "k"/5.000000000,0 -> /BYTES/k5 +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "l"/7.000000000,0 -> /BYTES/l7 + +run ok +clear_rangekey k=a end=z ts=10 +---- +>> at end: +rangekey: {a-b}/[1.000000000,0] +rangekey: {b-c}/[3.000000000,0 1.000000000,0] +rangekey: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +rangekey: {d-e}/[5.000000000,0 1.000000000,0] +rangekey: {e-f}/[5.000000000,0] +rangekey: {f-g}/[5.000000000,0 1.000000000,0] +rangekey: {g-h}/[3.000000000,0 1.000000000,0] +rangekey: {h-k}/[1.000000000,0] +rangekey: {l-m}/[1.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/7.000000000,0 -> /BYTES/a7 +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/4.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "e"/3.000000000,0 -> /BYTES/e3 +data: "f"/6.000000000,0 -> /BYTES/f6 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "f"/2.000000000,0 -> /BYTES/f2 +data: "g"/4.000000000,0 -> /BYTES/g4 +data: "g"/2.000000000,0 -> /BYTES/g2 +data: "h"/4.000000000,0 -> / +data: "h"/3.000000000,0 -> /BYTES/h3 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "j"/7.000000000,0 -> /BYTES/j7 +data: "k"/5.000000000,0 -> /BYTES/k5 +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "l"/7.000000000,0 -> /BYTES/l7 + +# Now clear a few spans. +run ok +clear_range k=c end=d +---- +>> at end: +rangekey: {a-b}/[1.000000000,0] +rangekey: {b-c}/[3.000000000,0 1.000000000,0] +rangekey: {d-e}/[5.000000000,0 1.000000000,0] +rangekey: {e-f}/[5.000000000,0] +rangekey: {f-g}/[5.000000000,0 1.000000000,0] +rangekey: {g-h}/[3.000000000,0 1.000000000,0] +rangekey: {h-k}/[1.000000000,0] +rangekey: {l-m}/[1.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/7.000000000,0 -> /BYTES/a7 +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/4.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "e"/3.000000000,0 -> /BYTES/e3 +data: "f"/6.000000000,0 -> /BYTES/f6 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "f"/2.000000000,0 -> /BYTES/f2 +data: "g"/4.000000000,0 -> /BYTES/g4 +data: "g"/2.000000000,0 -> /BYTES/g2 +data: "h"/4.000000000,0 -> / +data: "h"/3.000000000,0 -> /BYTES/h3 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "j"/7.000000000,0 -> /BYTES/j7 +data: "k"/5.000000000,0 -> /BYTES/k5 +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "l"/7.000000000,0 -> /BYTES/l7 + +run ok +clear_range k=f end=g +---- +>> at end: +rangekey: {a-b}/[1.000000000,0] +rangekey: {b-c}/[3.000000000,0 1.000000000,0] +rangekey: {d-e}/[5.000000000,0 1.000000000,0] +rangekey: {e-f}/[5.000000000,0] +rangekey: {g-h}/[3.000000000,0 1.000000000,0] +rangekey: {h-k}/[1.000000000,0] +rangekey: {l-m}/[1.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/7.000000000,0 -> /BYTES/a7 +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/4.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "e"/3.000000000,0 -> /BYTES/e3 +data: "g"/4.000000000,0 -> /BYTES/g4 +data: "g"/2.000000000,0 -> /BYTES/g2 +data: "h"/4.000000000,0 -> / +data: "h"/3.000000000,0 -> /BYTES/h3 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "j"/7.000000000,0 -> /BYTES/j7 +data: "k"/5.000000000,0 -> /BYTES/k5 +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "l"/7.000000000,0 -> /BYTES/l7 + +run ok +clear_range k=a end=z +---- +>> at end: + + +# Error conditions. We don't check clear_range, because it uses a metamorphic +# bool to switch between ClearMVCCRange and ClearMVCCIteratorRange, and these +# fail in different ways. +run error +clear_rangekey k=c end=a ts=1 +---- +>> at end: + +error: (*withstack.withStack:) invalid range key {c-a}/1.000000000,0: start key "c" is at or after end key "a"