From 9cc9023a22c90f01a62a10499fecc968680a26e9 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Fri, 19 Aug 2022 10:02:21 +0000 Subject: [PATCH 01/11] storage: tweak `unsafeMVCCIterator` construction Release justification: non-production code changes Release note: None --- pkg/storage/intent_interleaving_iter.go | 10 ++++-- pkg/storage/intent_interleaving_iter_test.go | 2 +- pkg/storage/pebble.go | 32 ++++---------------- pkg/storage/pebble_batch.go | 12 ++------ 4 files changed, 16 insertions(+), 40 deletions(-) diff --git a/pkg/storage/intent_interleaving_iter.go b/pkg/storage/intent_interleaving_iter.go index 253d1398c073..a4f8a6ff6b82 100644 --- a/pkg/storage/intent_interleaving_iter.go +++ b/pkg/storage/intent_interleaving_iter.go @@ -1245,7 +1245,7 @@ func (i *intentInterleavingIter) SupportsPrev() bool { return true } -// unsageMVCCIterator is used in RaceEnabled test builds to randomly inject +// unsafeMVCCIterator is used in RaceEnabled test builds to randomly inject // changes to unsafe keys retrieved from MVCCIterators. type unsafeMVCCIterator struct { MVCCIterator @@ -1254,8 +1254,12 @@ type unsafeMVCCIterator struct { rawMVCCKeyBuf []byte } -func wrapInUnsafeIter(iter MVCCIterator) MVCCIterator { - return &unsafeMVCCIterator{MVCCIterator: iter} +// gcassert:inline +func maybeWrapInUnsafeIter(iter MVCCIterator) MVCCIterator { + if util.RaceEnabled { + return &unsafeMVCCIterator{MVCCIterator: iter} + } + return iter } var _ MVCCIterator = &unsafeMVCCIterator{} diff --git a/pkg/storage/intent_interleaving_iter_test.go b/pkg/storage/intent_interleaving_iter_test.go index 832fa70f5eca..877a3260b923 100644 --- a/pkg/storage/intent_interleaving_iter_test.go +++ b/pkg/storage/intent_interleaving_iter_test.go @@ -332,7 +332,7 @@ func TestIntentInterleavingIter(t *testing.T) { if d.HasArg("prefix") { d.ScanArgs(t, "prefix", &opts.Prefix) } - iter := wrapInUnsafeIter(newIntentInterleavingIterator(eng, opts)) + iter := maybeWrapInUnsafeIter(newIntentInterleavingIterator(eng, opts)) var b strings.Builder defer iter.Close() // pos is the original : prefix computed by diff --git a/pkg/storage/pebble.go b/pkg/storage/pebble.go index e372c5465510..c64780f61415 100644 --- a/pkg/storage/pebble.go +++ b/pkg/storage/pebble.go @@ -35,7 +35,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/storage/enginepb" "github.com/cockroachdb/cockroach/pkg/storage/fs" - "github.com/cockroachdb/cockroach/pkg/util" "github.com/cockroachdb/cockroach/pkg/util/encoding" "github.com/cockroachdb/cockroach/pkg/util/envutil" "github.com/cockroachdb/cockroach/pkg/util/hlc" @@ -1124,17 +1123,11 @@ func (p *Pebble) NewMVCCIterator(iterKind MVCCIterKind, opts IterOptions) MVCCIt // Doing defer r.Free() does not inline. iter := r.NewMVCCIterator(iterKind, opts) r.Free() - if util.RaceEnabled { - iter = wrapInUnsafeIter(iter) - } - return iter + return maybeWrapInUnsafeIter(iter) } iter := newPebbleIterator(p.db, opts, StandardDurability, p.SupportsRangeKeys()) - if util.RaceEnabled { - return wrapInUnsafeIter(iter) - } - return iter + return maybeWrapInUnsafeIter(iter) } // NewEngineIterator implements the Engine interface. @@ -2049,10 +2042,7 @@ func (p *pebbleReadOnly) NewMVCCIterator(iterKind MVCCIterKind, opts IterOptions // Doing defer r.Free() does not inline. iter := r.NewMVCCIterator(iterKind, opts) r.Free() - if util.RaceEnabled { - iter = wrapInUnsafeIter(iter) - } - return iter + return maybeWrapInUnsafeIter(iter) } iter := &p.normalIter @@ -2077,11 +2067,7 @@ func (p *pebbleReadOnly) NewMVCCIterator(iterKind MVCCIterKind, opts IterOptions } iter.inuse = true - var rv MVCCIterator = iter - if util.RaceEnabled { - rv = wrapInUnsafeIter(rv) - } - return rv + return maybeWrapInUnsafeIter(iter) } // NewEngineIterator implements the Engine interface. @@ -2322,18 +2308,12 @@ func (p *pebbleSnapshot) NewMVCCIterator(iterKind MVCCIterKind, opts IterOptions // Doing defer r.Free() does not inline. iter := r.NewMVCCIterator(iterKind, opts) r.Free() - if util.RaceEnabled { - iter = wrapInUnsafeIter(iter) - } - return iter + return maybeWrapInUnsafeIter(iter) } iter := MVCCIterator(newPebbleIterator( p.snapshot, opts, StandardDurability, p.SupportsRangeKeys())) - if util.RaceEnabled { - iter = wrapInUnsafeIter(iter) - } - return iter + return maybeWrapInUnsafeIter(iter) } // NewEngineIterator implements the Reader interface. diff --git a/pkg/storage/pebble_batch.go b/pkg/storage/pebble_batch.go index d68eb92cb759..534edf34fd12 100644 --- a/pkg/storage/pebble_batch.go +++ b/pkg/storage/pebble_batch.go @@ -16,7 +16,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/settings/cluster" - "github.com/cockroachdb/cockroach/pkg/util" "github.com/cockroachdb/cockroach/pkg/util/protoutil" "github.com/cockroachdb/cockroach/pkg/util/uuid" "github.com/cockroachdb/errors" @@ -207,10 +206,7 @@ func (p *pebbleBatch) NewMVCCIterator(iterKind MVCCIterKind, opts IterOptions) M // Doing defer r.Free() does not inline. iter := r.NewMVCCIterator(iterKind, opts) r.Free() - if util.RaceEnabled { - iter = wrapInUnsafeIter(iter) - } - return iter + return maybeWrapInUnsafeIter(iter) } iter := &p.normalIter @@ -238,11 +234,7 @@ func (p *pebbleBatch) NewMVCCIterator(iterKind MVCCIterKind, opts IterOptions) M } iter.inuse = true - var rv MVCCIterator = iter - if util.RaceEnabled { - rv = wrapInUnsafeIter(rv) - } - return rv + return maybeWrapInUnsafeIter(iter) } // NewEngineIterator implements the Batch interface. From 8aefc3c81921e0750905f7e5ae748dcda43f8522 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Fri, 19 Aug 2022 07:56:19 +0000 Subject: [PATCH 02/11] storage: inline some `intentInterleavingIter` methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch splits up `maybeSkipIntentRangeKeys()` and `maybeSuppressRangeKeyChanged()` to allow for mid-stack inlining. I doubt that the gains are as large as these microbenchmarks claim, and there's a fair bit of variance between runs, but it can't hurt. ``` name old time/op new time/op delta MVCCScan_Pebble/rows=1/versions=1/valueSize=64/numRangeKeys=0-24 5.37µs ± 2% 5.43µs ± 2% +1.13% (p=0.041 n=10+10) MVCCScan_Pebble/rows=100/versions=1/valueSize=64/numRangeKeys=0-24 38.2µs ± 2% 38.2µs ± 2% ~ (p=0.971 n=10+10) MVCCScan_Pebble/rows=10000/versions=1/valueSize=64/numRangeKeys=0-24 2.79ms ± 2% 2.71ms ± 2% -2.59% (p=0.000 n=10+10) MVCCReverseScan_Pebble/rows=1/versions=1/valueSize=64/numRangeKeys=0-24 5.99µs ± 1% 5.99µs ± 2% ~ (p=0.541 n=10+10) MVCCReverseScan_Pebble/rows=100/versions=1/valueSize=64/numRangeKeys=0-24 51.7µs ± 3% 52.1µs ± 1% ~ (p=0.631 n=10+10) MVCCReverseScan_Pebble/rows=10000/versions=1/valueSize=64/numRangeKeys=0-24 3.88ms ± 2% 3.87ms ± 1% ~ (p=0.897 n=10+8) MVCCComputeStats_Pebble/valueSize=32/numRangeKeys=0-24 158ms ± 1% 155ms ± 1% -2.34% (p=0.000 n=10+9) ``` Release justification: bug fixes and low-risk updates to new functionality Release note: None --- pkg/storage/intent_interleaving_iter.go | 83 +++++++++++++++---------- 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/pkg/storage/intent_interleaving_iter.go b/pkg/storage/intent_interleaving_iter.go index a4f8a6ff6b82..46a2c5fb98e8 100644 --- a/pkg/storage/intent_interleaving_iter.go +++ b/pkg/storage/intent_interleaving_iter.go @@ -345,6 +345,8 @@ func (i *intentInterleavingIter) makeLowerLimitKey() roachpb.Key { // NB: This is called before computePos(), and can't rely on intentCmp. // // REQUIRES: i.dir > 0 +// +// gcassert:inline func (i *intentInterleavingIter) maybeSkipIntentRangeKey() error { if util.RaceEnabled && i.dir < 0 { i.err = errors.AssertionFailedf("maybeSkipIntentRangeKey called in reverse") @@ -352,38 +354,45 @@ func (i *intentInterleavingIter) maybeSkipIntentRangeKey() error { return i.err } if i.iterValid && i.intentKey != nil { - if hasPoint, hasRange := i.iter.HasPointAndRange(); hasRange && !hasPoint { - // iter may be on a bare range key that will cover the provisional value, - // in which case we can step onto it. We guard against emitting the wrong - // range key for the intent if the provisional value turns out to be - // missing by: - // - // 1. Before we step, make sure iter isn't ahead of intentIter. We have - // to do a key comparison anyway in case intentIter is ahead of iter. - // 2. After we step, make sure we're on a point key covered by a range key. - // We don't need a key comparison (but do so under race), because if - // the provisional value is missing then we'll either land on a - // different point key below the range key (which will emit the - // correct range key), or we'll land on a different bare range key. - // - // TODO(erikgrinaker): in cases where we don't step iter, we can save - // the result of the comparison in i.intentCmp to avoid another one. - if intentCmp := i.intentKey.Compare(i.iterKey.Key); intentCmp < 0 { - i.err = errors.Errorf("iter ahead of provisional value for intent %s (at %s)", - i.intentKey, i.iterKey) + return i.doMaybeSkipIntentRangeKey() + } + return nil +} + +// doMaybeSkipIntentRangeKey is a helper for maybeSkipIntentRangeKey(), which +// allows mid-stack inlining of the former. +func (i *intentInterleavingIter) doMaybeSkipIntentRangeKey() error { + if hasPoint, hasRange := i.iter.HasPointAndRange(); hasRange && !hasPoint { + // iter may be on a bare range key that will cover the provisional value, + // in which case we can step onto it. We guard against emitting the wrong + // range key for the intent if the provisional value turns out to be + // missing by: + // + // 1. Before we step, make sure iter isn't ahead of intentIter. We have + // to do a key comparison anyway in case intentIter is ahead of iter. + // 2. After we step, make sure we're on a point key covered by a range key. + // We don't need a key comparison (but do so under race), because if + // the provisional value is missing then we'll either land on a + // different point key below the range key (which will emit the + // correct range key), or we'll land on a different bare range key. + // + // TODO(erikgrinaker): in cases where we don't step iter, we can save + // the result of the comparison in i.intentCmp to avoid another one. + if intentCmp := i.intentKey.Compare(i.iterKey.Key); intentCmp < 0 { + i.err = errors.Errorf("iter ahead of provisional value for intent %s (at %s)", + i.intentKey, i.iterKey) + i.valid = false + return i.err + } else if intentCmp == 0 { + i.iter.Next() + if err := i.tryDecodeKey(); err != nil { + return err + } + hasPoint, hasRange = i.iter.HasPointAndRange() + if !hasPoint || !hasRange || (util.RaceEnabled && !i.iterKey.Key.Equal(i.intentKey)) { + i.err = errors.Errorf("iter not on provisional value for intent %s", i.intentKey) i.valid = false return i.err - } else if intentCmp == 0 { - i.iter.Next() - if err := i.tryDecodeKey(); err != nil { - return err - } - hasPoint, hasRange = i.iter.HasPointAndRange() - if !hasPoint || !hasRange || (util.RaceEnabled && !i.iterKey.Key.Equal(i.intentKey)) { - i.err = errors.Errorf("iter not on provisional value for intent %s", i.intentKey) - i.valid = false - return i.err - } } } } @@ -394,16 +403,24 @@ func (i *intentInterleavingIter) maybeSkipIntentRangeKey() error { // direction if the underlying iterator has moved past an intent onto a // different range key that should not be surfaced yet. Must be called after // computePos(). +// +// gcassert:inline func (i *intentInterleavingIter) maybeSuppressRangeKeyChanged() { if util.RaceEnabled && i.dir > 0 { panic(errors.AssertionFailedf("maybeSuppressRangeKeyChanged called in forward direction")) } - if i.rangeKeyChanged && i.isCurAtIntentIterReverse() && i.intentCmp > 0 && - i.iter.RangeBounds().EndKey.Compare(i.intentKey) <= 0 { - i.rangeKeyChanged = false + // NB: i.intentCmp implies isCurAtIntentIterReverse(), but cheaper. + if i.rangeKeyChanged && i.intentCmp > 0 { + i.doMaybeSuppressRangeKeyChanged() } } +// doMaybeSuppressRangeKeyChanges is a helper for maybeSuppressRangeKeyChanged +// which allows mid-stack inlining of the former. +func (i *intentInterleavingIter) doMaybeSuppressRangeKeyChanged() { + i.rangeKeyChanged = i.iter.RangeBounds().EndKey.Compare(i.intentKey) > 0 +} + func (i *intentInterleavingIter) SeekGE(key MVCCKey) { i.dir = +1 i.valid = true From e209dc1b80f842d451c751ad3947e9448ea83fd4 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Fri, 19 Aug 2022 08:22:36 +0000 Subject: [PATCH 03/11] storage: use concrete `pebbleIterator` in `intentInterleavingIter` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since `intentInterleavingIter` always constructs the underlying iterators from the given reader, and these readers always construct `*pebbleIterator`, it can use the concrete type rather than interfaces for both iterators. This avoids dynamic dispatch, yielding a decent performance improvement. Unfortunately, this requires disabling `unsafeMVCCIterator` inside `intentInterleavingIter`. This wasn't always enabled anyway, since it was omitted both for the engine iterator and when using an engine directly (which doesn't have consistent iterator). ``` name old time/op new time/op delta MVCCScan_Pebble/rows=1/versions=1/valueSize=64/numRangeKeys=0-24 5.43µs ± 2% 5.45µs ± 2% ~ (p=0.566 n=10+10) MVCCScan_Pebble/rows=100/versions=1/valueSize=64/numRangeKeys=0-24 38.2µs ± 2% 37.0µs ± 1% -3.02% (p=0.000 n=10+10) MVCCScan_Pebble/rows=10000/versions=1/valueSize=64/numRangeKeys=0-24 2.71ms ± 2% 2.66ms ± 1% -1.83% (p=0.000 n=10+9) MVCCReverseScan_Pebble/rows=1/versions=1/valueSize=64/numRangeKeys=0-24 5.99µs ± 2% 5.86µs ± 2% -2.15% (p=0.000 n=10+10) MVCCReverseScan_Pebble/rows=100/versions=1/valueSize=64/numRangeKeys=0-24 52.1µs ± 1% 50.2µs ± 2% -3.77% (p=0.000 n=10+10) MVCCReverseScan_Pebble/rows=10000/versions=1/valueSize=64/numRangeKeys=0-24 3.87ms ± 1% 3.83ms ± 1% -1.26% (p=0.000 n=8+10) MVCCComputeStats_Pebble/valueSize=32/numRangeKeys=0-24 155ms ± 1% 155ms ± 1% ~ (p=0.842 n=9+10) ``` Release justification: low risk, high benefit changes to existing functionality Release note: None --- pkg/storage/intent_interleaving_iter.go | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/pkg/storage/intent_interleaving_iter.go b/pkg/storage/intent_interleaving_iter.go index 46a2c5fb98e8..6bdf59db6380 100644 --- a/pkg/storage/intent_interleaving_iter.go +++ b/pkg/storage/intent_interleaving_iter.go @@ -86,7 +86,7 @@ type intentInterleavingIter struct { constraint intentInterleavingIterConstraint // iter is for iterating over MVCC keys and interleaved intents. - iter MVCCIterator + iter *pebbleIterator // MVCCIterator // The valid value from iter.Valid() after the last positioning call. iterValid bool // When iterValid = true, this contains the result of iter.UnsafeKey(). We @@ -96,7 +96,7 @@ type intentInterleavingIter struct { // intentIter is for iterating over separated intents, so that // intentInterleavingIter can make them look as if they were interleaved. - intentIter EngineIterator + intentIter *pebbleIterator // EngineIterator intentIterState pebble.IterValidityState // The decoded key from the lock table. This is an unsafe key // in that it is only valid when intentIter has not been @@ -255,17 +255,21 @@ func newIntentInterleavingIterator(reader Reader, opts IterOptions) MVCCIterator // constrainedToGlobal. intentOpts.UpperBound = keys.LockTableSingleKeyEnd } + + // All readers given to intentInterleavingIter construct pebbleIterators, so + // we can use the concrete type here to avoid the cost of dynamic dispatch. + // // Note that we can reuse intentKeyBuf, intentLimitKeyBuf after // NewEngineIterator returns. - intentIter := reader.NewEngineIterator(intentOpts) + intentIter := reader.NewEngineIterator(intentOpts).(*pebbleIterator) // The creation of these iterators can race with concurrent mutations, which // may make them inconsistent with each other. So we clone here, to ensure // consistency (certain Reader implementations already ensure consistency, // and we use that when possible to save allocations). - var iter MVCCIterator + var iter *pebbleIterator if reader.ConsistentIterators() { - iter = reader.NewMVCCIterator(MVCCKeyIterKind, opts) + iter = maybeUnwrapUnsafeIter(reader.NewMVCCIterator(MVCCKeyIterKind, opts)).(*pebbleIterator) } else { iter = newPebbleIteratorByCloning( intentIter.GetRawIter(), opts, StandardDurability, reader.SupportsRangeKeys()) @@ -1279,6 +1283,16 @@ func maybeWrapInUnsafeIter(iter MVCCIterator) MVCCIterator { return iter } +// gcassert:inline +func maybeUnwrapUnsafeIter(iter MVCCIterator) MVCCIterator { + if util.RaceEnabled { + if unsafeIter, ok := iter.(*unsafeMVCCIterator); ok { + return unsafeIter.MVCCIterator + } + } + return iter +} + var _ MVCCIterator = &unsafeMVCCIterator{} func (i *unsafeMVCCIterator) SeekGE(key MVCCKey) { From bfad96ea68c11bc11fd55279b48e52b7057f9f22 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Fri, 19 Aug 2022 18:53:25 +0000 Subject: [PATCH 04/11] storage: add Pebble SST iterator benchmarks Release justification: non-production code changes Release note: None --- pkg/storage/bench_pebble_test.go | 10 +++++++--- pkg/storage/bench_test.go | 22 ++++++++++++++++++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/pkg/storage/bench_pebble_test.go b/pkg/storage/bench_pebble_test.go index f1b124d12105..d9abc0a4cde5 100644 --- a/pkg/storage/bench_pebble_test.go +++ b/pkg/storage/bench_pebble_test.go @@ -494,9 +494,13 @@ func BenchmarkCheckSSTConflicts(b *testing.B) { func BenchmarkSSTIterator(b *testing.B) { for _, numKeys := range []int{1, 100, 10000} { b.Run(fmt.Sprintf("keys=%d", numKeys), func(b *testing.B) { - for _, verify := range []bool{false, true} { - b.Run(fmt.Sprintf("verify=%t", verify), func(b *testing.B) { - runSSTIterator(b, numKeys, verify) + for _, variant := range []string{"legacy", "pebble"} { + b.Run(fmt.Sprintf("variant=%s", variant), func(b *testing.B) { + for _, verify := range []bool{false, true} { + b.Run(fmt.Sprintf("verify=%t", verify), func(b *testing.B) { + runSSTIterator(b, variant, numKeys, verify) + }) + } }) } }) diff --git a/pkg/storage/bench_test.go b/pkg/storage/bench_test.go index 6f67d9fcf316..182ccf8064fb 100644 --- a/pkg/storage/bench_test.go +++ b/pkg/storage/bench_test.go @@ -1728,7 +1728,7 @@ func runCheckSSTConflicts( } } -func runSSTIterator(b *testing.B, numKeys int, verify bool) { +func runSSTIterator(b *testing.B, variant string, numKeys int, verify bool) { keyBuf := append(make([]byte, 0, 64), []byte("key-")...) value := MVCCValue{Value: roachpb.MakeValueFromBytes(bytes.Repeat([]byte("a"), 128))} @@ -1744,9 +1744,27 @@ func runSSTIterator(b *testing.B, numKeys int, verify bool) { } sstWriter.Close() + var makeSSTIterator func(data []byte, verify bool) (SimpleMVCCIterator, error) + switch variant { + case "legacy": + makeSSTIterator = func(data []byte, verify bool) (SimpleMVCCIterator, error) { + return NewMemSSTIterator(data, verify) + } + case "pebble": + makeSSTIterator = func(data []byte, verify bool) (SimpleMVCCIterator, error) { + return NewPebbleMemSSTIterator(data, verify, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + LowerBound: keys.MinKey, + UpperBound: keys.MaxKey, + }) + } + default: + b.Fatalf("unknown variant %q", variant) + } + b.ResetTimer() for i := 0; i < b.N; i++ { - iter, err := NewMemSSTIterator(sstFile.Bytes(), verify) + iter, err := makeSSTIterator(sstFile.Bytes(), verify) if err != nil { b.Fatal(err) } From a86ed196744b07602e5fd7d3ac5ae07a8a6810b6 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Fri, 19 Aug 2022 18:10:30 +0000 Subject: [PATCH 05/11] storage: use concrete `pebbleIterator` in `verifyingIterator` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gives a slight performance boost, since it avoid dynamic dispatch: ``` name old time/op new time/op delta SSTIterator/keys=1/variant=pebble/verify=true-24 42.8µs ± 1% 42.4µs ± 1% -0.79% (p=0.043 n=10+10) SSTIterator/keys=100/variant=pebble/verify=true-24 61.8µs ± 1% 60.7µs ± 1% -1.64% (p=0.000 n=10+10) SSTIterator/keys=10000/variant=pebble/verify=true-24 1.91ms ± 0% 1.88ms ± 0% -1.79% (p=0.000 n=10+10) ``` An attempt was also made at using `RangeKeyChanged()` instead of `HasPointAndRange()`, but this had no effect. Release justification: bug fixes and low-risk updates to new functionality Release note: None --- pkg/storage/sst_iterator.go | 2 +- pkg/storage/verifying_iterator.go | 71 ++++++++++++++++--------------- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/pkg/storage/sst_iterator.go b/pkg/storage/sst_iterator.go index 8bc7ea8b17d8..66cd652f1d1a 100644 --- a/pkg/storage/sst_iterator.go +++ b/pkg/storage/sst_iterator.go @@ -52,7 +52,7 @@ func NewPebbleMultiMemSSTIterator( return nil, err } if verify { - iter = NewVerifyingMVCCIterator(iter) + iter = newVerifyingMVCCIterator(iter.(*pebbleIterator)) } return iter, nil } diff --git a/pkg/storage/verifying_iterator.go b/pkg/storage/verifying_iterator.go index 21a82adf6bd5..ab64384072fa 100644 --- a/pkg/storage/verifying_iterator.go +++ b/pkg/storage/verifying_iterator.go @@ -15,31 +15,34 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/uuid" ) -// VerifyingMVCCIterator is an MVCC iterator that wraps an arbitrary MVCC -// iterator and verifies roachpb.Value checksums for encountered values. -type VerifyingMVCCIterator struct { - MVCCIterator - - valid bool - err error - key MVCCKey - value []byte +// verifyingMVCCIterator is an MVCC iterator that wraps a pebbleIterator and +// verifies roachpb.Value checksums for encountered values. +type verifyingMVCCIterator struct { + *pebbleIterator // concrete type to avoid dynamic dispatch + + valid bool + err error + key MVCCKey + value []byte + hasPoint bool + hasRange bool } -// NewVerifyingMVCCIterator creates a new VerifyingMVCCIterator. -func NewVerifyingMVCCIterator(iter MVCCIterator) MVCCIterator { - return &VerifyingMVCCIterator{MVCCIterator: iter} +// newVerifyingMVCCIterator creates a new VerifyingMVCCIterator. +func newVerifyingMVCCIterator(iter *pebbleIterator) MVCCIterator { + return &verifyingMVCCIterator{pebbleIterator: iter} } // saveAndVerify fetches the current key and value, saves them in the iterator, // and verifies the value. -func (i *VerifyingMVCCIterator) saveAndVerify() { - if i.valid, i.err = i.MVCCIterator.Valid(); !i.valid || i.err != nil { +func (i *verifyingMVCCIterator) saveAndVerify() { + if i.valid, i.err = i.pebbleIterator.Valid(); !i.valid || i.err != nil { return } - i.key = i.MVCCIterator.UnsafeKey() - if hasPoint, _ := i.MVCCIterator.HasPointAndRange(); hasPoint { - i.value = i.MVCCIterator.UnsafeValue() + i.key = i.pebbleIterator.UnsafeKey() + i.hasPoint, i.hasRange = i.pebbleIterator.HasPointAndRange() + if i.hasPoint { + i.value = i.pebbleIterator.UnsafeValue() if i.key.IsValue() { mvccValue, ok, err := tryDecodeSimpleMVCCValue(i.value) if !ok && err == nil { @@ -58,57 +61,57 @@ func (i *VerifyingMVCCIterator) saveAndVerify() { } // Next implements MVCCIterator. -func (i *VerifyingMVCCIterator) Next() { - i.MVCCIterator.Next() +func (i *verifyingMVCCIterator) Next() { + i.pebbleIterator.Next() i.saveAndVerify() } // NextKey implements MVCCIterator. -func (i *VerifyingMVCCIterator) NextKey() { - i.MVCCIterator.NextKey() +func (i *verifyingMVCCIterator) NextKey() { + i.pebbleIterator.NextKey() i.saveAndVerify() } // Prev implements MVCCIterator. -func (i *VerifyingMVCCIterator) Prev() { - i.MVCCIterator.Prev() +func (i *verifyingMVCCIterator) Prev() { + i.pebbleIterator.Prev() i.saveAndVerify() } // SeekGE implements MVCCIterator. -func (i *VerifyingMVCCIterator) SeekGE(key MVCCKey) { - i.MVCCIterator.SeekGE(key) +func (i *verifyingMVCCIterator) SeekGE(key MVCCKey) { + i.pebbleIterator.SeekGE(key) i.saveAndVerify() } // SeekIntentGE implements MVCCIterator. -func (i *VerifyingMVCCIterator) SeekIntentGE(key roachpb.Key, txnUUID uuid.UUID) { - i.MVCCIterator.SeekIntentGE(key, txnUUID) +func (i *verifyingMVCCIterator) SeekIntentGE(key roachpb.Key, txnUUID uuid.UUID) { + i.pebbleIterator.SeekIntentGE(key, txnUUID) i.saveAndVerify() } // SeekLT implements MVCCIterator. -func (i *VerifyingMVCCIterator) SeekLT(key MVCCKey) { - i.MVCCIterator.SeekLT(key) +func (i *verifyingMVCCIterator) SeekLT(key MVCCKey) { + i.pebbleIterator.SeekLT(key) i.saveAndVerify() } // UnsafeKey implements MVCCIterator. -func (i *VerifyingMVCCIterator) UnsafeKey() MVCCKey { +func (i *verifyingMVCCIterator) UnsafeKey() MVCCKey { return i.key } // UnsafeValue implements MVCCIterator. -func (i *VerifyingMVCCIterator) UnsafeValue() []byte { +func (i *verifyingMVCCIterator) UnsafeValue() []byte { return i.value } // Valid implements MVCCIterator. -func (i *VerifyingMVCCIterator) Valid() (bool, error) { +func (i *verifyingMVCCIterator) Valid() (bool, error) { return i.valid, i.err } // HasPointAndRange implements MVCCIterator. -func (i *VerifyingMVCCIterator) HasPointAndRange() (bool, bool) { - return i.MVCCIterator.HasPointAndRange() +func (i *verifyingMVCCIterator) HasPointAndRange() (bool, bool) { + return i.hasPoint, i.hasRange } From 44cb4737fd7c5b84f6ddff98956992c73bf26322 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Tue, 16 Aug 2022 17:19:51 +0000 Subject: [PATCH 06/11] storage: use `RangeKeyChanged()` in `MVCCIncrementalIterator` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch uses `RangeKeyChanged()` to detect changes to range keys in `MVCCIncrementalIterator`. There are no functional changes. Some quick benchmarks, using catchup scans: ``` name old time/op new time/op delta CatchUpScan/mixed-case/withDiff=true/perc=0.00/numRangeKeys=0-24 538ms ± 1% 535ms ± 1% ~ (p=0.211 n=10+9) CatchUpScan/mixed-case/withDiff=true/perc=0.00/numRangeKeys=1-24 690ms ± 0% 590ms ± 1% -14.56% (p=0.000 n=9+10) CatchUpScan/mixed-case/withDiff=true/perc=0.00/numRangeKeys=100-24 743ms ± 1% 646ms ± 1% -13.13% (p=0.000 n=9+9) CatchUpScan/mixed-case/withDiff=false/perc=0.00/numRangeKeys=0-24 794ms ± 1% 794ms ± 0% ~ (p=0.579 n=10+10) CatchUpScan/mixed-case/withDiff=false/perc=0.00/numRangeKeys=1-24 966ms ± 0% 911ms ± 1% -5.72% (p=0.000 n=10+10) CatchUpScan/mixed-case/withDiff=false/perc=0.00/numRangeKeys=100-24 974ms ± 0% 920ms ± 0% -5.51% (p=0.000 n=10+10) ``` Release justification: bug fixes and low-risk updates to new functionality. Release note: None --- pkg/storage/mvcc_incremental_iterator.go | 172 ++++++++++-------- .../mvcc_histories/range_key_iter_incremental | 76 ++++++++ 2 files changed, 170 insertions(+), 78 deletions(-) diff --git a/pkg/storage/mvcc_incremental_iterator.go b/pkg/storage/mvcc_incremental_iterator.go index 0ff24c59af13..8f9766daa979 100644 --- a/pkg/storage/mvcc_incremental_iterator.go +++ b/pkg/storage/mvcc_incremental_iterator.go @@ -93,16 +93,14 @@ type MVCCIncrementalIterator struct { // or range key from the underlying iterator. If true, this implies that the // underlying iterator returns true as well. This can be used to hide point or // range keys where one key kind satisfies the time predicate but the other - // one doesn't. + // one doesn't. Ignored following IgnoringTime() calls. hasPoint, hasRange bool - // rangeKeysStart contains the last seen range key start bound. It is used - // to detect changes to range keys. - // - // TODO(erikgrinaker): This pattern keeps coming up, and involves one - // comparison for every covered point key. Consider exposing this from Pebble, - // who has presumably already done these comparisons, so we can avoid them. - rangeKeysStart roachpb.Key + // rangeKeys contains the filtered range keys at the current location. + rangeKeys MVCCRangeKeyStack + + // rangeKeysIgnoringTime contains the complete range keys at the current location. + rangeKeysIgnoringTime MVCCRangeKeyStack // ignoringTime is true if the iterator is currently ignoring time bounds, // i.e. following a call to NextIgnoringTime(). @@ -246,8 +244,7 @@ func (i *MVCCIncrementalIterator) SeekGE(startKey MVCCKey) { } } i.iter.SeekGE(startKey) - i.rangeKeysStart = nil - i.advance() + i.advance(true /* seeked */) } // Close implements SimpleMVCCIterator. @@ -261,7 +258,7 @@ func (i *MVCCIncrementalIterator) Close() { // Next implements SimpleMVCCIterator. func (i *MVCCIncrementalIterator) Next() { i.iter.Next() - i.advance() + i.advance(false /* seeked */) } // updateValid updates i.valid and i.err based on the underlying iterator, and @@ -275,14 +272,17 @@ func (i *MVCCIncrementalIterator) updateValid() bool { // NextKey implements SimpleMVCCIterator. func (i *MVCCIncrementalIterator) NextKey() { i.iter.NextKey() - i.advance() + i.advance(false /* seeked */) } // maybeSkipKeys checks if any keys can be skipped by using a time-bound // iterator. If keys can be skipped, it will update the main iterator to point -// to the earliest version of the next candidate key. -// It is expected (but not required) that TBI is at a key <= main iterator key -// when calling maybeSkipKeys(). +// to the earliest version of the next candidate key. It is expected (but not +// required) that TBI is at a key <= main iterator key when calling +// maybeSkipKeys(). +// +// Returns true if any of the iter positioning operations caused the range keys +// to change. // // NB: This logic will not handle TBI range key filtering properly -- the TBI // may see different range key fragmentation than the regular iterator, causing @@ -290,10 +290,10 @@ func (i *MVCCIncrementalIterator) NextKey() { // disabled in pebbleMVCCIterator, since the performance gains are expected to // be marginal, and the necessary seeks/processing here would likely negate it. // See: https://github.com/cockroachdb/cockroach/issues/86260 -func (i *MVCCIncrementalIterator) maybeSkipKeys() { +func (i *MVCCIncrementalIterator) maybeSkipKeys() (rangeKeyChanged bool) { if i.timeBoundIter == nil { // If there is no time bound iterator, we cannot skip any keys. - return + return false } tbiKey := i.timeBoundIter.UnsafeKey().Key iterKey := i.iter.UnsafeKey().Key @@ -312,7 +312,7 @@ func (i *MVCCIncrementalIterator) maybeSkipKeys() { i.timeBoundIter.NextKey() if ok, err := i.timeBoundIter.Valid(); !ok { i.valid, i.err = false, err - return + return false } tbiKey = i.timeBoundIter.UnsafeKey().Key @@ -327,7 +327,7 @@ func (i *MVCCIncrementalIterator) maybeSkipKeys() { i.timeBoundIter.SeekGE(seekKey) if ok, err := i.timeBoundIter.Valid(); !ok { i.valid, i.err = false, err - return + return false } tbiKey = i.timeBoundIter.UnsafeKey().Key @@ -338,7 +338,7 @@ func (i *MVCCIncrementalIterator) maybeSkipKeys() { i.timeBoundIter.Next() if ok, err := i.timeBoundIter.Valid(); !ok { i.valid, i.err = false, err - return + return false } tbiKey = i.timeBoundIter.UnsafeKey().Key } @@ -357,8 +357,9 @@ func (i *MVCCIncrementalIterator) maybeSkipKeys() { seekKey := MakeMVCCMetadataKey(tbiKey) i.iter.SeekGE(seekKey) if !i.updateValid() { - return + return false } + rangeKeyChanged := i.iter.RangeKeyChanged() // The seek may have landed in the middle of a bare range key, in which // case we should move on to the next key. @@ -366,12 +367,15 @@ func (i *MVCCIncrementalIterator) maybeSkipKeys() { if !i.iter.RangeBounds().Key.Equal(i.iter.UnsafeKey().Key) { i.iter.Next() if !i.updateValid() { - return + return false } + rangeKeyChanged = rangeKeyChanged || i.iter.RangeKeyChanged() } } + return rangeKeyChanged } } + return false } // updateMeta initializes i.meta. It sets i.err and returns an error on any @@ -433,57 +437,78 @@ func (i *MVCCIncrementalIterator) updateMeta() error { return nil } +// updateRangeKeys updates the iterator with the current range keys, filtered by +// time span, and returns whether the position has point and/or range keys. +func (i *MVCCIncrementalIterator) updateRangeKeys() (bool, bool) { + hasPoint, hasRange := i.iter.HasPointAndRange() + if hasRange { + // Clone full set of range keys into i.rangeKeysIgnoringTime. + rangeKeys := i.iter.RangeKeys() + rangeKeys.CloneInto(&i.rangeKeysIgnoringTime) + + // Keep trimmed subset in i.rangeKeys. + i.rangeKeys = i.rangeKeysIgnoringTime + i.rangeKeys.Trim(i.startTime.Next(), i.endTime) + if i.rangeKeys.IsEmpty() { + i.rangeKeys.Clear() + hasRange = false + } + } else { + i.rangeKeys.Clear() + i.rangeKeysIgnoringTime.Clear() + } + return hasPoint, hasRange +} + // advance advances the main iterator until it is referencing a key within -// (start_time, end_time]. +// (start_time, end_time]. If seeked is true, the caller is a SeekGE operation, +// in which case we should emit the current range key position even if +// RangeKeyChanged() doesn't trigger. // // It populates i.err with an error if it encountered an inline value or an // intent with a timestamp within the incremental iterator's bounds when the // intent policy is MVCCIncrementalIterIntentPolicyError. -func (i *MVCCIncrementalIterator) advance() { +func (i *MVCCIncrementalIterator) advance(seeked bool) { i.ignoringTime = false for { if !i.updateValid() { return } - i.maybeSkipKeys() + + // If the caller was a SeekGE operation, process the initial range key (if + // any) even if RangeKeyChanged() does not fire. + rangeKeyChanged := seeked || i.iter.RangeKeyChanged() + seeked = false + + if i.maybeSkipKeys() { + rangeKeyChanged = true + } if !i.valid { return } - // NB: Don't update i.hasRange directly -- we only change it when - // i.rangeKeysStart changes, which allows us to retain i.hasRange=false if - // we've already determined that the current range keys are outside of the - // time bounds. - hasPoint, hasRange := i.iter.HasPointAndRange() - i.hasPoint = hasPoint - // Process range keys. - // - // TODO(erikgrinaker): This needs to be optimized. For example, range keys - // only change on unversioned keys (except after a SeekGE), which can save a - // bunch of comparisons here. HasPointAndRange() has also been seen to have - // a non-negligible cost even without any range keys. var newRangeKey bool - if hasRange { - if rangeStart := i.iter.RangeBounds().Key; !rangeStart.Equal(i.rangeKeysStart) { - i.rangeKeysStart = append(i.rangeKeysStart[:0], rangeStart...) - i.hasRange = i.iter.RangeKeys().HasBetween(i.startTime.Next(), i.endTime) - newRangeKey = i.hasRange + if rangeKeyChanged { + i.hasPoint, i.hasRange = i.updateRangeKeys() + newRangeKey = i.hasRange + + // If we're on a visible, bare range key then we're done. If the range key + // was filtered out by the time bounds (the !hasPoint && !hasRange case), + // then we move on to the next key. + if !i.hasPoint { + if !i.hasRange { + i.iter.Next() + continue + } + i.meta.Reset() + return } - // Else: keep i.hasRange from last i.rangeKeysStart change. - } else { - i.hasRange = false - } - // If we're on a visible, bare range key then we're done. If the range key - // isn't visible either, then we keep going. - if !i.hasPoint { - if !i.hasRange { - i.iter.Next() - continue - } - i.meta.Reset() - return + } else if !i.hasPoint { + // If the range key didn't change, and this wasn't a seek, then we must be + // on a point key since the iterator won't surface anything else. + i.hasPoint = true } // Process point keys. @@ -548,30 +573,26 @@ func (i *MVCCIncrementalIterator) UnsafeKey() MVCCKey { // HasPointAndRange implements SimpleMVCCIterator. func (i *MVCCIncrementalIterator) HasPointAndRange() (bool, bool) { + if i.ignoringTime { + return i.iter.HasPointAndRange() + } return i.hasPoint, i.hasRange } // RangeBounds implements SimpleMVCCIterator. func (i *MVCCIncrementalIterator) RangeBounds() roachpb.Span { - if !i.hasRange { - return roachpb.Span{} + if i.ignoringTime { + return i.rangeKeysIgnoringTime.Bounds } - return i.iter.RangeBounds() + return i.rangeKeys.Bounds } // RangeKeys implements SimpleMVCCIterator. func (i *MVCCIncrementalIterator) RangeKeys() MVCCRangeKeyStack { - if !i.hasRange { - return MVCCRangeKeyStack{} + if i.ignoringTime { + return i.rangeKeysIgnoringTime } - // TODO(erikgrinaker): It may be worthwhile to clone and memoize this result - // for the same range key. However, callers may avoid calling RangeKeys() - // unnecessarily, and we may optimize parent iterators, so let's measure. - rangeKeys := i.iter.RangeKeys() - if !i.ignoringTime { - rangeKeys.Trim(i.startTime.Next(), i.endTime) - } - return rangeKeys + return i.rangeKeys } // RangeKeyChanged implements SimpleMVCCIterator. @@ -596,17 +617,12 @@ func (i *MVCCIncrementalIterator) updateIgnoreTime() { return } - i.hasPoint, i.hasRange = i.iter.HasPointAndRange() - if i.hasRange { - // Make sure we update rangeKeysStart appropriately so that switching back - // to regular iteration won't emit bare range keys twice. - if rangeStart := i.iter.RangeBounds().Key; !rangeStart.Equal(i.rangeKeysStart) { - i.rangeKeysStart = append(i.rangeKeysStart[:0], rangeStart...) + if i.iter.RangeKeyChanged() { + if i.hasPoint, i.hasRange = i.updateRangeKeys(); !i.hasPoint { + return } - } - - if !i.hasPoint { - return + } else { + i.hasPoint = true } if err := i.updateMeta(); err != nil { diff --git a/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental b/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental index 27d1030fe8fd..eb67adaa25b1 100644 --- a/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental +++ b/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental @@ -1135,6 +1135,47 @@ iter_next: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/] iter_next_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_next: "d"/4.000000000,0=/BYTES/d4 +# Switch between ignoring and respecting time with NextIgnoringTime across span. +run ok +iter_new_incremental types=pointsAndRanges k=a end=z startTs=2 endTs=4 intents=emit +iter_seek_ge k=a +iter_next +iter_next_ignoring_time +iter_next +iter_next_ignoring_time +iter_next +iter_next_ignoring_time +iter_next +iter_next_ignoring_time +iter_next +iter_next_ignoring_time +iter_next +iter_next_ignoring_time +iter_next +iter_next_ignoring_time +iter_next +iter_next_ignoring_time +iter_next +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_next: {b-c}/[3.000000000,0=/] +iter_next_ignoring_time: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_next: {c-d}/[3.000000000,0=/] +iter_next_ignoring_time: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_next: "d"/4.000000000,0=/BYTES/d4 +iter_next_ignoring_time: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_next: {f-g}/[3.000000000,0=/] +iter_next_ignoring_time: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_next: "f"/4.000000000,0=/BYTES/f4 {f-g}/[3.000000000,0=/] +iter_next_ignoring_time: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_next: {g-h}/[3.000000000,0=/] +iter_next_ignoring_time: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_next: "h"/4.000000000,0=/ +iter_next_ignoring_time: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] +iter_next: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_next_ignoring_time: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_next: . + # NextIgnoringTime with only range keys. run ok iter_new_incremental types=rangesOnly k=a end=z startTs=4 endTs=5 intents=emit @@ -1160,6 +1201,7 @@ iter_next_key_ignoring_time iter_next_key_ignoring_time iter_next_key_ignoring_time iter_next_key_ignoring_time +iter_next_key_ignoring_time ---- iter_seek_ge: "a"/4.000000000,0=/ iter_next_key_ignoring_time: {b-c}/[3.000000000,0=/ 1.000000000,0=/] @@ -1174,6 +1216,40 @@ iter_next_key_ignoring_time: "k"/5.000000000,0=/BYTES/k5 iter_next_key_ignoring_time: "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 iter_next_key_ignoring_time: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] iter_next_key_ignoring_time: "o"/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 +iter_next_key_ignoring_time: . + +# Switch between ignoring and respecting time with NextKeyIgnoringTime across span. +run ok +iter_new_incremental types=pointsAndRanges k=a end=z startTs=2 endTs=4 intents=emit +iter_seek_ge k=a +iter_next +iter_next_key_ignoring_time +iter_next +iter_next_key_ignoring_time +iter_next +iter_next_key_ignoring_time +iter_next +iter_next_key_ignoring_time +iter_next +iter_next_key_ignoring_time +iter_next +iter_next_key_ignoring_time +iter_next +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_next: {b-c}/[3.000000000,0=/] +iter_next_key_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_next: "d"/4.000000000,0=/BYTES/d4 +iter_next_key_ignoring_time: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_next: {f-g}/[3.000000000,0=/] +iter_next_key_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_next: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/] +iter_next_key_ignoring_time: {h-k}/[1.000000000,0=/] +iter_next: "h"/4.000000000,0=/ +iter_next_key_ignoring_time: "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 {h-k}/[1.000000000,0=/] +iter_next: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_next_key_ignoring_time: "o"/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 +iter_next: . # NextKeyIgnoringTime with only range keys. run ok From 290926e60c6cafcd2da2e49103d40dc1077c748e Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Tue, 16 Aug 2022 19:04:26 +0000 Subject: [PATCH 07/11] storage: implement `RangeKeyChanged()` for `MVCCIncrementalIterator` This patch implements `RangeKeyChanged()` for `MVCCIncrementalIterator`. It only fires if the time bound range keys change, not if a `Next(Key)IgnoringTime()` operation reveals additional range keys. Release justification: bug fixes and low-risk updates to new functionality Release note: None --- pkg/storage/mvcc_history_test.go | 28 +- pkg/storage/mvcc_incremental_iterator.go | 28 +- .../mvcc_histories/range_key_iter_incremental | 718 +++++++++--------- 3 files changed, 399 insertions(+), 375 deletions(-) diff --git a/pkg/storage/mvcc_history_test.go b/pkg/storage/mvcc_history_test.go index 7c572798e70c..e37376013e22 100644 --- a/pkg/storage/mvcc_history_test.go +++ b/pkg/storage/mvcc_history_test.go @@ -1605,16 +1605,15 @@ func cmdIterScan(e *evalCtx) error { // adjust e.iterRangeKeys to comply with the previous positioning operation. // The previous position already passed this check, so it doesn't matter that // we're fudging e.rangeKeys. - if _, ok := e.bareIter().(*MVCCIncrementalIterator); !ok { - if e.iter.RangeKeyChanged() { - if e.iterRangeKeys.IsEmpty() { - e.iterRangeKeys = MVCCRangeKeyStack{ - Bounds: roachpb.Span{Key: keys.MinKey.Next(), EndKey: keys.MaxKey}, - Versions: MVCCRangeKeyVersions{{Timestamp: hlc.MinTimestamp}}, - } - } else { - e.iterRangeKeys.Clear() + if e.iter.RangeKeyChanged() { + if e.iterRangeKeys.IsEmpty() { + e.iterRangeKeys = MVCCRangeKeyStack{ + // NB: Clone MinKey/MaxKey, since we write into these byte slices later. + Bounds: roachpb.Span{Key: keys.MinKey.Next().Clone(), EndKey: keys.MaxKey.Clone()}, + Versions: MVCCRangeKeyVersions{{Timestamp: hlc.MinTimestamp}}, } + } else { + e.iterRangeKeys.Clear() } } @@ -1757,12 +1756,15 @@ func printIter(e *evalCtx) { } func checkAndUpdateRangeKeyChanged(e *evalCtx) bool { - // MVCCIncrementalIterator does not yet support RangeKeyChanged(). - if _, ok := e.bareIter().(*MVCCIncrementalIterator); ok { - return false - } rangeKeyChanged := e.iter.RangeKeyChanged() rangeKeys := e.iter.RangeKeys() + + // For MVCCIncrementalIterator, Next(Key)IgnoringTime() may reveal additional + // range key versions, but RangeKeyChanged only applies to the filtered set. + if incrIter, ok := e.bareIter().(*MVCCIncrementalIterator); ok && incrIter.ignoringTime { + rangeKeys = incrIter.rangeKeys + } + if rangeKeyChanged != !rangeKeys.Equal(e.iterRangeKeys) { e.t.Fatalf("incorrect RangeKeyChanged=%t (was:%s is:%s) at %s\n", rangeKeyChanged, e.iterRangeKeys, rangeKeys, e.td.Pos) diff --git a/pkg/storage/mvcc_incremental_iterator.go b/pkg/storage/mvcc_incremental_iterator.go index 8f9766daa979..19011c07c288 100644 --- a/pkg/storage/mvcc_incremental_iterator.go +++ b/pkg/storage/mvcc_incremental_iterator.go @@ -102,6 +102,10 @@ type MVCCIncrementalIterator struct { // rangeKeysIgnoringTime contains the complete range keys at the current location. rangeKeysIgnoringTime MVCCRangeKeyStack + // rangeKeyChanged is true if i.rangeKeys changed during the previous + // positioning operation. + rangeKeyChanged bool + // ignoringTime is true if the iterator is currently ignoring time bounds, // i.e. following a call to NextIgnoringTime(). ignoringTime bool @@ -243,8 +247,10 @@ func (i *MVCCIncrementalIterator) SeekGE(startKey MVCCKey) { startKey = MakeMVCCMetadataKey(tbiKey) } } + prevRangeKey := i.rangeKeys.Bounds.Key.Clone() i.iter.SeekGE(startKey) i.advance(true /* seeked */) + i.rangeKeyChanged = !prevRangeKey.Equal(i.rangeKeys.Bounds.Key) // Is there a better way? } // Close implements SimpleMVCCIterator. @@ -470,6 +476,8 @@ func (i *MVCCIncrementalIterator) updateRangeKeys() (bool, bool) { // intent policy is MVCCIncrementalIterIntentPolicyError. func (i *MVCCIncrementalIterator) advance(seeked bool) { i.ignoringTime = false + i.rangeKeyChanged = false + hadRange := !i.rangeKeys.IsEmpty() for { if !i.updateValid() { return @@ -491,6 +499,7 @@ func (i *MVCCIncrementalIterator) advance(seeked bool) { var newRangeKey bool if rangeKeyChanged { i.hasPoint, i.hasRange = i.updateRangeKeys() + i.rangeKeyChanged = hadRange || i.hasRange // !hasRange → !hasRange is no change newRangeKey = i.hasRange // If we're on a visible, bare range key then we're done. If the range key @@ -596,8 +605,12 @@ func (i *MVCCIncrementalIterator) RangeKeys() MVCCRangeKeyStack { } // RangeKeyChanged implements SimpleMVCCIterator. +// +// RangeKeyChanged only applies to the filtered set of range keys. If an +// IgnoringTime() operation reveals addition range keys or versions, these do +// not trigger RangeKeyChanged(). func (i *MVCCIncrementalIterator) RangeKeyChanged() bool { - panic("not implemented") + return i.rangeKeyChanged } // UnsafeValue implements SimpleMVCCIterator. @@ -612,16 +625,21 @@ func (i *MVCCIncrementalIterator) UnsafeValue() []byte { // intent policy. func (i *MVCCIncrementalIterator) updateIgnoreTime() { i.ignoringTime = true + i.rangeKeyChanged = false + hadRange := !i.rangeKeys.IsEmpty() for { if !i.updateValid() { return } if i.iter.RangeKeyChanged() { - if i.hasPoint, i.hasRange = i.updateRangeKeys(); !i.hasPoint { + i.hasPoint, i.hasRange = i.updateRangeKeys() + i.rangeKeyChanged = hadRange || i.hasRange // !hasRange → !hasRange is no change + if !i.hasPoint { + i.meta.Reset() return } - } else { + } else if !i.hasPoint { i.hasPoint = true } @@ -653,6 +671,8 @@ func (i *MVCCIncrementalIterator) updateIgnoreTime() { // non-incremental iteration by moving the underlying non-TBI iterator forward. // Intents within and outside the (StartTime, EndTime] time range are handled // according to the iterator policy. +// +// RangeKeyChanged() will only fire if the time bound range keys change. func (i *MVCCIncrementalIterator) NextIgnoringTime() { i.iter.Next() i.updateIgnoreTime() @@ -662,6 +682,8 @@ func (i *MVCCIncrementalIterator) NextIgnoringTime() { // in a non-incremental iteration by moving the underlying non-TBI iterator // forward. Intents within and outside the (StartTime, EndTime] time range are // handled according to the iterator policy. +// +// RangeKeyChanged() will only fire if the time bound range keys change. func (i *MVCCIncrementalIterator) NextKeyIgnoringTime() { i.iter.NextKey() i.updateIgnoreTime() diff --git a/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental b/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental index eb67adaa25b1..80ba9bf39d86 100644 --- a/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental +++ b/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental @@ -121,36 +121,36 @@ iter_new_incremental types=pointsAndRanges intents=emit iter_seek_ge k=a iter_scan ---- -iter_seek_ge: "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 {a-b}/[1.000000000,0=/] -iter_scan: "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 {a-b}/[1.000000000,0=/] +iter_seek_ge: "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 {a-b}/[1.000000000,0=/] ! +iter_scan: "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 {a-b}/[1.000000000,0=/] ! iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0=/] iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] iter_scan: "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 {h-k}/[1.000000000,0=/] iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! iter_scan: "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 iter_scan: "l"/7.000000000,0=/BYTES/l7 -iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_scan: "o"/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 +iter_scan: "o"/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 ! iter_scan: "o"/7.000000000,0=/BYTES/o7 iter_scan: . @@ -159,15 +159,15 @@ iter_new_incremental types=rangesOnly intents=emit iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_scan: {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] -iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_scan: {a-b}/[1.000000000,0=/] ! +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {h-k}/[1.000000000,0=/] ! +iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: . # Iterate across the span while moving StartTime upwards. @@ -181,30 +181,30 @@ iter_scan: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000 iter_scan: "a"/7.000000000,0=/BYTES/a7 iter_scan: "a"/4.000000000,0=/ iter_scan: "a"/2.000000000,0=/BYTES/a2 -iter_scan: {b-c}/[3.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/] -iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/] ! +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/] ! iter_scan: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/] iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/] iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/] -iter_scan: "h"/4.000000000,0=/ +iter_scan: "h"/4.000000000,0=/ ! iter_scan: "h"/3.000000000,0=/BYTES/h3 iter_scan: "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 iter_scan: "j"/7.000000000,0=/BYTES/j7 iter_scan: "k"/5.000000000,0=/BYTES/k5 iter_scan: "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 iter_scan: "l"/7.000000000,0=/BYTES/l7 -iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_scan: "o"/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 +iter_scan: "o"/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 ! iter_scan: "o"/7.000000000,0=/BYTES/o7 iter_scan: . @@ -217,28 +217,28 @@ iter_seek_ge: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000 iter_scan: "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 iter_scan: "a"/7.000000000,0=/BYTES/a7 iter_scan: "a"/4.000000000,0=/ -iter_scan: {b-c}/[3.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/] -iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/] ! +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/] ! iter_scan: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/] iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/] -iter_scan: "h"/4.000000000,0=/ +iter_scan: "h"/4.000000000,0=/ ! iter_scan: "h"/3.000000000,0=/BYTES/h3 iter_scan: "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 iter_scan: "j"/7.000000000,0=/BYTES/j7 iter_scan: "k"/5.000000000,0=/BYTES/k5 iter_scan: "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 iter_scan: "l"/7.000000000,0=/BYTES/l7 -iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_scan: "o"/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 +iter_scan: "o"/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 ! iter_scan: "o"/7.000000000,0=/BYTES/o7 iter_scan: . @@ -252,14 +252,14 @@ iter_scan: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000 iter_scan: "a"/7.000000000,0=/BYTES/a7 iter_scan: "a"/4.000000000,0=/ iter_scan: "b"/4.000000000,0=/ -iter_scan: {c-d}/[5.000000000,0=/] -iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/] ! +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/] ! iter_scan: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/] iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/] -iter_scan: "g"/4.000000000,0=/BYTES/g4 +iter_scan: "g"/4.000000000,0=/BYTES/g4 ! iter_scan: "h"/4.000000000,0=/ iter_scan: "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 iter_scan: "j"/7.000000000,0=/BYTES/j7 @@ -280,12 +280,12 @@ iter_scan iter_seek_ge: "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 iter_scan: "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 iter_scan: "a"/7.000000000,0=/BYTES/a7 -iter_scan: {c-d}/[5.000000000,0=/] -iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/] ! +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/] ! iter_scan: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/] -iter_scan: "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 +iter_scan: "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 ! iter_scan: "j"/7.000000000,0=/BYTES/j7 iter_scan: "k"/5.000000000,0=/BYTES/k5 iter_scan: "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 @@ -363,36 +363,36 @@ iter_new_incremental types=pointsAndRanges intents=emit endTs=8 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: "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 {a-b}/[1.000000000,0=/] -iter_scan: "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 {a-b}/[1.000000000,0=/] +iter_seek_ge: "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 {a-b}/[1.000000000,0=/] ! +iter_scan: "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 {a-b}/[1.000000000,0=/] ! iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0=/] iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] iter_scan: "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 {h-k}/[1.000000000,0=/] iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! iter_scan: "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 iter_scan: "l"/7.000000000,0=/BYTES/l7 -iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_scan: "o"/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 +iter_scan: "o"/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 ! iter_scan: "o"/7.000000000,0=/BYTES/o7 iter_scan: . @@ -401,34 +401,34 @@ iter_new_incremental types=pointsAndRanges intents=emit endTs=7 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: "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 {a-b}/[1.000000000,0=/] -iter_scan: "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 {a-b}/[1.000000000,0=/] +iter_seek_ge: "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 {a-b}/[1.000000000,0=/] ! +iter_scan: "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 {a-b}/[1.000000000,0=/] ! iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0=/] iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] iter_scan: "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 {h-k}/[1.000000000,0=/] iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! iter_scan: "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 iter_scan: "l"/7.000000000,0=/BYTES/l7 -iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_scan: "o"/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 +iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! +iter_scan: "o"/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 ! iter_scan: "o"/7.000000000,0=/BYTES/o7 iter_scan: . @@ -437,28 +437,28 @@ iter_new_incremental types=pointsAndRanges intents=emit endTs=6 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_scan: {a-b}/[1.000000000,0=/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_scan: {a-b}/[1.000000000,0=/] ! iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 -iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! +iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: . run ok @@ -466,27 +466,27 @@ iter_new_incremental types=pointsAndRanges intents=emit endTs=5 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_scan: {a-b}/[1.000000000,0=/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_scan: {a-b}/[1.000000000,0=/] ! iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 -iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! +iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: . run ok @@ -494,26 +494,26 @@ iter_new_incremental types=pointsAndRanges intents=emit endTs=4 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_scan: {a-b}/[1.000000000,0=/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_scan: {a-b}/[1.000000000,0=/] ! iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {d-f}/[1.000000000,0=/] +iter_scan: {c-d}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {d-f}/[1.000000000,0=/] ! iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[1.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[1.000000000,0=/] -iter_scan: {f-g}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] -iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: . run ok @@ -521,20 +521,20 @@ iter_new_incremental types=pointsAndRanges intents=emit endTs=3 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_scan: {a-b}/[1.000000000,0=/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_scan: {a-b}/[1.000000000,0=/] ! iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {d-f}/[1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {c-d}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {d-f}/[1.000000000,0=/] ! iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[1.000000000,0=/] -iter_scan: {f-g}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] -iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: . run ok @@ -542,17 +542,17 @@ iter_new_incremental types=pointsAndRanges intents=emit endTs=2 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_scan: {a-b}/[1.000000000,0=/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_scan: {a-b}/[1.000000000,0=/] ! iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[1.000000000,0=/] -iter_scan: {c-d}/[1.000000000,0=/] -iter_scan: {d-f}/[1.000000000,0=/] -iter_scan: {f-g}/[1.000000000,0=/] +iter_scan: {b-c}/[1.000000000,0=/] ! +iter_scan: {c-d}/[1.000000000,0=/] ! +iter_scan: {d-f}/[1.000000000,0=/] ! +iter_scan: {f-g}/[1.000000000,0=/] ! iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[1.000000000,0=/] -iter_scan: {g-h}/[1.000000000,0=/] +iter_scan: {g-h}/[1.000000000,0=/] ! iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: . run ok @@ -560,14 +560,14 @@ iter_new_incremental types=pointsAndRanges intents=emit endTs=1 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_scan: {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[1.000000000,0=/] -iter_scan: {c-d}/[1.000000000,0=/] -iter_scan: {d-f}/[1.000000000,0=/] -iter_scan: {f-g}/[1.000000000,0=/] -iter_scan: {g-h}/[1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_scan: {a-b}/[1.000000000,0=/] ! +iter_scan: {b-c}/[1.000000000,0=/] ! +iter_scan: {c-d}/[1.000000000,0=/] ! +iter_scan: {d-f}/[1.000000000,0=/] ! +iter_scan: {f-g}/[1.000000000,0=/] ! +iter_scan: {g-h}/[1.000000000,0=/] ! +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: . # Run a scan of a single timestamp going upwards. @@ -576,14 +576,14 @@ iter_new_incremental types=pointsAndRanges intents=emit startTs=0 endTs=1 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_scan: {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[1.000000000,0=/] -iter_scan: {c-d}/[1.000000000,0=/] -iter_scan: {d-f}/[1.000000000,0=/] -iter_scan: {f-g}/[1.000000000,0=/] -iter_scan: {g-h}/[1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_scan: {a-b}/[1.000000000,0=/] ! +iter_scan: {b-c}/[1.000000000,0=/] ! +iter_scan: {c-d}/[1.000000000,0=/] ! +iter_scan: {d-f}/[1.000000000,0=/] ! +iter_scan: {f-g}/[1.000000000,0=/] ! +iter_scan: {g-h}/[1.000000000,0=/] ! +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: . run ok @@ -602,14 +602,14 @@ iter_new_incremental types=pointsAndRanges intents=emit startTs=2 endTs=3 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {b-c}/[3.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/] -iter_scan: {c-d}/[3.000000000,0=/] -iter_scan: "e"/3.000000000,0=/BYTES/e3 -iter_scan: {f-g}/[3.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/] -iter_scan: "h"/3.000000000,0=/BYTES/h3 -iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_seek_ge: {b-c}/[3.000000000,0=/] ! +iter_scan: {b-c}/[3.000000000,0=/] ! +iter_scan: {c-d}/[3.000000000,0=/] ! +iter_scan: "e"/3.000000000,0=/BYTES/e3 ! +iter_scan: {f-g}/[3.000000000,0=/] ! +iter_scan: {g-h}/[3.000000000,0=/] ! +iter_scan: "h"/3.000000000,0=/BYTES/h3 ! +iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: . run ok @@ -631,11 +631,11 @@ iter_new_incremental types=pointsAndRanges intents=emit startTs=4 endTs=5 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {c-d}/[5.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/] -iter_scan: {d-f}/[5.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_seek_ge: {c-d}/[5.000000000,0=/] ! +iter_scan: {c-d}/[5.000000000,0=/] ! +iter_scan: {d-f}/[5.000000000,0=/] ! +iter_scan: {f-g}/[5.000000000,0=/] ! +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! iter_scan: . run ok @@ -689,12 +689,12 @@ iter_new_incremental types=pointsAndRanges k=ccc end=fff intents=emit startTs=1 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {ccc-d}/[5.000000000,0=/ 3.000000000,0=/] -iter_scan: {ccc-d}/[5.000000000,0=/ 3.000000000,0=/] -iter_scan: {d-f}/[5.000000000,0=/] +iter_seek_ge: {ccc-d}/[5.000000000,0=/ 3.000000000,0=/] ! +iter_scan: {ccc-d}/[5.000000000,0=/ 3.000000000,0=/] ! +iter_scan: {d-f}/[5.000000000,0=/] ! iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/] -iter_scan: f{-ff}/[5.000000000,0=/ 3.000000000,0=/] +iter_scan: f{-ff}/[5.000000000,0=/ 3.000000000,0=/] ! iter_scan: "f"/4.000000000,0=/BYTES/f4 f{-ff}/[5.000000000,0=/ 3.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 f{-ff}/[5.000000000,0=/ 3.000000000,0=/] iter_scan: . @@ -717,19 +717,19 @@ iter_next_key iter_next_key iter_next_key ---- -iter_seek_ge: "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 {a-b}/[1.000000000,0=/] -iter_next_key: {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_next_key: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next_key: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_seek_ge: "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 {a-b}/[1.000000000,0=/] ! +iter_next_key: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_next_key: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_next_key: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_next_key: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_next_key: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next_key: {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_next_key: {h-k}/[1.000000000,0=/] +iter_next_key: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_next_key: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_next_key: {h-k}/[1.000000000,0=/] ! iter_next_key: "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 {h-k}/[1.000000000,0=/] -iter_next_key: "k"/5.000000000,0=/BYTES/k5 +iter_next_key: "k"/5.000000000,0=/BYTES/k5 ! iter_next_key: "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 -iter_next_key: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_next_key: "o"/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 +iter_next_key: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! +iter_next_key: "o"/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 ! iter_next_key: . run ok @@ -740,10 +740,10 @@ iter_next_key iter_next_key iter_next_key ---- -iter_seek_ge: {ccc-d}/[5.000000000,0=/ 3.000000000,0=/] -iter_next_key: {d-f}/[5.000000000,0=/] +iter_seek_ge: {ccc-d}/[5.000000000,0=/ 3.000000000,0=/] ! +iter_next_key: {d-f}/[5.000000000,0=/] ! iter_next_key: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/] -iter_next_key: f{-ff}/[5.000000000,0=/ 3.000000000,0=/] +iter_next_key: f{-ff}/[5.000000000,0=/ 3.000000000,0=/] ! iter_next_key: . # Seek to every point location. @@ -765,14 +765,14 @@ iter_seek_ge k=m iter_seek_ge k=n ---- iter_seek_ge: "a"/4.000000000,0=/ -iter_seek_ge: {b-c}/[3.000000000,0=/] -iter_seek_ge: {c-d}/[3.000000000,0=/] -iter_seek_ge: "d"/4.000000000,0=/BYTES/d4 +iter_seek_ge: {b-c}/[3.000000000,0=/] ! +iter_seek_ge: {c-d}/[3.000000000,0=/] ! +iter_seek_ge: "d"/4.000000000,0=/BYTES/d4 ! iter_seek_ge: "e"/3.000000000,0=/BYTES/e3 -iter_seek_ge: {f-g}/[3.000000000,0=/] -iter_seek_ge: {g-h}/[3.000000000,0=/] -iter_seek_ge: "h"/4.000000000,0=/ -iter_seek_ge: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_seek_ge: {f-g}/[3.000000000,0=/] ! +iter_seek_ge: {g-h}/[3.000000000,0=/] ! +iter_seek_ge: "h"/4.000000000,0=/ ! +iter_seek_ge: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_seek_ge: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] iter_seek_ge: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] iter_seek_ge: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] @@ -798,14 +798,14 @@ iter_seek_ge k=m ts=5 iter_seek_ge k=n ts=5 ---- iter_seek_ge: "a"/4.000000000,0=/ -iter_seek_ge: {b-c}/[3.000000000,0=/] -iter_seek_ge: {c-d}/[3.000000000,0=/] -iter_seek_ge: "d"/4.000000000,0=/BYTES/d4 +iter_seek_ge: {b-c}/[3.000000000,0=/] ! +iter_seek_ge: {c-d}/[3.000000000,0=/] ! +iter_seek_ge: "d"/4.000000000,0=/BYTES/d4 ! iter_seek_ge: "e"/3.000000000,0=/BYTES/e3 -iter_seek_ge: {f-g}/[3.000000000,0=/] -iter_seek_ge: {g-h}/[3.000000000,0=/] -iter_seek_ge: "h"/4.000000000,0=/ -iter_seek_ge: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_seek_ge: {f-g}/[3.000000000,0=/] ! +iter_seek_ge: {g-h}/[3.000000000,0=/] ! +iter_seek_ge: "h"/4.000000000,0=/ ! +iter_seek_ge: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_seek_ge: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] iter_seek_ge: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] iter_seek_ge: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] @@ -830,12 +830,12 @@ iter_seek_ge k=m ts=3 iter_seek_ge k=n ts=3 ---- iter_seek_ge: "b"/4.000000000,0=/ +iter_seek_ge: {c-d}/[5.000000000,0=/] ! iter_seek_ge: {c-d}/[5.000000000,0=/] -iter_seek_ge: {c-d}/[5.000000000,0=/] -iter_seek_ge: {d-f}/[5.000000000,0=/] +iter_seek_ge: {d-f}/[5.000000000,0=/] ! iter_seek_ge: {d-f}/[5.000000000,0=/] -iter_seek_ge: {f-g}/[5.000000000,0=/] -iter_seek_ge: "h"/4.000000000,0=/ +iter_seek_ge: {f-g}/[5.000000000,0=/] ! +iter_seek_ge: "h"/4.000000000,0=/ ! iter_seek_ge: "k"/5.000000000,0=/BYTES/k5 iter_seek_ge: "k"/5.000000000,0=/BYTES/k5 iter_seek_ge: "k"/5.000000000,0=/BYTES/k5 @@ -851,7 +851,7 @@ iter_new_incremental types=pointsAndRanges k=a end=z startTs=2 endTs=4 intents=e iter_seek_ge k=f ts=6 iter_next ---- -iter_seek_ge: {f-g}/[3.000000000,0=/] +iter_seek_ge: {f-g}/[3.000000000,0=/] ! iter_next: "f"/4.000000000,0=/BYTES/f4 {f-g}/[3.000000000,0=/] # Seeking twice to within the same range key must emit the bare range key both @@ -881,34 +881,34 @@ iter_new_incremental types=pointsAndRanges k=a end=z intents=aggregate iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_scan: {a-b}/[1.000000000,0=/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_scan: {a-b}/[1.000000000,0=/] ! iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0=/] iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! iter_scan: "l"/7.000000000,0=/BYTES/l7 -iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_scan: "o"/7.000000000,0=/BYTES/o7 +iter_scan: "o"/7.000000000,0=/BYTES/o7 ! iter_scan: . error: (*roachpb.WriteIntentError:) conflicting intents on "a", "d", "j", "l", "m", "o" @@ -917,32 +917,32 @@ iter_new_incremental types=pointsAndRanges k=a end=z endTs=7 intents=aggregate iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_scan: {a-b}/[1.000000000,0=/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_scan: {a-b}/[1.000000000,0=/] ! iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0=/] iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! iter_scan: "l"/7.000000000,0=/BYTES/l7 -iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_scan: "o"/7.000000000,0=/BYTES/o7 +iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! +iter_scan: "o"/7.000000000,0=/BYTES/o7 ! iter_scan: . error: (*roachpb.WriteIntentError:) conflicting intents on "a", "j", "l", "o" @@ -951,28 +951,28 @@ iter_new_incremental types=pointsAndRanges k=a end=z endTs=6 intents=aggregate iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_scan: {a-b}/[1.000000000,0=/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_scan: {a-b}/[1.000000000,0=/] ! iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 -iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! +iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: . run ok @@ -980,17 +980,17 @@ iter_new_incremental types=pointsAndRanges k=e end=j intents=aggregate iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {e-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {e-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_seek_ge: {e-f}/[5.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {e-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "e"/3.000000000,0=/BYTES/e3 {e-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-j}/[1.000000000,0=/] +iter_scan: {h-j}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-j}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-j}/[1.000000000,0=/] iter_scan: . @@ -1002,15 +1002,15 @@ iter_new_incremental types=pointsAndRanges k=a end=z startTs=2 endTs=4 intents=e iter_seek_ge k=c iter_next_ignoring_time ---- -iter_seek_ge: {c-d}/[3.000000000,0=/] -iter_next_ignoring_time: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_seek_ge: {c-d}/[3.000000000,0=/] ! +iter_next_ignoring_time: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! run error iter_new_incremental types=pointsAndRanges k=a end=z startTs=2 endTs=10 intents=error iter_seek_ge k=c iter_next_ignoring_time ---- -iter_seek_ge: {c-d}/[5.000000000,0=/ 3.000000000,0=/] +iter_seek_ge: {c-d}/[5.000000000,0=/ 3.000000000,0=/] ! iter_next_ignoring_time: err=conflicting intents on "d" error: (*roachpb.WriteIntentError:) conflicting intents on "d" @@ -1019,15 +1019,15 @@ iter_new_incremental types=pointsAndRanges k=a end=z startTs=2 endTs=4 intents=e iter_seek_ge k=c iter_next_key_ignoring_time ---- -iter_seek_ge: {c-d}/[3.000000000,0=/] -iter_next_key_ignoring_time: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_seek_ge: {c-d}/[3.000000000,0=/] ! +iter_next_key_ignoring_time: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! run error iter_new_incremental types=pointsAndRanges k=a end=z startTs=2 endTs=10 intents=error iter_seek_ge k=c iter_next_key_ignoring_time ---- -iter_seek_ge: {c-d}/[5.000000000,0=/ 3.000000000,0=/] +iter_seek_ge: {c-d}/[5.000000000,0=/ 3.000000000,0=/] ! iter_next_key_ignoring_time: err=conflicting intents on "d" error: (*roachpb.WriteIntentError:) conflicting intents on "d" @@ -1037,15 +1037,15 @@ iter_new_incremental types=rangesOnly k=a end=z intents=error iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_scan: {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] -iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_scan: {a-b}/[1.000000000,0=/] ! +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {h-k}/[1.000000000,0=/] ! +iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: . # Test NextIgnoringTime(). @@ -1082,21 +1082,21 @@ iter_next_ignoring_time ---- iter_seek_ge: "a"/4.000000000,0=/ iter_next_ignoring_time: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_next_ignoring_time: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_next_ignoring_time: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_next_ignoring_time: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_next_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_next_ignoring_time: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_next_ignoring_time: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_next_ignoring_time: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_next_ignoring_time: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_next_ignoring_time: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_next_ignoring_time: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_next_ignoring_time: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_next_ignoring_time: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_next_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_next_ignoring_time: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_next_ignoring_time: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: {h-k}/[1.000000000,0=/] +iter_next_ignoring_time: {h-k}/[1.000000000,0=/] ! iter_next_ignoring_time: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_next_ignoring_time: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] iter_next_ignoring_time: "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 {h-k}/[1.000000000,0=/] @@ -1104,9 +1104,9 @@ iter_next_ignoring_time: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0=/ txnDidNotUpdateMeta=true iter_next_ignoring_time: "l"/7.000000000,0=/BYTES/l7 -iter_next_ignoring_time: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_next_ignoring_time: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_next_ignoring_time: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_next_ignoring_time: "o"/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 +iter_next_ignoring_time: "o"/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 ! iter_next_ignoring_time: "o"/7.000000000,0=/BYTES/o7 # Switch from ignoring to respecting time at point key. @@ -1118,7 +1118,7 @@ iter_next ---- iter_seek_ge: "a"/4.000000000,0=/ iter_next_ignoring_time: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_next: {b-c}/[3.000000000,0=/] +iter_next: {b-c}/[3.000000000,0=/] ! # Switch from ignoring to respecting time at range key. run ok @@ -1130,10 +1130,10 @@ iter_next_ignoring_time iter_next ---- iter_seek_ge: "a"/4.000000000,0=/ -iter_next: {b-c}/[3.000000000,0=/] +iter_next: {b-c}/[3.000000000,0=/] ! iter_next: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/] -iter_next_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next: "d"/4.000000000,0=/BYTES/d4 +iter_next_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_next: "d"/4.000000000,0=/BYTES/d4 ! # Switch between ignoring and respecting time with NextIgnoringTime across span. run ok @@ -1158,21 +1158,21 @@ iter_next_ignoring_time iter_next ---- iter_seek_ge: "a"/4.000000000,0=/ -iter_next: {b-c}/[3.000000000,0=/] +iter_next: {b-c}/[3.000000000,0=/] ! iter_next_ignoring_time: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_next: {c-d}/[3.000000000,0=/] -iter_next_ignoring_time: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_next: {c-d}/[3.000000000,0=/] ! +iter_next_ignoring_time: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_next: "d"/4.000000000,0=/BYTES/d4 iter_next_ignoring_time: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_next: {f-g}/[3.000000000,0=/] +iter_next: {f-g}/[3.000000000,0=/] ! iter_next_ignoring_time: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_next: "f"/4.000000000,0=/BYTES/f4 {f-g}/[3.000000000,0=/] iter_next_ignoring_time: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next: {g-h}/[3.000000000,0=/] +iter_next: {g-h}/[3.000000000,0=/] ! iter_next_ignoring_time: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_next: "h"/4.000000000,0=/ +iter_next: "h"/4.000000000,0=/ ! iter_next_ignoring_time: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] -iter_next: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_next: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_next_ignoring_time: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] iter_next: . @@ -1182,8 +1182,8 @@ iter_new_incremental types=rangesOnly k=a end=z startTs=4 endTs=5 intents=emit iter_seek_ge k=f iter_next_ignoring_time ---- -iter_seek_ge: {f-g}/[5.000000000,0=/] -iter_next_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_seek_ge: {f-g}/[5.000000000,0=/] ! +iter_next_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! # Test NextKeyIgnoringTime(). run ok @@ -1204,18 +1204,18 @@ iter_next_key_ignoring_time iter_next_key_ignoring_time ---- iter_seek_ge: "a"/4.000000000,0=/ -iter_next_key_ignoring_time: {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_next_key_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next_key_ignoring_time: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_next_key_ignoring_time: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_next_key_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_next_key_ignoring_time: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_next_key_ignoring_time: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_next_key_ignoring_time: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next_key_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_next_key_ignoring_time: {h-k}/[1.000000000,0=/] +iter_next_key_ignoring_time: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_next_key_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_next_key_ignoring_time: {h-k}/[1.000000000,0=/] ! iter_next_key_ignoring_time: "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 {h-k}/[1.000000000,0=/] iter_next_key_ignoring_time: "k"/5.000000000,0=/BYTES/k5 iter_next_key_ignoring_time: "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 -iter_next_key_ignoring_time: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_next_key_ignoring_time: "o"/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 +iter_next_key_ignoring_time: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! +iter_next_key_ignoring_time: "o"/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 ! iter_next_key_ignoring_time: . # Switch between ignoring and respecting time with NextKeyIgnoringTime across span. @@ -1237,18 +1237,18 @@ iter_next_key_ignoring_time iter_next ---- iter_seek_ge: "a"/4.000000000,0=/ -iter_next: {b-c}/[3.000000000,0=/] -iter_next_key_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next: "d"/4.000000000,0=/BYTES/d4 +iter_next: {b-c}/[3.000000000,0=/] ! +iter_next_key_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_next: "d"/4.000000000,0=/BYTES/d4 ! iter_next_key_ignoring_time: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_next: {f-g}/[3.000000000,0=/] -iter_next_key_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_next: {f-g}/[3.000000000,0=/] ! +iter_next_key_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_next: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/] -iter_next_key_ignoring_time: {h-k}/[1.000000000,0=/] +iter_next_key_ignoring_time: {h-k}/[1.000000000,0=/] ! iter_next: "h"/4.000000000,0=/ iter_next_key_ignoring_time: "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 {h-k}/[1.000000000,0=/] -iter_next: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_next_key_ignoring_time: "o"/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 +iter_next: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! +iter_next_key_ignoring_time: "o"/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 ! iter_next: . # NextKeyIgnoringTime with only range keys. @@ -1257,8 +1257,8 @@ iter_new_incremental types=rangesOnly k=a end=z startTs=4 endTs=5 intents=emit iter_seek_ge k=f iter_next_key_ignoring_time ---- -iter_seek_ge: {f-g}/[5.000000000,0=/] -iter_next_key_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_seek_ge: {f-g}/[5.000000000,0=/] ! +iter_next_key_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! # Try some scans, bounded and unbounded, with only point keys. run ok @@ -1355,15 +1355,15 @@ iter_new_incremental types=rangesOnly k=a end=z endTs=8 intents=emit iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_scan: {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] -iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_scan: {a-b}/[1.000000000,0=/] ! +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: {h-k}/[1.000000000,0=/] ! +iter_scan: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: . run ok @@ -1371,11 +1371,11 @@ iter_new_incremental types=rangesOnly k=bbb end=fff startTs=2 endTs=5 intents=em iter_seek_ge k=a iter_scan ---- -iter_seek_ge: {bbb-c}/[3.000000000,0=/] -iter_scan: {bbb-c}/[3.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/] -iter_scan: {d-f}/[5.000000000,0=/] -iter_scan: f{-ff}/[5.000000000,0=/ 3.000000000,0=/] +iter_seek_ge: {bbb-c}/[3.000000000,0=/] ! +iter_scan: {bbb-c}/[3.000000000,0=/] ! +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/] ! +iter_scan: {d-f}/[5.000000000,0=/] ! +iter_scan: f{-ff}/[5.000000000,0=/ 3.000000000,0=/] ! iter_scan: . run ok @@ -1392,17 +1392,17 @@ iter_seek_ge k=i iter_seek_ge k=j iter_seek_ge k=k ---- -iter_seek_ge: {a-b}/[1.000000000,0=/] -iter_seek_ge: {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_seek_ge: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_seek_ge: {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_seek_ge: {a-b}/[1.000000000,0=/] ! +iter_seek_ge: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_seek_ge: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_seek_ge: {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_seek_ge: {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_seek_ge: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_seek_ge: {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_seek_ge: {h-k}/[1.000000000,0=/] +iter_seek_ge: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_seek_ge: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_seek_ge: {h-k}/[1.000000000,0=/] ! iter_seek_ge: {h-k}/[1.000000000,0=/] iter_seek_ge: {h-k}/[1.000000000,0=/] -iter_seek_ge: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_seek_ge: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! run ok iter_new_incremental types=rangesOnly k=a end=z endTs=8 intents=emit @@ -1414,7 +1414,7 @@ iter_seek_ge k=f ts=3 iter_seek_ge k=f ts=2 iter_seek_ge k=f ts=1 ---- -iter_seek_ge: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_seek_ge: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_seek_ge: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_seek_ge: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_seek_ge: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] @@ -1428,36 +1428,36 @@ iter_new_incremental types=pointsAndRanges k=a end=z intents=emit maskBelow=1 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: "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 {a-b}/[1.000000000,0=/] -iter_scan: "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 {a-b}/[1.000000000,0=/] +iter_seek_ge: "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 {a-b}/[1.000000000,0=/] ! +iter_scan: "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 {a-b}/[1.000000000,0=/] ! iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0=/] iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] iter_scan: "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 {h-k}/[1.000000000,0=/] iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! iter_scan: "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 iter_scan: "l"/7.000000000,0=/BYTES/l7 -iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_scan: "o"/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 +iter_scan: "o"/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 ! iter_scan: "o"/7.000000000,0=/BYTES/o7 iter_scan: . @@ -1466,36 +1466,36 @@ iter_new_incremental types=pointsAndRanges k=a end=z intents=emit maskBelow=2 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: "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 {a-b}/[1.000000000,0=/] -iter_scan: "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 {a-b}/[1.000000000,0=/] +iter_seek_ge: "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 {a-b}/[1.000000000,0=/] ! +iter_scan: "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 {a-b}/[1.000000000,0=/] ! iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0=/] iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] iter_scan: "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 {h-k}/[1.000000000,0=/] iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! iter_scan: "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 iter_scan: "l"/7.000000000,0=/BYTES/l7 -iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_scan: "o"/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 +iter_scan: "o"/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 ! iter_scan: "o"/7.000000000,0=/BYTES/o7 iter_scan: . @@ -1504,34 +1504,34 @@ iter_new_incremental types=pointsAndRanges k=a end=z intents=emit maskBelow=3 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: "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 {a-b}/[1.000000000,0=/] -iter_scan: "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 {a-b}/[1.000000000,0=/] +iter_seek_ge: "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 {a-b}/[1.000000000,0=/] ! +iter_scan: "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 {a-b}/[1.000000000,0=/] ! iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0=/] iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] iter_scan: "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 {h-k}/[1.000000000,0=/] iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! iter_scan: "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 iter_scan: "l"/7.000000000,0=/BYTES/l7 -iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_scan: "o"/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 +iter_scan: "o"/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 ! iter_scan: "o"/7.000000000,0=/BYTES/o7 iter_scan: . @@ -1540,34 +1540,34 @@ iter_new_incremental types=pointsAndRanges k=a end=z intents=emit maskBelow=4 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: "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 {a-b}/[1.000000000,0=/] -iter_scan: "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 {a-b}/[1.000000000,0=/] +iter_seek_ge: "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 {a-b}/[1.000000000,0=/] ! +iter_scan: "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 {a-b}/[1.000000000,0=/] ! iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0=/] iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] iter_scan: "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 {h-k}/[1.000000000,0=/] iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! iter_scan: "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 iter_scan: "l"/7.000000000,0=/BYTES/l7 -iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_scan: "o"/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 +iter_scan: "o"/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 ! iter_scan: "o"/7.000000000,0=/BYTES/o7 iter_scan: . @@ -1576,31 +1576,31 @@ iter_new_incremental types=pointsAndRanges k=a end=z intents=emit maskBelow=5 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: "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 {a-b}/[1.000000000,0=/] -iter_scan: "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 {a-b}/[1.000000000,0=/] +iter_seek_ge: "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 {a-b}/[1.000000000,0=/] ! +iter_scan: "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 {a-b}/[1.000000000,0=/] ! iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0=/] iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] iter_scan: "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 {h-k}/[1.000000000,0=/] iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! iter_scan: "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 iter_scan: "l"/7.000000000,0=/BYTES/l7 -iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_scan: "o"/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 +iter_scan: "o"/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 ! iter_scan: "o"/7.000000000,0=/BYTES/o7 iter_scan: . @@ -1609,31 +1609,31 @@ iter_new_incremental types=pointsAndRanges k=a end=z intents=emit maskBelow=6 iter_seek_ge k=a iter_scan ---- -iter_seek_ge: "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 {a-b}/[1.000000000,0=/] -iter_scan: "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 {a-b}/[1.000000000,0=/] +iter_seek_ge: "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 {a-b}/[1.000000000,0=/] ! +iter_scan: "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 {a-b}/[1.000000000,0=/] ! iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0=/] iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0=/] iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_scan: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! iter_scan: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_scan: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_scan: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_scan: {h-k}/[1.000000000,0=/] +iter_scan: {h-k}/[1.000000000,0=/] ! iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] iter_scan: "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 {h-k}/[1.000000000,0=/] iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0=/] -iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "k"/5.000000000,0=/BYTES/k5 ! iter_scan: "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 iter_scan: "l"/7.000000000,0=/BYTES/l7 -iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] +iter_scan: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_scan: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] -iter_scan: "o"/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 +iter_scan: "o"/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 ! iter_scan: "o"/7.000000000,0=/BYTES/o7 iter_scan: . @@ -1646,8 +1646,8 @@ iter_next_ignoring_time iter_next_ignoring_time iter_next_ignoring_time ---- -iter_seek_ge: {f-g}/[5.000000000,0=/] +iter_seek_ge: {f-g}/[5.000000000,0=/] ! iter_next_ignoring_time: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_next_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! iter_next_ignoring_time: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] iter_next_ignoring_time: {h-k}/[1.000000000,0=/] From 1b064b446962585e0e8eceea6af427b020333f9f Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Sat, 20 Aug 2022 11:59:26 +0000 Subject: [PATCH 08/11] storage: add `MVCCIncrementalIterator.RangeKeysIgnoringTime()` This patch changes `MVCCIncrementalIterator` range key behavior following a `Next(Key)IgnoringTime()` call. Previously, range key methods would then expose unfiltered range keys. Now, the standard range key methods only apply to filtered range keys. The additional `RangeKeysIgnoringTime()` and `RangeKeyChangedIgnoringTime()` methods provide access to unfiltered range keys. This implies that if such a call steps onto a range key that's entirely outside of the time bounds then: * `HasPointAndRange()` will return `false`,`false` if on a bare range key. * `RangeKeyChanged()` will not fire, unless stepping off of a range key within the time bounds. * `RangeBounds()` and `RangeKeys()` will return empty results. This is done to avoid conditional range key handling following these calls, except for the exact sites where the caller is interested in the unfiltered range keys. Release justification: bug fixes and low-risk updates to new functionality Release note: None --- pkg/kv/kvserver/rangefeed/catchup_scan.go | 20 ++-- pkg/storage/mvcc.go | 35 +++--- pkg/storage/mvcc_history_test.go | 51 ++++++++- pkg/storage/mvcc_incremental_iterator.go | 68 +++++++++--- .../mvcc_histories/range_key_iter_incremental | 100 +++++++++--------- 5 files changed, 166 insertions(+), 108 deletions(-) diff --git a/pkg/kv/kvserver/rangefeed/catchup_scan.go b/pkg/kv/kvserver/rangefeed/catchup_scan.go index 51003b88dd34..aa6b73041586 100644 --- a/pkg/kv/kvserver/rangefeed/catchup_scan.go +++ b/pkg/kv/kvserver/rangefeed/catchup_scan.go @@ -34,6 +34,7 @@ import ( type simpleCatchupIter interface { storage.SimpleMVCCIterator NextIgnoringTime() + RangeKeysIgnoringTime() storage.MVCCRangeKeyStack } type simpleCatchupIterAdapter struct { @@ -44,6 +45,10 @@ func (i simpleCatchupIterAdapter) NextIgnoringTime() { i.SimpleMVCCIterator.Next() } +func (i simpleCatchupIterAdapter) RangeKeysIgnoringTime() storage.MVCCRangeKeyStack { + return i.SimpleMVCCIterator.RangeKeys() +} + var _ simpleCatchupIter = simpleCatchupIterAdapter{} // CatchUpIterator is an iterator for catchup-scans. @@ -145,8 +150,6 @@ func (i *CatchUpIterator) CatchUpScan(outputFn outputEventFn, withDiff bool) err // Emit any new MVCC range tombstones when their start key is encountered. // Range keys can currently only be MVCC range tombstones. - // We need to verify that the range tombstone is visible at the catch-up - // timestamp, since we might have come here after a call to NextIgnoringTime. // // TODO(erikgrinaker): Find a faster/better way to detect range key changes // that doesn't involve constant comparisons. Pebble probably already knows, @@ -161,11 +164,6 @@ func (i *CatchUpIterator) CatchUpScan(outputFn outputEventFn, withDiff bool) err // Emit events for these MVCC range tombstones, in chronological order. versions := i.RangeKeys().Versions for j := len(versions) - 1; j >= 0; j-- { - if !i.startTime.LessEq(versions[j].Timestamp) { - // This range tombstone isn't visible by this catch-up scan. - continue - } - var span roachpb.Span a, span.Key = a.Copy(rangeBounds.Key, 0) a, span.EndKey = a.Copy(rangeBounds.EndKey, 0) @@ -285,12 +283,8 @@ func (i *CatchUpIterator) CatchUpScan(outputFn outputEventFn, withDiff bool) err } // If an MVCC range tombstone exists between this value and the next // one, we don't emit the value after all -- it should be a tombstone. - // - // TODO(erikgrinaker): We can't save range keys when we detect changes - // to rangeKeysStart above, because NextIgnoringTime() could reveal - // additional MVCC range tombstones below StartTime that cover this - // point. We need to find a more performant way to handle this. - if !hasRange || !i.RangeKeys().HasBetween(ts, reorderBuf[l].Val.Value.Timestamp) { + // The RangeKeysIgnoringTime() call is cheap, no need for caching. + if !i.RangeKeysIgnoringTime().HasBetween(ts, reorderBuf[l].Val.Value.Timestamp) { // TODO(sumeer): find out if it is deliberate that we are not populating // PrevValue.Timestamp. reorderBuf[l].Val.PrevValue.RawBytes = val diff --git a/pkg/storage/mvcc.go b/pkg/storage/mvcc.go index 66fc8c4e1108..9f1031f1d724 100644 --- a/pkg/storage/mvcc.go +++ b/pkg/storage/mvcc.go @@ -2622,16 +2622,8 @@ func MVCCClearTimeRange( break } - // Because we're using NextIgnoringTime() to look for older keys, it's - // possible that the iterator will surface range keys outside of the time - // bounds, so we need to do additional filtering here. - // // TODO(erikgrinaker): Consider a Clone() variant that can reuse a buffer. - // See also TODO in Clone() to use a single allocation for all byte - // slices. - rangeKeys := iter.RangeKeys() - rangeKeys.Trim(startTime.Next(), endTime) - clearRangeKeys = rangeKeys.Clone() + clearRangeKeys = iter.RangeKeys().Clone() } if hasPoint, _ := iter.HasPointAndRange(); !hasPoint { @@ -2662,7 +2654,7 @@ func MVCCClearTimeRange( // cleared key, then we didn't restore it after all, but we must still // adjust the stats for the range tombstone. if !restoredMeta.Deleted { - if v, ok := iter.RangeKeys().FirstAbove(k.Timestamp); ok { + if v, ok := iter.RangeKeysIgnoringTime().FirstAbove(k.Timestamp); ok { if v.Timestamp.LessEq(clearedMeta.Timestamp.ToTimestamp()) { restoredMeta.Deleted = true restoredMeta.KeyBytes = 0 @@ -2700,14 +2692,9 @@ func MVCCClearTimeRange( // we cleared or a different range tombstone below the one we cleared. if !v.IsTombstone() { if v, ok := clearRangeKeys.FirstAbove(k.Timestamp); ok { - // TODO(erikgrinaker): We have to fetch the complete set of range keys - // as seen by this key -- these may or may not be filtered by timestamp - // depending on whether we did a NextIgnoringTime(), so we have to fetch - // the entire set rather than using clearedRangeKeys. We should optimize - // this somehow. if !clearedMetaKey.Key.Equal(k.Key) || !clearedMeta.Timestamp.ToTimestamp().LessEq(v.Timestamp) { - if !iter.RangeKeys().HasBetween(v.Timestamp.Prev(), k.Timestamp) { + if !iter.RangeKeysIgnoringTime().HasBetween(k.Timestamp, v.Timestamp.Prev()) { ms.Add(enginepb.MVCCStats{ LastUpdateNanos: v.Timestamp.WallTime, LiveCount: 1, @@ -2921,15 +2908,17 @@ func MVCCPredicateDeleteRange( // // 2) The latest key is live, matches the predicates, and has a // timestamp below EndTime. - continueRun := func(k MVCCKey, iter SimpleMVCCIterator, + continueRun := func(k MVCCKey, iter *MVCCIncrementalIterator, ) (toContinue bool, isPointTombstone bool, isRangeTombstone bool, err error) { - hasPointKey, hasRangeKey := iter.HasPointAndRange() + // We need to see the full, unfiltered set of range keys, ignoring time + // bounds. The RangeKeysIgnoringTime() call is cheap. + hasPointKey, _ := iter.HasPointAndRange() + rangeKeys := iter.RangeKeysIgnoringTime() + hasRangeKey := !rangeKeys.IsEmpty() + if hasRangeKey { - // TODO (msbutler): cache the range keys while the range bounds remain - // constant, since iter.RangeKeys() is expensive. Manual caching may not be necessary if - // https://github.com/cockroachdb/cockroach/issues/84379 lands. - newestRangeKey := iter.RangeKeys().Versions[0].Timestamp - if endTime.LessEq(newestRangeKey) { + newestRangeKey := rangeKeys.Newest() + if endTime.LessEq(rangeKeys.Newest()) { return false, false, false, roachpb.NewWriteTooOldError( endTime, newestRangeKey.Next(), k.Key.Clone()) } diff --git a/pkg/storage/mvcc_history_test.go b/pkg/storage/mvcc_history_test.go index e37376013e22..791c64069bc2 100644 --- a/pkg/storage/mvcc_history_test.go +++ b/pkg/storage/mvcc_history_test.go @@ -1703,6 +1703,13 @@ func printIter(e *evalCtx) { e.results.buf.Printf("%s:", e.td.Cmd) defer e.results.buf.Printf("\n") + // MVCCIncrementalIterator has odd behavior following a NextIgnoringTime() + // call, so we detect this and adjust expectations. + var incrIterIgnoringTime bool + if incrIter, ok := e.bareIter().(*MVCCIncrementalIterator); ok { + incrIterIgnoringTime = incrIter.ignoringTime + } + ok, err := e.iter.Valid() if err != nil { e.results.buf.Printf(" err=%v", err) @@ -1715,7 +1722,9 @@ func printIter(e *evalCtx) { } hasPoint, hasRange := e.iter.HasPointAndRange() if !hasPoint && !hasRange { - e.t.Fatalf("valid iterator at %s without point nor range keys", e.iter.UnsafeKey()) + if !incrIterIgnoringTime || e.mvccIncrementalIter().RangeKeysIgnoringTime().IsEmpty() { + e.t.Fatalf("valid iterator at %s without point nor range keys", e.iter.UnsafeKey()) + } } if hasPoint { @@ -1750,6 +1759,29 @@ func printIter(e *evalCtx) { e.results.buf.Printf("]") } + if incrIterIgnoringTime { + rangeKeys := e.mvccIncrementalIter().RangeKeysIgnoringTime() + if !rangeKeys.Equal(e.iter.RangeKeys()) { + e.results.buf.Printf(" (+%s/[", rangeKeys.Bounds) + for i, version := range rangeKeys.Versions { + value, err := DecodeMVCCValue(version.Value) + if err != nil { + e.Fatalf("%v", err) + } + if i > 0 { + e.results.buf.Printf(" ") + } + e.results.buf.Printf("%s=%s", version.Timestamp, value) + } + e.results.buf.Printf("]") + if e.mvccIncrementalIter().RangeKeyChangedIgnoringTime() { + e.results.buf.Printf(" !") + } + e.results.buf.Printf(")") + + } + } + if checkAndUpdateRangeKeyChanged(e) { e.results.buf.Printf(" !") } @@ -1759,10 +1791,19 @@ func checkAndUpdateRangeKeyChanged(e *evalCtx) bool { rangeKeyChanged := e.iter.RangeKeyChanged() rangeKeys := e.iter.RangeKeys() - // For MVCCIncrementalIterator, Next(Key)IgnoringTime() may reveal additional - // range key versions, but RangeKeyChanged only applies to the filtered set. - if incrIter, ok := e.bareIter().(*MVCCIncrementalIterator); ok && incrIter.ignoringTime { - rangeKeys = incrIter.rangeKeys + if incrIter, ok := e.bareIter().(*MVCCIncrementalIterator); ok { + // For MVCCIncrementalIterator, make sure RangeKeyChangedIgnoringTime() fires + // whenever RangeKeyChanged() does. The inverse is not true. + rangeKeyChangedIgnoringTime := incrIter.RangeKeyChangedIgnoringTime() + if rangeKeyChanged && !rangeKeyChangedIgnoringTime { + e.t.Fatalf("RangeKeyChanged=%t but RangeKeyChangedIgnoringTime=%t", + rangeKeyChanged, incrIter.RangeKeyChangedIgnoringTime()) + } + // If RangeKeyChangedIgnoringTime() fires, and RangeKeyChanged() doesn't, + // then RangeKeys() must be empty. + if rangeKeyChangedIgnoringTime && !rangeKeyChanged && !rangeKeys.IsEmpty() { + e.t.Fatalf("RangeKeyChangedIgnoringTime without RangeKeyChanged, but RangeKeys not empty") + } } if rangeKeyChanged != !rangeKeys.Equal(e.iterRangeKeys) { diff --git a/pkg/storage/mvcc_incremental_iterator.go b/pkg/storage/mvcc_incremental_iterator.go index 19011c07c288..0976261edbfc 100644 --- a/pkg/storage/mvcc_incremental_iterator.go +++ b/pkg/storage/mvcc_incremental_iterator.go @@ -106,6 +106,10 @@ type MVCCIncrementalIterator struct { // positioning operation. rangeKeyChanged bool + // rangeKeyChangedIgnoringTime is true if i.rangeKeysIgnoringTime changed + // during the previous positioning operation. + rangeKeyChangedIgnoringTime bool + // ignoringTime is true if the iterator is currently ignoring time bounds, // i.e. following a call to NextIgnoringTime(). ignoringTime bool @@ -251,6 +255,7 @@ func (i *MVCCIncrementalIterator) SeekGE(startKey MVCCKey) { i.iter.SeekGE(startKey) i.advance(true /* seeked */) i.rangeKeyChanged = !prevRangeKey.Equal(i.rangeKeys.Bounds.Key) // Is there a better way? + i.rangeKeyChangedIgnoringTime = i.rangeKeyChanged } // Close implements SimpleMVCCIterator. @@ -476,8 +481,8 @@ func (i *MVCCIncrementalIterator) updateRangeKeys() (bool, bool) { // intent policy is MVCCIncrementalIterIntentPolicyError. func (i *MVCCIncrementalIterator) advance(seeked bool) { i.ignoringTime = false - i.rangeKeyChanged = false - hadRange := !i.rangeKeys.IsEmpty() + i.rangeKeyChanged, i.rangeKeyChangedIgnoringTime = false, false + hadRange, hadRangeIgnoringTime := !i.rangeKeys.IsEmpty(), !i.rangeKeysIgnoringTime.IsEmpty() for { if !i.updateValid() { return @@ -499,9 +504,12 @@ func (i *MVCCIncrementalIterator) advance(seeked bool) { var newRangeKey bool if rangeKeyChanged { i.hasPoint, i.hasRange = i.updateRangeKeys() - i.rangeKeyChanged = hadRange || i.hasRange // !hasRange → !hasRange is no change newRangeKey = i.hasRange + // NB: !hasRange → !hasRange is not a change. + i.rangeKeyChanged = hadRange || i.hasRange + i.rangeKeyChangedIgnoringTime = hadRangeIgnoringTime || !i.rangeKeysIgnoringTime.IsEmpty() + // If we're on a visible, bare range key then we're done. If the range key // was filtered out by the time bounds (the !hasPoint && !hasRange case), // then we move on to the next key. @@ -581,38 +589,54 @@ func (i *MVCCIncrementalIterator) UnsafeKey() MVCCKey { } // HasPointAndRange implements SimpleMVCCIterator. +// +// This only returns hasRange=true if there are filtered range keys present. +// Thus, it is possible for this to return hasPoint=false,hasRange=false +// following a NextIgnoringTime() call if positioned on a bare, filtered +// range key. In this case, the range keys are available via +// RangeKeysIgnoringTime(). func (i *MVCCIncrementalIterator) HasPointAndRange() (bool, bool) { - if i.ignoringTime { - return i.iter.HasPointAndRange() - } return i.hasPoint, i.hasRange } // RangeBounds implements SimpleMVCCIterator. +// +// This only returns the filtered range key bounds. Thus, if a +// NextIgnoringTime() call moves onto an otherwise hidden range key, this will +// still return an empty span. These hidden range keys are available via +// RangeKeysIgnoringTime(). func (i *MVCCIncrementalIterator) RangeBounds() roachpb.Span { - if i.ignoringTime { - return i.rangeKeysIgnoringTime.Bounds - } return i.rangeKeys.Bounds } // RangeKeys implements SimpleMVCCIterator. func (i *MVCCIncrementalIterator) RangeKeys() MVCCRangeKeyStack { - if i.ignoringTime { - return i.rangeKeysIgnoringTime - } return i.rangeKeys } +// RangeKeysIgnoringTime returns the range keys at the current position, +// ignoring time bounds. This call is cheap, so callers do not need to perform +// their own caching. +func (i *MVCCIncrementalIterator) RangeKeysIgnoringTime() MVCCRangeKeyStack { + return i.rangeKeysIgnoringTime +} + // RangeKeyChanged implements SimpleMVCCIterator. // // RangeKeyChanged only applies to the filtered set of range keys. If an -// IgnoringTime() operation reveals addition range keys or versions, these do -// not trigger RangeKeyChanged(). +// IgnoringTime() operation reveals additional range keys or versions, these do +// not trigger RangeKeyChanged(). See also RangeKeyChangedIgnoringTime(). func (i *MVCCIncrementalIterator) RangeKeyChanged() bool { return i.rangeKeyChanged } +// RangeKeyChangedIgnoringTime is like RangeKeyChanged, but returns true if the +// range keys returned by RangeKeysIgnoringTime() changed since the previous +// positioning operation -- in particular, after a Next(Key)IgnoringTime() call. +func (i *MVCCIncrementalIterator) RangeKeyChangedIgnoringTime() bool { + return i.rangeKeyChangedIgnoringTime +} + // UnsafeValue implements SimpleMVCCIterator. func (i *MVCCIncrementalIterator) UnsafeValue() []byte { if !i.hasPoint { @@ -625,7 +649,7 @@ func (i *MVCCIncrementalIterator) UnsafeValue() []byte { // intent policy. func (i *MVCCIncrementalIterator) updateIgnoreTime() { i.ignoringTime = true - i.rangeKeyChanged = false + i.rangeKeyChanged, i.rangeKeyChangedIgnoringTime = false, false hadRange := !i.rangeKeys.IsEmpty() for { if !i.updateValid() { @@ -635,6 +659,7 @@ func (i *MVCCIncrementalIterator) updateIgnoreTime() { if i.iter.RangeKeyChanged() { i.hasPoint, i.hasRange = i.updateRangeKeys() i.rangeKeyChanged = hadRange || i.hasRange // !hasRange → !hasRange is no change + i.rangeKeyChangedIgnoringTime = true if !i.hasPoint { i.meta.Reset() return @@ -672,7 +697,16 @@ func (i *MVCCIncrementalIterator) updateIgnoreTime() { // Intents within and outside the (StartTime, EndTime] time range are handled // according to the iterator policy. // -// RangeKeyChanged() will only fire if the time bound range keys change. +// NB: Range key methods only respect the filtered set of range keys. To access +// unfiltered range keys, use RangeKeysIgnoringTime(). This implies that if this +// call steps onto a range key that's entirely outside of the time bounds: +// +// * HasPointAndRange() will return false,false if on a bare range key. +// +// * RangeKeyChanged() will not fire, unless stepping off of a range key +// within the time bounds. +// +// * RangeBounds() and RangeKeys() will return empty results. func (i *MVCCIncrementalIterator) NextIgnoringTime() { i.iter.Next() i.updateIgnoreTime() @@ -683,7 +717,7 @@ func (i *MVCCIncrementalIterator) NextIgnoringTime() { // forward. Intents within and outside the (StartTime, EndTime] time range are // handled according to the iterator policy. // -// RangeKeyChanged() will only fire if the time bound range keys change. +// NB: See NextIgnoringTime comment for important details about range keys. func (i *MVCCIncrementalIterator) NextKeyIgnoringTime() { i.iter.NextKey() i.updateIgnoreTime() diff --git a/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental b/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental index 80ba9bf39d86..94be0cf80003 100644 --- a/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental +++ b/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental @@ -1003,7 +1003,7 @@ iter_seek_ge k=c iter_next_ignoring_time ---- iter_seek_ge: {c-d}/[3.000000000,0=/] ! -iter_next_ignoring_time: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! +iter_next_ignoring_time: "d"/8.000000000,0=/BYTES/d8 (+{d-f}/[5.000000000,0=/ 1.000000000,0=/] !) ! run error iter_new_incremental types=pointsAndRanges k=a end=z startTs=2 endTs=10 intents=error @@ -1020,7 +1020,7 @@ iter_seek_ge k=c iter_next_key_ignoring_time ---- iter_seek_ge: {c-d}/[3.000000000,0=/] ! -iter_next_key_ignoring_time: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! +iter_next_key_ignoring_time: "d"/8.000000000,0=/BYTES/d8 (+{d-f}/[5.000000000,0=/ 1.000000000,0=/] !) ! run error iter_new_incremental types=pointsAndRanges k=a end=z startTs=2 endTs=10 intents=error @@ -1081,26 +1081,26 @@ iter_next_ignoring_time iter_next_ignoring_time ---- iter_seek_ge: "a"/4.000000000,0=/ -iter_next_ignoring_time: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] -iter_next_ignoring_time: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! -iter_next_ignoring_time: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! -iter_next_ignoring_time: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! -iter_next_ignoring_time: "d"/8.000000000,0=/BYTES/d8 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! -iter_next_ignoring_time: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! -iter_next_ignoring_time: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: {h-k}/[1.000000000,0=/] ! -iter_next_ignoring_time: "h"/4.000000000,0=/ {h-k}/[1.000000000,0=/] -iter_next_ignoring_time: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] -iter_next_ignoring_time: "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 {h-k}/[1.000000000,0=/] -iter_next_ignoring_time: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0=/] +iter_next_ignoring_time: "a"/2.000000000,0=/BYTES/a2 (+{a-b}/[1.000000000,0=/]) +iter_next_ignoring_time: {b-c}/[3.000000000,0=/] (+{b-c}/[3.000000000,0=/ 1.000000000,0=/] !) ! +iter_next_ignoring_time: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/] (+{b-c}/[3.000000000,0=/ 1.000000000,0=/]) +iter_next_ignoring_time: {c-d}/[3.000000000,0=/] (+{c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] !) ! +iter_next_ignoring_time: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true (+{d-f}/[5.000000000,0=/ 1.000000000,0=/] !) ! +iter_next_ignoring_time: "d"/8.000000000,0=/BYTES/d8 (+{d-f}/[5.000000000,0=/ 1.000000000,0=/]) +iter_next_ignoring_time: "d"/4.000000000,0=/BYTES/d4 (+{d-f}/[5.000000000,0=/ 1.000000000,0=/]) +iter_next_ignoring_time: "e"/3.000000000,0=/BYTES/e3 (+{d-f}/[5.000000000,0=/ 1.000000000,0=/]) +iter_next_ignoring_time: {f-g}/[3.000000000,0=/] (+{f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] !) ! +iter_next_ignoring_time: "f"/6.000000000,0=/BYTES/f6 {f-g}/[3.000000000,0=/] (+{f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/]) +iter_next_ignoring_time: "f"/4.000000000,0=/BYTES/f4 {f-g}/[3.000000000,0=/] (+{f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/]) +iter_next_ignoring_time: "f"/2.000000000,0=/BYTES/f2 {f-g}/[3.000000000,0=/] (+{f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/]) +iter_next_ignoring_time: {g-h}/[3.000000000,0=/] (+{g-h}/[3.000000000,0=/ 1.000000000,0=/] !) ! +iter_next_ignoring_time: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/] (+{g-h}/[3.000000000,0=/ 1.000000000,0=/]) +iter_next_ignoring_time: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0=/] (+{g-h}/[3.000000000,0=/ 1.000000000,0=/]) +iter_next_ignoring_time: (+{h-k}/[1.000000000,0=/] !) ! +iter_next_ignoring_time: "h"/4.000000000,0=/ (+{h-k}/[1.000000000,0=/]) +iter_next_ignoring_time: "h"/3.000000000,0=/BYTES/h3 (+{h-k}/[1.000000000,0=/]) +iter_next_ignoring_time: "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 (+{h-k}/[1.000000000,0=/]) +iter_next_ignoring_time: "j"/7.000000000,0=/BYTES/j7 (+{h-k}/[1.000000000,0=/]) iter_next_ignoring_time: "k"/5.000000000,0=/BYTES/k5 iter_next_ignoring_time: "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 iter_next_ignoring_time: "l"/7.000000000,0=/BYTES/l7 @@ -1117,7 +1117,7 @@ iter_next_ignoring_time iter_next ---- iter_seek_ge: "a"/4.000000000,0=/ -iter_next_ignoring_time: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0=/] +iter_next_ignoring_time: "a"/2.000000000,0=/BYTES/a2 (+{a-b}/[1.000000000,0=/]) iter_next: {b-c}/[3.000000000,0=/] ! # Switch from ignoring to respecting time at range key. @@ -1132,7 +1132,7 @@ iter_next iter_seek_ge: "a"/4.000000000,0=/ iter_next: {b-c}/[3.000000000,0=/] ! iter_next: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/] -iter_next_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_next_ignoring_time: {c-d}/[3.000000000,0=/] (+{c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] !) ! iter_next: "d"/4.000000000,0=/BYTES/d4 ! # Switch between ignoring and respecting time with NextIgnoringTime across span. @@ -1159,19 +1159,19 @@ iter_next ---- iter_seek_ge: "a"/4.000000000,0=/ iter_next: {b-c}/[3.000000000,0=/] ! -iter_next_ignoring_time: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/ 1.000000000,0=/] +iter_next_ignoring_time: "b"/4.000000000,0=/ {b-c}/[3.000000000,0=/] (+{b-c}/[3.000000000,0=/ 1.000000000,0=/]) iter_next: {c-d}/[3.000000000,0=/] ! -iter_next_ignoring_time: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! +iter_next_ignoring_time: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true (+{d-f}/[5.000000000,0=/ 1.000000000,0=/] !) ! iter_next: "d"/4.000000000,0=/BYTES/d4 -iter_next_ignoring_time: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_next_ignoring_time: "e"/3.000000000,0=/BYTES/e3 (+{d-f}/[5.000000000,0=/ 1.000000000,0=/]) iter_next: {f-g}/[3.000000000,0=/] ! -iter_next_ignoring_time: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_next_ignoring_time: "f"/6.000000000,0=/BYTES/f6 {f-g}/[3.000000000,0=/] (+{f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/]) iter_next: "f"/4.000000000,0=/BYTES/f4 {f-g}/[3.000000000,0=/] -iter_next_ignoring_time: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +iter_next_ignoring_time: "f"/2.000000000,0=/BYTES/f2 {f-g}/[3.000000000,0=/] (+{f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/]) iter_next: {g-h}/[3.000000000,0=/] ! -iter_next_ignoring_time: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] +iter_next_ignoring_time: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/] (+{g-h}/[3.000000000,0=/ 1.000000000,0=/]) iter_next: "h"/4.000000000,0=/ ! -iter_next_ignoring_time: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0=/] +iter_next_ignoring_time: "h"/3.000000000,0=/BYTES/h3 (+{h-k}/[1.000000000,0=/]) iter_next: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_next_ignoring_time: "m"/8.000000000,0=/BYTES/m8 {m-n}/[3.000000000,0={localTs=2.000000000,0}/] iter_next: . @@ -1183,7 +1183,7 @@ iter_seek_ge k=f iter_next_ignoring_time ---- iter_seek_ge: {f-g}/[5.000000000,0=/] ! -iter_next_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_next_ignoring_time: (+{g-h}/[3.000000000,0=/ 1.000000000,0=/] !) ! # Test NextKeyIgnoringTime(). run ok @@ -1204,14 +1204,14 @@ iter_next_key_ignoring_time iter_next_key_ignoring_time ---- iter_seek_ge: "a"/4.000000000,0=/ -iter_next_key_ignoring_time: {b-c}/[3.000000000,0=/ 1.000000000,0=/] ! -iter_next_key_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! -iter_next_key_ignoring_time: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0=/ 1.000000000,0=/] ! -iter_next_key_ignoring_time: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] -iter_next_key_ignoring_time: {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! -iter_next_key_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! -iter_next_key_ignoring_time: {h-k}/[1.000000000,0=/] ! -iter_next_key_ignoring_time: "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 {h-k}/[1.000000000,0=/] +iter_next_key_ignoring_time: {b-c}/[3.000000000,0=/] (+{b-c}/[3.000000000,0=/ 1.000000000,0=/] !) ! +iter_next_key_ignoring_time: {c-d}/[3.000000000,0=/] (+{c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] !) ! +iter_next_key_ignoring_time: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true (+{d-f}/[5.000000000,0=/ 1.000000000,0=/] !) ! +iter_next_key_ignoring_time: "e"/3.000000000,0=/BYTES/e3 (+{d-f}/[5.000000000,0=/ 1.000000000,0=/]) +iter_next_key_ignoring_time: {f-g}/[3.000000000,0=/] (+{f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] !) ! +iter_next_key_ignoring_time: {g-h}/[3.000000000,0=/] (+{g-h}/[3.000000000,0=/ 1.000000000,0=/] !) ! +iter_next_key_ignoring_time: (+{h-k}/[1.000000000,0=/] !) ! +iter_next_key_ignoring_time: "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 (+{h-k}/[1.000000000,0=/]) iter_next_key_ignoring_time: "k"/5.000000000,0=/BYTES/k5 iter_next_key_ignoring_time: "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 iter_next_key_ignoring_time: "m"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! @@ -1238,15 +1238,15 @@ iter_next ---- iter_seek_ge: "a"/4.000000000,0=/ iter_next: {b-c}/[3.000000000,0=/] ! -iter_next_key_ignoring_time: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] ! +iter_next_key_ignoring_time: {c-d}/[3.000000000,0=/] (+{c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] !) ! iter_next: "d"/4.000000000,0=/BYTES/d4 ! -iter_next_key_ignoring_time: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0=/ 1.000000000,0=/] +iter_next_key_ignoring_time: "e"/3.000000000,0=/BYTES/e3 (+{d-f}/[5.000000000,0=/ 1.000000000,0=/]) iter_next: {f-g}/[3.000000000,0=/] ! -iter_next_key_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_next_key_ignoring_time: {g-h}/[3.000000000,0=/] (+{g-h}/[3.000000000,0=/ 1.000000000,0=/] !) ! iter_next: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/] -iter_next_key_ignoring_time: {h-k}/[1.000000000,0=/] ! +iter_next_key_ignoring_time: (+{h-k}/[1.000000000,0=/] !) ! iter_next: "h"/4.000000000,0=/ -iter_next_key_ignoring_time: "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 {h-k}/[1.000000000,0=/] +iter_next_key_ignoring_time: "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 (+{h-k}/[1.000000000,0=/]) iter_next: {m-n}/[3.000000000,0={localTs=2.000000000,0}/] ! iter_next_key_ignoring_time: "o"/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 ! iter_next: . @@ -1258,7 +1258,7 @@ iter_seek_ge k=f iter_next_key_ignoring_time ---- iter_seek_ge: {f-g}/[5.000000000,0=/] ! -iter_next_key_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! +iter_next_key_ignoring_time: (+{g-h}/[3.000000000,0=/ 1.000000000,0=/] !) ! # Try some scans, bounded and unbounded, with only point keys. run ok @@ -1647,7 +1647,7 @@ iter_next_ignoring_time iter_next_ignoring_time ---- iter_seek_ge: {f-g}/[5.000000000,0=/] ! -iter_next_ignoring_time: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] ! -iter_next_ignoring_time: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0=/ 1.000000000,0=/] -iter_next_ignoring_time: {h-k}/[1.000000000,0=/] +iter_next_ignoring_time: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0=/] (+{f-g}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/]) +iter_next_ignoring_time: (+{g-h}/[3.000000000,0=/ 1.000000000,0=/] !) ! +iter_next_ignoring_time: "g"/4.000000000,0=/BYTES/g4 (+{g-h}/[3.000000000,0=/ 1.000000000,0=/]) +iter_next_ignoring_time: (+{h-k}/[1.000000000,0=/] !) From cc25f74811262fa528c4276a42fa2b2e54603a25 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Sat, 20 Aug 2022 16:39:13 +0000 Subject: [PATCH 09/11] storage: use `RangeKeyChanged()` in `MVCCDeleteRangeUsingTombstone()` Release justification: bug fixes and low-risk updates to new functionality Release note: None --- pkg/storage/mvcc.go | 55 ++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/pkg/storage/mvcc.go b/pkg/storage/mvcc.go index 66fc8c4e1108..10e7a6ea7c28 100644 --- a/pkg/storage/mvcc.go +++ b/pkg/storage/mvcc.go @@ -3262,35 +3262,22 @@ func MVCCDeleteRangeUsingTombstone( break } - // Check for conflicts with point/range keys and update MVCC stats. - hasPoint, hasRange := iter.HasPointAndRange() - - if hasPoint { - key := iter.UnsafeKey() - if timestamp.LessEq(key.Timestamp) { - return roachpb.NewWriteTooOldError(timestamp, key.Timestamp.Next(), key.Key) - } - if key.Timestamp.IsEmpty() { - return errors.Errorf("can't write range tombstone across inline key %s", key) - } - if ms != nil { - ms.Add(updateStatsOnRangeKeyCover(timestamp, key, iter.UnsafeValue())) - } - } - - if hasRange { - if rangeBounds := iter.RangeBounds(); !rangeBounds.EndKey.Equal(prevRangeEnd) { + // Process range keys. + if iter.RangeKeyChanged() { + hasPoint, hasRange := iter.HasPointAndRange() + if hasRange { rangeKeys := iter.RangeKeys() if timestamp.LessEq(rangeKeys.Newest()) { - return roachpb.NewWriteTooOldError(timestamp, rangeKeys.Newest().Next(), rangeBounds.Key) + return roachpb.NewWriteTooOldError(timestamp, rangeKeys.Newest().Next(), + rangeKeys.Bounds.Key) } if ms != nil { // If the encountered range key does not abut the previous range key, // we'll write a new range key fragment in the gap between them. - if !rangeBounds.Key.Equal(prevRangeEnd) { + if !rangeKeys.Bounds.Key.Equal(prevRangeEnd) { ms.Add(updateStatsOnRangeKeyPut(MVCCRangeKeyStack{ - Bounds: roachpb.Span{Key: prevRangeEnd, EndKey: rangeBounds.Key}, + Bounds: roachpb.Span{Key: prevRangeEnd, EndKey: rangeKeys.Bounds.Key}, Versions: MVCCRangeKeyVersions{{Timestamp: timestamp, Value: valueRaw}}, })) } @@ -3298,17 +3285,29 @@ func MVCCDeleteRangeUsingTombstone( MVCCRangeKeyVersion{Timestamp: timestamp, Value: valueRaw})) } - prevRangeEnd = append(prevRangeEnd[:0], rangeBounds.EndKey...) + prevRangeEnd = append(prevRangeEnd[:0], rangeKeys.Bounds.EndKey...) + } + + // If we hit a bare range key, it's possible that there's a point key on the + // same key as its start key, so take a normal step to look for it. + if !hasPoint { + iter.Next() + continue } } - // If we hit a bare range key, it's possible that there's a point key on the - // same key as its start key, so take a normal step to look for it. - if hasRange && !hasPoint { - iter.Next() - } else { - iter.NextKey() + // Process point key. + key := iter.UnsafeKey() + if timestamp.LessEq(key.Timestamp) { + return roachpb.NewWriteTooOldError(timestamp, key.Timestamp.Next(), key.Key) + } + if key.Timestamp.IsEmpty() { + return errors.Errorf("can't write range tombstone across inline key %s", key) + } + if ms != nil { + ms.Add(updateStatsOnRangeKeyCover(timestamp, key, iter.UnsafeValue())) } + iter.NextKey() } // Once we've iterated across the range key span, fill in the final gap From b184a710bb393c74505abfeb9cb7a9761e19bd31 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Sat, 20 Aug 2022 16:13:07 +0000 Subject: [PATCH 10/11] storage: use `RangeKeyChanged` in `ReadAsOfIterator` This patch uses `RangeKeyChanged()` to detect range keys in `ReadAsOfIterator`, and caches them to improve performance. It also fixes a bug where the iterator would fail to detect tombstones with a non-empty `MVCCValueHeader`. Release justification: bug fixes and low-risk updates to new functionality Release note: None --- pkg/storage/multi_iterator_test.go | 16 ++++---- pkg/storage/read_as_of_iterator.go | 61 +++++++++++++++--------------- pkg/storage/sst_iterator_test.go | 8 +++- 3 files changed, 47 insertions(+), 38 deletions(-) diff --git a/pkg/storage/multi_iterator_test.go b/pkg/storage/multi_iterator_test.go index b6ed9456a2dc..bf6308c0a31d 100644 --- a/pkg/storage/multi_iterator_test.go +++ b/pkg/storage/multi_iterator_test.go @@ -114,21 +114,21 @@ func populateBatch(t *testing.T, batch Batch, input string) { } k := []byte{input[i]} ts := hlc.Timestamp{WallTime: int64(input[i+1])} - var v []byte + var v MVCCValue if i+1 < len(input) && input[i+1] == 'M' { ts = hlc.Timestamp{} - v = nil } else if i+2 < len(input) && input[i+2] == 'X' { - v = nil i++ } else { - v = []byte{input[i+1]} + v.Value.SetString(string(input[i+1])) } i += 2 if ts.IsEmpty() { - require.NoError(t, batch.PutUnversioned(k, v)) + vRaw, err := EncodeMVCCValue(v) + require.NoError(t, err) + require.NoError(t, batch.PutUnversioned(k, vRaw)) } else { - require.NoError(t, batch.PutRawMVCC(MVCCKey{Key: k, Timestamp: ts}, v)) + require.NoError(t, batch.PutMVCC(MVCCKey{Key: k, Timestamp: ts}, v)) } } } @@ -154,7 +154,9 @@ func iterateSimpleMultiIter(t *testing.T, it SimpleMVCCIterator, subtest iterSub output.WriteRune('M') } else { output.WriteByte(byte(it.UnsafeKey().Timestamp.WallTime)) - if len(it.UnsafeValue()) == 0 { + v, err := DecodeMVCCValue(it.UnsafeValue()) + require.NoError(t, err) + if v.IsTombstone() { output.WriteRune('X') } } diff --git a/pkg/storage/read_as_of_iterator.go b/pkg/storage/read_as_of_iterator.go index 85ae7756743c..80c497638310 100644 --- a/pkg/storage/read_as_of_iterator.go +++ b/pkg/storage/read_as_of_iterator.go @@ -31,6 +31,10 @@ type ReadAsOfIterator struct { // err tracks if iterating to the current key returned an error err error + + // newestRangeTombstone contains the timestamp of the latest range + // tombstone below asOf at the current position, if any. + newestRangeTombstone hlc.Timestamp } var _ SimpleMVCCIterator = &ReadAsOfIterator{} @@ -51,7 +55,7 @@ func (f *ReadAsOfIterator) SeekGE(originalKey MVCCKey) { synthetic := MVCCKey{Key: originalKey.Key, Timestamp: f.asOf} f.iter.SeekGE(synthetic) - if f.advance(); f.valid && f.UnsafeKey().Less(originalKey) { + if f.advance(true /* seeked */); f.valid && f.UnsafeKey().Less(originalKey) { // The following is true: // originalKey.Key == f.UnsafeKey && // f.asOf timestamp (if set) >= current timestamp > originalKey timestamp. @@ -83,7 +87,7 @@ func (f *ReadAsOfIterator) Next() { // call before any calls to NextKey(). func (f *ReadAsOfIterator) NextKey() { f.iter.NextKey() - f.advance() + f.advance(false /* seeked */) } // UnsafeKey returns the current key, but the memory is invalidated on the next @@ -129,43 +133,40 @@ func (f *ReadAsOfIterator) updateValid() bool { // advance moves past keys with timestamps later than f.asOf and skips MVCC keys // whose latest value (subject to f.asOF) has been deleted by a point or range // tombstone. -func (f *ReadAsOfIterator) advance() { +func (f *ReadAsOfIterator) advance(seeked bool) { for { if ok := f.updateValid(); !ok { return } - key := f.iter.UnsafeKey() - - if f.asOf.Less(key.Timestamp) { - // Skip keys above the asOf timestamp, regardless of type of key (e.g. point or range) - f.iter.Next() - } else if hasPoint, hasRange := f.iter.HasPointAndRange(); !hasPoint && hasRange { - // Bare range keys get surfaced before the point key, even though the - // point key shadows it; thus, because we can infer range key information - // when the point key surfaces, skip over the bare range key, and reason - // about shadowed keys at the surfaced point key. - // - // E.g. Scanning the keys below: - // 2 a2 - // 1 o---o - // a b - // - // would result in two surfaced keys: - // {a-b}@1; - // a2, {a-b}@1 + // Detect range tombstones, and step forward to the next key if on a bare + // range key. + if seeked || f.iter.RangeKeyChanged() { + seeked = false + hasPoint, hasRange := f.iter.HasPointAndRange() + f.newestRangeTombstone = hlc.Timestamp{} + if hasRange { + if v, ok := f.iter.RangeKeys().FirstBelow(f.asOf); ok { + f.newestRangeTombstone = v.Timestamp + } + } + if !hasPoint { + f.iter.Next() + continue + } + } + // Process point keys. + if key := f.iter.UnsafeKey(); f.asOf.Less(key.Timestamp) { + // Skip keys above the asOf timestamp. f.iter.Next() - } else if len(f.iter.UnsafeValue()) == 0 { + } else if value, err := DecodeMVCCValue(f.iter.UnsafeValue()); err != nil { + f.valid, f.err = false, err + return + } else if value.IsTombstone() { // Skip to the next MVCC key if we find a point tombstone. f.iter.NextKey() - } else if !hasRange { - // On a valid key without a range key - return - // TODO (msbutler): ensure this caches range key values (#84379) before - // the 22.2 branch cut, else we face a steep perf cliff for RESTORE with - // range keys. - } else if f.iter.RangeKeys().HasBetween(key.Timestamp, f.asOf) { + } else if key.Timestamp.LessEq(f.newestRangeTombstone) { // The latest range key, as of system time, shadows the latest point key. // This key is therefore deleted as of system time. f.iter.NextKey() diff --git a/pkg/storage/sst_iterator_test.go b/pkg/storage/sst_iterator_test.go index 06163f71e24d..ce201fef8e9f 100644 --- a/pkg/storage/sst_iterator_test.go +++ b/pkg/storage/sst_iterator_test.go @@ -23,6 +23,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/pebble/vfs" + "github.com/stretchr/testify/require" ) func runTestSSTIterator(t *testing.T, iter SimpleMVCCIterator, allKVs []MVCCKeyValue) { @@ -85,12 +86,17 @@ func TestSSTIterator(t *testing.T) { var allKVs []MVCCKeyValue maxWallTime := 10 for i := 0; i < maxWallTime; i++ { + var v MVCCValue + v.Value.SetBytes([]byte{'a', byte(i)}) + vRaw, err := EncodeMVCCValue(v) + require.NoError(t, err) + kv := MVCCKeyValue{ Key: MVCCKey{ Key: []byte{'A' + byte(i)}, Timestamp: hlc.Timestamp{WallTime: int64(i)}, }, - Value: []byte{'a' + byte(i)}, + Value: vRaw, } if err := sst.Put(kv.Key, kv.Value); err != nil { t.Fatalf("%+v", err) From 9061c76557e5195afe12604c61d768c96e92e3b8 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Sun, 21 Aug 2022 15:43:29 +0000 Subject: [PATCH 11/11] storage: omit unnecessary range key calls during intent resolution Release justification: bug fixes and low-risk updates to new functionality Release note: None --- pkg/storage/mvcc.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/storage/mvcc.go b/pkg/storage/mvcc.go index 66fc8c4e1108..9abfa936c5ca 100644 --- a/pkg/storage/mvcc.go +++ b/pkg/storage/mvcc.go @@ -4450,17 +4450,19 @@ func mvccResolveWriteIntent( // The latestKey was not the smallest possible timestamp {WallTime: 0, // Logical: 1}. Practically, this is the only case that will occur in // production. + var hasPoint, hasRange bool iter.SeekGE(nextKey) if ok, err = iter.Valid(); err != nil { return false, err } else if ok { // If the seek lands on a bare range key, attempt to step to a point. - if hasPoint, hasRange := iter.HasPointAndRange(); hasRange && !hasPoint { + if hasPoint, hasRange = iter.HasPointAndRange(); hasRange && !hasPoint { iter.Next() if ok, err = iter.Valid(); err != nil { return false, err } else if ok { - ok, _ = iter.HasPointAndRange() + hasPoint, hasRange = iter.HasPointAndRange() + ok = hasPoint } } } @@ -4475,7 +4477,7 @@ func mvccResolveWriteIntent( // If a non-tombstone point key is covered by a range tombstone, then // synthesize a point tombstone at the lowest range tombstone covering it. // This is where the point key ceases to exist, contributing to GCBytesAge. - if len(unsafeNextValueRaw) > 0 { + if len(unsafeNextValueRaw) > 0 && hasRange { if v, found := iter.RangeKeys().FirstAbove(unsafeNextKey.Timestamp); found { unsafeNextKey.Timestamp = v.Timestamp unsafeNextValueRaw = []byte{}