From 8c9df8a7fb64f8934f0e3f0743297911bc374abd Mon Sep 17 00:00:00 2001 From: Michael Butler Date: Fri, 1 Jul 2022 09:28:47 -0400 Subject: [PATCH] storage: add NextKeyIgnoringTime() to MVCCIncrementalIterator The MVCCInvermentalIterator's NextKeyIgnoringTime() function was previously deleted in #82691, as there wasn't any use for it at the time. Now, the new Delete Range with predicate filtering will need it (#83676). This PR also cleans up duplicate code used to test NextIgnoringTime and NextKeyIgnoringTime. Release note: None --- pkg/storage/mvcc_history_test.go | 28 ++- pkg/storage/mvcc_incremental_iterator.go | 45 ++-- pkg/storage/mvcc_incremental_iterator_test.go | 202 +++++++++++++++--- .../mvcc_histories/range_key_iter_incremental | 57 +++++ 4 files changed, 284 insertions(+), 48 deletions(-) diff --git a/pkg/storage/mvcc_history_test.go b/pkg/storage/mvcc_history_test.go index 0a80b5f249e8..15e52ec37beb 100644 --- a/pkg/storage/mvcc_history_test.go +++ b/pkg/storage/mvcc_history_test.go @@ -89,6 +89,7 @@ var sstIterVerify = util.ConstantWithMetamorphicTestBool("mvcc-histories-sst-ite // iter_seek_intent_ge k= txn= // iter_next // iter_next_ignoring_time +// iter_next_key_ignoring_time // iter_next_key // iter_prev // iter_scan [reverse] @@ -667,16 +668,17 @@ var commands = map[string]cmd{ "put_rangekey": {typDataUpdate, cmdPutRangeKey}, "scan": {typReadOnly, cmdScan}, - "iter_new": {typReadOnly, cmdIterNew}, - "iter_new_incremental": {typReadOnly, cmdIterNewIncremental}, // MVCCIncrementalIterator - "iter_seek_ge": {typReadOnly, cmdIterSeekGE}, - "iter_seek_lt": {typReadOnly, cmdIterSeekLT}, - "iter_seek_intent_ge": {typReadOnly, cmdIterSeekIntentGE}, - "iter_next": {typReadOnly, cmdIterNext}, - "iter_next_ignoring_time": {typReadOnly, cmdIterNextIgnoringTime}, // MVCCIncrementalIterator - "iter_next_key": {typReadOnly, cmdIterNextKey}, - "iter_prev": {typReadOnly, cmdIterPrev}, - "iter_scan": {typReadOnly, cmdIterScan}, + "iter_new": {typReadOnly, cmdIterNew}, + "iter_new_incremental": {typReadOnly, cmdIterNewIncremental}, // MVCCIncrementalIterator + "iter_seek_ge": {typReadOnly, cmdIterSeekGE}, + "iter_seek_lt": {typReadOnly, cmdIterSeekLT}, + "iter_seek_intent_ge": {typReadOnly, cmdIterSeekIntentGE}, + "iter_next": {typReadOnly, cmdIterNext}, + "iter_next_ignoring_time": {typReadOnly, cmdIterNextIgnoringTime}, // MVCCIncrementalIterator + "iter_next_key_ignoring_time": {typReadOnly, cmdIterNextKeyIgnoringTime}, // MVCCIncrementalIterator + "iter_next_key": {typReadOnly, cmdIterNextKey}, + "iter_prev": {typReadOnly, cmdIterPrev}, + "iter_scan": {typReadOnly, cmdIterScan}, "sst_put": {typDataUpdate, cmdSSTPut}, "sst_put_rangekey": {typDataUpdate, cmdSSTPutRangeKey}, @@ -1432,6 +1434,12 @@ func cmdIterNextIgnoringTime(e *evalCtx) error { return nil } +func cmdIterNextKeyIgnoringTime(e *evalCtx) error { + e.mvccIncrementalIter().NextKeyIgnoringTime() + printIter(e) + return nil +} + func cmdIterNextKey(e *evalCtx) error { e.iter.NextKey() printIter(e) diff --git a/pkg/storage/mvcc_incremental_iterator.go b/pkg/storage/mvcc_incremental_iterator.go index 2c28f9983bec..5310d0839d79 100644 --- a/pkg/storage/mvcc_incremental_iterator.go +++ b/pkg/storage/mvcc_incremental_iterator.go @@ -137,13 +137,7 @@ const ( // keep collecting entries and intents or skip entries. MVCCIncrementalIterIntentPolicyAggregate // MVCCIncrementalIterIntentPolicyEmit will return intents to - // the caller if they are inside the time range. Intents - // outside of the time range will be filtered without error. - // - // TODO(ssd): If we relaxed the requirement that intents are - // filtered by time range, we could avoid parsing intents - // inside the iterator and leave it to the caller to deal - // with. + // the caller if they are inside or outside the time range. MVCCIncrementalIterIntentPolicyEmit ) @@ -599,14 +593,11 @@ func (i *MVCCIncrementalIterator) UnsafeValue() []byte { return i.iter.UnsafeValue() } -// NextIgnoringTime returns the next key/value that would be encountered in a -// non-incremental iteration by moving the underlying non-TBI iterator forward. -// Intents in the time range (startTime,EndTime] are handled according to the -// iterator policy. -func (i *MVCCIncrementalIterator) NextIgnoringTime() { +// updateIgnoreTime updates the iterator's metadata and handles intents depending on the iterator's +// intent policy. +func (i *MVCCIncrementalIterator) updateIgnoreTime() { i.ignoringTime = true for { - i.iter.Next() if !i.updateValid() { return } @@ -630,8 +621,16 @@ func (i *MVCCIncrementalIterator) NextIgnoringTime() { // We have encountered an intent but it does not lie in the timestamp span // (startTime, endTime] so we do not throw an error, and attempt to move to - // the next valid KV. + // the intent's corresponding provisional value. + // + // Note: it's important to surface the intent's provisional value as callers rely on observing + // any value -- provisional, or not -- to make decisions. MVCClearTimeRange, for example, + // flushes keys for deletion whenever it encounters a key outside (StartTime,EndTime]. + // + // TODO(msbulter): investigate if it's clearer for the caller to emit the intent in + // addition to the provisional value. if i.meta.Txn != nil && i.intentPolicy != MVCCIncrementalIterIntentPolicyEmit { + i.iter.Next() continue } @@ -640,6 +639,24 @@ func (i *MVCCIncrementalIterator) NextIgnoringTime() { } } +// NextIgnoringTime returns the next key/value that would be encountered 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. +func (i *MVCCIncrementalIterator) NextIgnoringTime() { + i.iter.Next() + i.updateIgnoreTime() +} + +// NextKeyIgnoringTime returns the next distinct key that would be encountered +// 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. +func (i *MVCCIncrementalIterator) NextKeyIgnoringTime() { + i.iter.NextKey() + i.updateIgnoreTime() +} + // NumCollectedIntents returns number of intents encountered during iteration. // This is only the case when intent aggregation is enabled, otherwise it is // always 0. diff --git a/pkg/storage/mvcc_incremental_iterator_test.go b/pkg/storage/mvcc_incremental_iterator_test.go index 0d8903492c1e..1c38e433bcc8 100644 --- a/pkg/storage/mvcc_incremental_iterator_test.go +++ b/pkg/storage/mvcc_incremental_iterator_test.go @@ -263,20 +263,27 @@ func assertExportedKVs( require.False(t, ok) } -func nextIgnoreTimeExpectErr( +func ignoreTimeExpectErr( t *testing.T, e Engine, startKey, endKey roachpb.Key, startTime, endTime hlc.Timestamp, errString string, + nextKey bool, ) { iter := NewMVCCIncrementalIterator(e, MVCCIncrementalIterOptions{ EndKey: endKey, StartTime: startTime, EndTime: endTime, }) + var next func() + if nextKey { + next = iter.NextKeyIgnoringTime + } else { + next = iter.NextIgnoringTime + } defer iter.Close() - for iter.SeekGE(MakeMVCCMetadataKey(startKey)); ; iter.NextIgnoringTime() { + for iter.SeekGE(MakeMVCCMetadataKey(startKey)); ; next() { if ok, _ := iter.Valid(); !ok || iter.UnsafeKey().Key.Compare(endKey) >= 0 { break } @@ -287,12 +294,13 @@ func nextIgnoreTimeExpectErr( } } -func assertNextIgnoreTimeIteratedKVs( +func assertIgnoreTimeIteratedKVs( t *testing.T, e Engine, startKey, endKey roachpb.Key, startTime, endTime hlc.Timestamp, expected []MVCCKeyValue, + nextKey bool, ) { iter := NewMVCCIncrementalIterator(e, MVCCIncrementalIterOptions{ EndKey: endKey, @@ -300,8 +308,15 @@ func assertNextIgnoreTimeIteratedKVs( EndTime: endTime, }) defer iter.Close() + next := func() { + if nextKey { + iter.NextKeyIgnoringTime() + } else { + iter.NextIgnoringTime() + } + } var kvs []MVCCKeyValue - for iter.SeekGE(MakeMVCCMetadataKey(startKey)); ; iter.NextIgnoringTime() { + for iter.SeekGE(MakeMVCCMetadataKey(startKey)); ; next() { if ok, err := iter.Valid(); err != nil { t.Fatalf("unexpected error: %+v", err) } else if !ok || iter.UnsafeKey().Key.Compare(endKey) >= 0 { @@ -432,7 +447,7 @@ func TestMVCCIncrementalIteratorNextIgnoringTime(t *testing.T) { defer e.Close() t.Run("empty", func(t *testing.T) { - assertNextIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, ts3, nil) + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, ts3, nil, false) }) for _, kv := range kvs(kv1_1_1, kv1_2_2, kv2_2_2) { @@ -444,34 +459,172 @@ func TestMVCCIncrementalIteratorNextIgnoringTime(t *testing.T) { // Exercise time ranges. t.Run("ts (0-0]", func(t *testing.T) { - assertNextIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, tsMin, nil) + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, tsMin, nil, false) }) // Returns the kv_2_2_2 even though it is outside (startTime, endTime]. t.Run("ts (0-1]", func(t *testing.T) { - assertNextIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, ts1, kvs(kv1_1_1, kv2_2_2)) + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, ts1, kvs(kv1_1_1, kv2_2_2), false) }) t.Run("ts (0-∞]", func(t *testing.T) { - assertNextIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, tsMax, kvs(kv1_2_2, kv1_1_1, - kv2_2_2)) + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, tsMax, kvs(kv1_2_2, kv1_1_1, + kv2_2_2), false) }) t.Run("ts (1-1]", func(t *testing.T) { - assertNextIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts1, ts1, nil) + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts1, ts1, nil, false) + }) + // Returns the kv_1_1_1 even though it is outside (startTime, endTime]. + t.Run("ts (1-2]", func(t *testing.T) { + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts1, ts2, kvs(kv1_2_2, kv1_1_1, + kv2_2_2), false) + }) + t.Run("ts (2-2]", func(t *testing.T) { + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts2, ts2, nil, false) + }) + + // Exercise key ranges. + t.Run("kv [1-1)", func(t *testing.T) { + assertIgnoreTimeIteratedKVs(t, e, testKey1, testKey1, tsMin, tsMax, nil, false) + }) + t.Run("kv [1-2)", func(t *testing.T) { + assertIgnoreTimeIteratedKVs(t, e, testKey1, testKey2, tsMin, tsMax, kvs(kv1_2_2, + kv1_1_1), false) }) + + // Exercise deletion. + if err := MVCCDelete(ctx, e, nil, testKey1, ts3, hlc.ClockTimestamp{}, nil); err != nil { + t.Fatal(err) + } // Returns the kv_1_1_1 even though it is outside (startTime, endTime]. + t.Run("del", func(t *testing.T) { + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts1, tsMax, kvs(kv1_3Deleted, + kv1_2_2, kv1_1_1, kv2_2_2), false) + }) + + // Insert an intent of testKey2. + txn1ID := uuid.MakeV4() + txn1 := roachpb.Transaction{ + TxnMeta: enginepb.TxnMeta{ + Key: testKey2, + ID: txn1ID, + Epoch: 1, + WriteTimestamp: ts4, + }, + ReadTimestamp: ts4, + } + if err := MVCCPut(ctx, e, nil, txn1.TxnMeta.Key, txn1.ReadTimestamp, hlc.ClockTimestamp{}, testValue4, &txn1); err != nil { + t.Fatal(err) + } + + // We have to be careful that we are testing the intent handling logic of + // NextIgnoreTime() rather than the first SeekGE(). We do this by + // ensuring that the SeekGE() doesn't encounter an intent. + t.Run("intents", func(t *testing.T) { + ignoreTimeExpectErr(t, e, testKey1, testKey2.PrefixEnd(), tsMin, tsMax, + "conflicting intents", false) + }) + t.Run("intents", func(t *testing.T) { + ignoreTimeExpectErr(t, e, localMax, keyMax, tsMin, ts4, "conflicting intents", false) + }) + // Intents above the upper time bound or beneath the lower time bound must + // be ignored. Note that the lower time bound is exclusive while the upper + // time bound is inclusive. + // + // The intent at ts=4 for kv2 lies outside the timespan + // (startTime, endTime] so we do not raise an error and just move on to + // its versioned KV, a provisional value. + t.Run("intents", func(t *testing.T) { + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, ts3, kvs(kv1_3Deleted, + kv1_2_2, kv1_1_1, kv2_4_4, kv2_2_2), false) + }) + t.Run("intents", func(t *testing.T) { + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts4, tsMax, kvs(), false) + }) + t.Run("intents", func(t *testing.T) { + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts4.Next(), tsMax, kvs(), false) + }) + }) + } +} + +// TestMVCCIncrementalIteratorNextKeyIgnoringTime tests the iteration semantics +// of the method NextKeyIgnoreTime(). This method is supposed to return all new +// keys that would be encountered in a non-incremental iteration. +func TestMVCCIncrementalIteratorNextKeyIgnoringTime(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + DisableMetamorphicSimpleValueEncoding(t) + ctx := context.Background() + + var ( + keyMax = roachpb.KeyMax + testKey1 = roachpb.Key("/db1") + testKey2 = roachpb.Key("/db2") + + testValue1 = roachpb.MakeValueFromString("val1") + testValue2 = roachpb.MakeValueFromString("val2") + testValue3 = roachpb.MakeValueFromString("val3") + testValue4 = roachpb.MakeValueFromString("val4") + + // Use a non-zero min, since we use IsEmpty to decide if a ts should be used + // as upper/lower-bound during iterator initialization. + tsMin = hlc.Timestamp{WallTime: 0, Logical: 1} + ts1 = hlc.Timestamp{WallTime: 1, Logical: 0} + ts2 = hlc.Timestamp{WallTime: 2, Logical: 0} + ts3 = hlc.Timestamp{WallTime: 3, Logical: 0} + ts4 = hlc.Timestamp{WallTime: 4, Logical: 0} + tsMax = hlc.Timestamp{WallTime: math.MaxInt64, Logical: 0} + ) + + kv1_1_1 := makeKVT(testKey1, testValue1, ts1) + kv1_2_2 := makeKVT(testKey1, testValue2, ts2) + kv2_2_2 := makeKVT(testKey2, testValue3, ts2) + kv2_4_4 := makeKVT(testKey2, testValue4, ts4) + kv1_3Deleted := makeKVT(testKey1, roachpb.Value{}, ts3) + + for _, engineImpl := range mvccEngineImpls { + t.Run(engineImpl.name, func(t *testing.T) { + e := engineImpl.create() + defer e.Close() + + t.Run("empty", func(t *testing.T) { + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, ts3, nil, true) + }) + + for _, kv := range kvs(kv1_1_1, kv1_2_2, kv2_2_2) { + v := roachpb.Value{RawBytes: kv.Value} + if err := MVCCPut(ctx, e, nil, kv.Key.Key, kv.Key.Timestamp, hlc.ClockTimestamp{}, v, nil); err != nil { + t.Fatal(err) + } + } + + // Exercise time ranges. + t.Run("ts (0-0]", func(t *testing.T) { + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, tsMin, nil, true) + }) + // Returns the kv_2_2_2 even though it is outside (startTime, endTime]. + t.Run("ts (0-1]", func(t *testing.T) { + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, ts1, kvs(kv1_1_1, kv2_2_2), true) + }) + t.Run("ts (0-∞]", func(t *testing.T) { + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, tsMax, kvs(kv1_2_2, kv2_2_2), + true) + }) + t.Run("ts (1-1]", func(t *testing.T) { + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts1, ts1, nil, true) + }) t.Run("ts (1-2]", func(t *testing.T) { - assertNextIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts1, ts2, kvs(kv1_2_2, kv1_1_1, - kv2_2_2)) + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts1, ts2, kvs(kv1_2_2, kv2_2_2), true) }) t.Run("ts (2-2]", func(t *testing.T) { - assertNextIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts2, ts2, nil) + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts2, ts2, nil, true) }) // Exercise key ranges. t.Run("kv [1-1)", func(t *testing.T) { - assertNextIgnoreTimeIteratedKVs(t, e, testKey1, testKey1, tsMin, tsMax, nil) + assertIgnoreTimeIteratedKVs(t, e, testKey1, testKey1, tsMin, tsMax, nil, true) }) t.Run("kv [1-2)", func(t *testing.T) { - assertNextIgnoreTimeIteratedKVs(t, e, testKey1, testKey2, tsMin, tsMax, kvs(kv1_2_2, kv1_1_1)) + assertIgnoreTimeIteratedKVs(t, e, testKey1, testKey2, tsMin, tsMax, kvs(kv1_2_2), true) }) // Exercise deletion. @@ -480,8 +633,8 @@ func TestMVCCIncrementalIteratorNextIgnoringTime(t *testing.T) { } // Returns the kv_1_1_1 even though it is outside (startTime, endTime]. t.Run("del", func(t *testing.T) { - assertNextIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts1, tsMax, kvs(kv1_3Deleted, - kv1_2_2, kv1_1_1, kv2_2_2)) + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts1, tsMax, kvs(kv1_3Deleted, + kv2_2_2), true) }) // Insert an intent of testKey2. @@ -503,10 +656,11 @@ func TestMVCCIncrementalIteratorNextIgnoringTime(t *testing.T) { // NextIgnoreTime() rather than the first SeekGE(). We do this by // ensuring that the SeekGE() doesn't encounter an intent. t.Run("intents", func(t *testing.T) { - nextIgnoreTimeExpectErr(t, e, testKey1, testKey2.PrefixEnd(), tsMin, tsMax, "conflicting intents") + ignoreTimeExpectErr(t, e, testKey1, testKey2.PrefixEnd(), tsMin, tsMax, + "conflicting intents", true) }) t.Run("intents", func(t *testing.T) { - nextIgnoreTimeExpectErr(t, e, localMax, keyMax, tsMin, ts4, "conflicting intents") + ignoreTimeExpectErr(t, e, localMax, keyMax, tsMin, ts4, "conflicting intents", true) }) // Intents above the upper time bound or beneath the lower time bound must // be ignored. Note that the lower time bound is exclusive while the upper @@ -514,16 +668,16 @@ func TestMVCCIncrementalIteratorNextIgnoringTime(t *testing.T) { // // The intent at ts=4 for kv2 lies outside the timespan // (startTime, endTime] so we do not raise an error and just move on to - // its versioned KV. + // its versioned KV, a provisional value. t.Run("intents", func(t *testing.T) { - assertNextIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, ts3, kvs(kv1_3Deleted, - kv1_2_2, kv1_1_1, kv2_4_4, kv2_2_2)) + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, tsMin, ts3, kvs(kv1_3Deleted, + kv2_4_4), true) }) t.Run("intents", func(t *testing.T) { - assertNextIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts4, tsMax, kvs()) + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts4, tsMax, kvs(), true) }) t.Run("intents", func(t *testing.T) { - assertNextIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts4.Next(), tsMax, kvs()) + assertIgnoreTimeIteratedKVs(t, e, localMax, keyMax, ts4.Next(), tsMax, kvs(), true) }) }) } diff --git a/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental b/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental index 857a52a2d54c..ae88c7509b7f 100644 --- a/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental +++ b/pkg/storage/testdata/mvcc_histories/range_key_iter_incremental @@ -1011,6 +1011,23 @@ 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" +run ok +iter_new_incremental types=pointsAndRanges k=a end=z startTs=2 endTs=4 intents=error +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=/] + +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_next_key_ignoring_time: err=conflicting intents on "d" +error: (*roachpb.WriteIntentError:) conflicting intents on "d" + # rangesOnly doesn't care about intents. run ok iter_new_incremental types=rangesOnly k=a end=z intents=error @@ -1124,6 +1141,46 @@ iter_next_ignoring_time iter_seek_ge: {f-g}/[5.000000000,0=/] iter_next_ignoring_time: {g-h}/[3.000000000,0=/ 1.000000000,0=/] +# Test NextKeyIgnoringTime(). +run ok +iter_new_incremental types=pointsAndRanges k=a end=z startTs=2 endTs=4 intents=emit +iter_seek_ge k=a +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_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_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: "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 + +# NextKeyIgnoringTime with only range keys. +run ok +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=/] + # Try some scans, bounded and unbounded, with only point keys. run ok iter_new_incremental types=pointsOnly k=a end=z endTs=8 intents=emit