github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/batch_spanset_test.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package kvserver
    12  
    13  import (
    14  	"bytes"
    15  	"context"
    16  	"reflect"
    17  	"testing"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/spanset"
    20  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    21  	"github.com/cockroachdb/cockroach/pkg/storage"
    22  	"github.com/cockroachdb/cockroach/pkg/storage/enginepb"
    23  	"github.com/cockroachdb/cockroach/pkg/testutils"
    24  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    25  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    26  	"github.com/cockroachdb/errors"
    27  )
    28  
    29  func TestSpanSetBatchBoundaries(t *testing.T) {
    30  	defer leaktest.AfterTest(t)()
    31  	eng := storage.NewDefaultInMem()
    32  	defer eng.Close()
    33  
    34  	var ss spanset.SpanSet
    35  	ss.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{Key: roachpb.Key("c"), EndKey: roachpb.Key("g")})
    36  	outsideKey := storage.MakeMVCCMetadataKey(roachpb.Key("a"))
    37  	outsideKey2 := storage.MakeMVCCMetadataKey(roachpb.Key("b"))
    38  	outsideKey3 := storage.MakeMVCCMetadataKey(roachpb.Key("g"))
    39  	outsideKey4 := storage.MakeMVCCMetadataKey(roachpb.Key("m"))
    40  	insideKey := storage.MakeMVCCMetadataKey(roachpb.Key("c"))
    41  	insideKey2 := storage.MakeMVCCMetadataKey(roachpb.Key("d"))
    42  	insideKey3 := storage.MakeMVCCMetadataKey(roachpb.Key("f"))
    43  
    44  	// Write values outside the range that we can try to read later.
    45  	if err := eng.Put(outsideKey, []byte("value")); err != nil {
    46  		t.Fatalf("direct write failed: %+v", err)
    47  	}
    48  	if err := eng.Put(outsideKey3, []byte("value")); err != nil {
    49  		t.Fatalf("direct write failed: %+v", err)
    50  	}
    51  
    52  	batch := spanset.NewBatch(eng.NewBatch(), &ss)
    53  	defer batch.Close()
    54  
    55  	// Writes inside the range work. Write twice for later read testing.
    56  	if err := batch.Put(insideKey, []byte("value")); err != nil {
    57  		t.Fatalf("failed to write inside the range: %+v", err)
    58  	}
    59  	if err := batch.Put(insideKey2, []byte("value2")); err != nil {
    60  		t.Fatalf("failed to write inside the range: %+v", err)
    61  	}
    62  
    63  	// Writes outside the range fail. We try to cover all write methods
    64  	// in the failure case to make sure the CheckAllowed call is
    65  	// present, but we don't attempt successful versions of all
    66  	// methods since those are harder to set up.
    67  	isWriteSpanErr := func(err error) bool {
    68  		return testutils.IsError(err, "cannot write undeclared span")
    69  	}
    70  
    71  	t.Run("writes before range", func(t *testing.T) {
    72  		if err := batch.Clear(outsideKey); !isWriteSpanErr(err) {
    73  			t.Errorf("Clear: unexpected error %v", err)
    74  		}
    75  		if err := batch.ClearRange(outsideKey, outsideKey2); !isWriteSpanErr(err) {
    76  			t.Errorf("ClearRange: unexpected error %v", err)
    77  		}
    78  		{
    79  			iter := batch.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax})
    80  			err := batch.ClearIterRange(iter, outsideKey.Key, outsideKey2.Key)
    81  			iter.Close()
    82  			if !isWriteSpanErr(err) {
    83  				t.Errorf("ClearIterRange: unexpected error %v", err)
    84  			}
    85  		}
    86  		if err := batch.Merge(outsideKey, nil); !isWriteSpanErr(err) {
    87  			t.Errorf("Merge: unexpected error %v", err)
    88  		}
    89  		if err := batch.Put(outsideKey, nil); !isWriteSpanErr(err) {
    90  			t.Errorf("Put: unexpected error %v", err)
    91  		}
    92  	})
    93  
    94  	t.Run("writes after range", func(t *testing.T) {
    95  		if err := batch.Clear(outsideKey3); !isWriteSpanErr(err) {
    96  			t.Errorf("Clear: unexpected error %v", err)
    97  		}
    98  		if err := batch.ClearRange(insideKey2, outsideKey4); !isWriteSpanErr(err) {
    99  			t.Errorf("ClearRange: unexpected error %v", err)
   100  		}
   101  		{
   102  			iter := batch.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax})
   103  			err := batch.ClearIterRange(iter, insideKey2.Key, outsideKey4.Key)
   104  			iter.Close()
   105  			if !isWriteSpanErr(err) {
   106  				t.Errorf("ClearIterRange: unexpected error %v", err)
   107  			}
   108  		}
   109  		if err := batch.Merge(outsideKey3, nil); !isWriteSpanErr(err) {
   110  			t.Errorf("Merge: unexpected error %v", err)
   111  		}
   112  		if err := batch.Put(outsideKey3, nil); !isWriteSpanErr(err) {
   113  			t.Errorf("Put: unexpected error %v", err)
   114  		}
   115  	})
   116  
   117  	t.Run("reads inside range", func(t *testing.T) {
   118  		//lint:ignore SA1019 historical usage of deprecated batch.Get is OK
   119  		if value, err := batch.Get(insideKey); err != nil {
   120  			t.Errorf("failed to read inside the range: %+v", err)
   121  		} else if !bytes.Equal(value, []byte("value")) {
   122  			t.Errorf("failed to read previously written value, got %q", value)
   123  		}
   124  		//lint:ignore SA1019 historical usage of deprecated batch.GetProto is OK
   125  		if _, _, _, err := batch.GetProto(insideKey, nil); err != nil {
   126  			t.Errorf("GetProto: unexpected error %v", err)
   127  		}
   128  		if err := batch.Iterate(insideKey.Key, insideKey2.Key,
   129  			func(v storage.MVCCKeyValue) (bool, error) {
   130  				return false, nil
   131  			},
   132  		); err != nil {
   133  			t.Errorf("Iterate: unexpected error %v", err)
   134  		}
   135  	})
   136  
   137  	// Reads outside the range fail.
   138  	isReadSpanErr := func(err error) bool {
   139  		return testutils.IsError(err, "cannot read undeclared span")
   140  	}
   141  
   142  	t.Run("reads before range", func(t *testing.T) {
   143  		//lint:ignore SA1019 historical usage of deprecated batch.Get is OK
   144  		if _, err := batch.Get(outsideKey); !isReadSpanErr(err) {
   145  			t.Errorf("Get: unexpected error %v", err)
   146  		}
   147  		//lint:ignore SA1019 historical usage of deprecated batch.GetProto is OK
   148  		if _, _, _, err := batch.GetProto(outsideKey, nil); !isReadSpanErr(err) {
   149  			t.Errorf("GetProto: unexpected error %v", err)
   150  		}
   151  		if err := batch.Iterate(outsideKey.Key, insideKey2.Key,
   152  			func(v storage.MVCCKeyValue) (bool, error) {
   153  				return false, errors.Errorf("unexpected callback: %v", v)
   154  			},
   155  		); !isReadSpanErr(err) {
   156  			t.Errorf("Iterate: unexpected error %v", err)
   157  		}
   158  	})
   159  
   160  	t.Run("reads after range", func(t *testing.T) {
   161  		//lint:ignore SA1019 historical usage of deprecated batch.Get is OK
   162  		if _, err := batch.Get(outsideKey3); !isReadSpanErr(err) {
   163  			t.Errorf("Get: unexpected error %v", err)
   164  		}
   165  		//lint:ignore SA1019 historical usage of deprecated batch.GetProto is OK
   166  		if _, _, _, err := batch.GetProto(outsideKey3, nil); !isReadSpanErr(err) {
   167  			t.Errorf("GetProto: unexpected error %v", err)
   168  		}
   169  		if err := batch.Iterate(insideKey2.Key, outsideKey4.Key,
   170  			func(v storage.MVCCKeyValue) (bool, error) {
   171  				return false, errors.Errorf("unexpected callback: %v", v)
   172  			},
   173  		); !isReadSpanErr(err) {
   174  			t.Errorf("Iterate: unexpected error %v", err)
   175  		}
   176  	})
   177  
   178  	t.Run("forward scans", func(t *testing.T) {
   179  		iter := batch.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax})
   180  		defer iter.Close()
   181  
   182  		// Iterators check boundaries on seek and next/prev
   183  		iter.SeekGE(outsideKey)
   184  		if _, err := iter.Valid(); !isReadSpanErr(err) {
   185  			t.Fatalf("Seek: unexpected error %v", err)
   186  		}
   187  		iter.SeekGE(outsideKey3)
   188  		if _, err := iter.Valid(); !isReadSpanErr(err) {
   189  			t.Fatalf("Seek: unexpected error %v", err)
   190  		}
   191  		// Seeking back in bounds restores validity.
   192  		iter.SeekGE(insideKey)
   193  		if ok, err := iter.Valid(); !ok || err != nil {
   194  			t.Fatalf("expected valid iterator, err=%v", err)
   195  		}
   196  		if !reflect.DeepEqual(iter.Key(), insideKey) {
   197  			t.Fatalf("expected key %s, got %s", insideKey, iter.Key())
   198  		}
   199  		iter.Next()
   200  		if ok, err := iter.Valid(); !ok || err != nil {
   201  			t.Fatalf("expected valid iterator, err=%v", err)
   202  		}
   203  		if !reflect.DeepEqual(iter.Key(), insideKey2) {
   204  			t.Fatalf("expected key %s, got %s", insideKey2, iter.Key())
   205  		}
   206  		// Scan out of bounds.
   207  		iter.Next()
   208  		if ok, err := iter.Valid(); ok {
   209  			t.Fatalf("expected invalid iterator; found valid at key %s", iter.Key())
   210  		} else if err != nil {
   211  			// Scanning out of bounds sets Valid() to false but is not an error.
   212  			t.Errorf("unexpected error on iterator: %+v", err)
   213  		}
   214  	})
   215  
   216  	// Same test in reverse. We commit the batch and wrap an iterator on
   217  	// the raw engine because we don't support bidirectional iteration
   218  	// over a pending batch.
   219  	if err := batch.Commit(true); err != nil {
   220  		t.Fatal(err)
   221  	}
   222  
   223  	t.Run("reverse scans", func(t *testing.T) {
   224  		iter := spanset.NewIterator(eng.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax}), &ss)
   225  		defer iter.Close()
   226  		iter.SeekLT(outsideKey4)
   227  		if _, err := iter.Valid(); !isReadSpanErr(err) {
   228  			t.Fatalf("SeekLT: unexpected error %v", err)
   229  		}
   230  		// Seeking back in bounds restores validity.
   231  		iter.SeekLT(insideKey3)
   232  		if ok, err := iter.Valid(); !ok || err != nil {
   233  			t.Fatalf("expected valid iterator, err=%v", err)
   234  		}
   235  		if !reflect.DeepEqual(iter.Key(), insideKey2) {
   236  			t.Fatalf("expected key %s, got %s", insideKey2, iter.Key())
   237  		}
   238  		iter.Prev()
   239  		if ok, err := iter.Valid(); !ok || err != nil {
   240  			t.Fatalf("expected valid iterator, err=%v", err)
   241  		}
   242  		if !reflect.DeepEqual(iter.Key(), insideKey) {
   243  			t.Fatalf("expected key %s, got %s", insideKey, iter.Key())
   244  		}
   245  		// Scan out of bounds.
   246  		iter.Prev()
   247  		if ok, err := iter.Valid(); ok {
   248  			t.Fatalf("expected invalid iterator; found valid at key %s", iter.Key())
   249  		} else if err != nil {
   250  			t.Errorf("unexpected error on iterator: %+v", err)
   251  		}
   252  
   253  		// Seeking back in bounds restores validity.
   254  		iter.SeekLT(insideKey2)
   255  		if ok, err := iter.Valid(); !ok || err != nil {
   256  			t.Fatalf("expected valid iterator, err=%v", err)
   257  		}
   258  		// SeekLT to the lower bound is invalid.
   259  		iter.SeekLT(insideKey)
   260  		if ok, err := iter.Valid(); ok {
   261  			t.Fatalf("expected invalid iterator; found valid at key %s", iter.Key())
   262  		} else if !isReadSpanErr(err) {
   263  			t.Fatalf("SeekLT: unexpected error %v", err)
   264  		}
   265  		// SeekLT to upper bound restores validity.
   266  		iter.SeekLT(outsideKey3)
   267  		if ok, err := iter.Valid(); !ok || err != nil {
   268  			t.Fatalf("expected valid iterator, err=%v", err)
   269  		}
   270  	})
   271  }
   272  
   273  func TestSpanSetBatchTimestamps(t *testing.T) {
   274  	defer leaktest.AfterTest(t)()
   275  	eng := storage.NewDefaultInMem()
   276  	defer eng.Close()
   277  
   278  	var ss spanset.SpanSet
   279  	ss.AddMVCC(spanset.SpanReadOnly,
   280  		roachpb.Span{Key: roachpb.Key("a"), EndKey: roachpb.Key("c")}, hlc.Timestamp{WallTime: 2})
   281  	ss.AddMVCC(spanset.SpanReadWrite,
   282  		roachpb.Span{Key: roachpb.Key("d"), EndKey: roachpb.Key("f")}, hlc.Timestamp{WallTime: 2})
   283  
   284  	rkey := storage.MakeMVCCMetadataKey(roachpb.Key("b"))
   285  	wkey := storage.MakeMVCCMetadataKey(roachpb.Key("e"))
   286  
   287  	value := []byte("value")
   288  
   289  	// Write value that we can try to read later.
   290  	if err := eng.Put(rkey, value); err != nil {
   291  		t.Fatalf("direct write failed: %+v", err)
   292  	}
   293  
   294  	batchNonMVCC := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 0})
   295  	defer batchNonMVCC.Close()
   296  
   297  	batchBefore := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 1})
   298  	defer batchBefore.Close()
   299  
   300  	batchDuring := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 2})
   301  	defer batchDuring.Close()
   302  
   303  	batchAfter := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 3})
   304  	defer batchAfter.Close()
   305  
   306  	// Writes.
   307  	for _, batch := range []storage.Batch{batchAfter, batchDuring} {
   308  		if err := batch.Put(wkey, value); err != nil {
   309  			t.Fatalf("failed to write inside the range at same or greater ts than latch declaration: %+v", err)
   310  		}
   311  	}
   312  
   313  	for _, batch := range []storage.Batch{batchBefore, batchNonMVCC} {
   314  		if err := batch.Put(wkey, value); err == nil {
   315  			t.Fatalf("was able to write inside the range at ts less than latch declaration: %+v", err)
   316  		}
   317  	}
   318  
   319  	// We try to cover all write methods in the failure case to make sure
   320  	// the CheckAllowedAt call is present, but we don't attempt to successful
   321  	// versions of all methods since those are harder to set up.
   322  	isWriteSpanErr := func(err error) bool {
   323  		return testutils.IsError(err, "cannot write undeclared span")
   324  	}
   325  
   326  	for _, batch := range []storage.Batch{batchBefore, batchNonMVCC} {
   327  		if err := batch.Clear(wkey); !isWriteSpanErr(err) {
   328  			t.Errorf("Clear: unexpected error %v", err)
   329  		}
   330  		{
   331  			iter := batch.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax})
   332  			err := batch.ClearIterRange(iter, wkey.Key, wkey.Key)
   333  			iter.Close()
   334  			if !isWriteSpanErr(err) {
   335  				t.Errorf("ClearIterRange: unexpected error %v", err)
   336  			}
   337  		}
   338  		if err := batch.Merge(wkey, nil); !isWriteSpanErr(err) {
   339  			t.Errorf("Merge: unexpected error %v", err)
   340  		}
   341  		if err := batch.Put(wkey, nil); !isWriteSpanErr(err) {
   342  			t.Errorf("Put: unexpected error %v", err)
   343  		}
   344  	}
   345  
   346  	// Reads.
   347  	for _, batch := range []storage.Batch{batchBefore, batchDuring} {
   348  		//lint:ignore SA1019 historical usage of deprecated batch.Get is OK
   349  		if res, err := batch.Get(rkey); err != nil {
   350  			t.Errorf("failed to read inside the range: %+v", err)
   351  		} else if !bytes.Equal(res, value) {
   352  			t.Errorf("failed to read previously written value, got %q", res)
   353  		}
   354  	}
   355  
   356  	isReadSpanErr := func(err error) bool {
   357  		return testutils.IsError(err, "cannot read undeclared span")
   358  	}
   359  
   360  	for _, batch := range []storage.Batch{batchAfter, batchNonMVCC} {
   361  		//lint:ignore SA1019 historical usage of deprecated batch.Get is OK
   362  		if _, err := batch.Get(rkey); !isReadSpanErr(err) {
   363  			t.Errorf("Get: unexpected error %v", err)
   364  		}
   365  
   366  		//lint:ignore SA1019 historical usage of deprecated batch.GetProto is OK
   367  		if _, _, _, err := batch.GetProto(rkey, nil); !isReadSpanErr(err) {
   368  			t.Errorf("GetProto: unexpected error %v", err)
   369  		}
   370  		if err := batch.Iterate(rkey.Key, rkey.Key,
   371  			func(v storage.MVCCKeyValue) (bool, error) {
   372  				return false, errors.Errorf("unexpected callback: %v", v)
   373  			},
   374  		); !isReadSpanErr(err) {
   375  			t.Errorf("Iterate: unexpected error %v", err)
   376  		}
   377  	}
   378  }
   379  
   380  func TestSpanSetIteratorTimestamps(t *testing.T) {
   381  	defer leaktest.AfterTest(t)()
   382  	eng := storage.NewDefaultInMem()
   383  	defer eng.Close()
   384  
   385  	var ss spanset.SpanSet
   386  	ss.AddMVCC(spanset.SpanReadOnly, roachpb.Span{
   387  		Key: roachpb.Key("a"), EndKey: roachpb.Key("c")}, hlc.Timestamp{WallTime: 1})
   388  	ss.AddMVCC(spanset.SpanReadOnly, roachpb.Span{
   389  		Key: roachpb.Key("c"), EndKey: roachpb.Key("e")}, hlc.Timestamp{WallTime: 2})
   390  
   391  	k1, v1 := storage.MakeMVCCMetadataKey(roachpb.Key("b")), []byte("b-value")
   392  	k2, v2 := storage.MakeMVCCMetadataKey(roachpb.Key("d")), []byte("d-value")
   393  
   394  	// Write values that we can try to read later.
   395  	if err := eng.Put(k1, v1); err != nil {
   396  		t.Fatalf("direct write failed: %+v", err)
   397  	}
   398  	if err := eng.Put(k2, v2); err != nil {
   399  		t.Fatalf("direct write failed: %+v", err)
   400  	}
   401  
   402  	batchNonMVCC := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 0})
   403  	defer batchNonMVCC.Close()
   404  
   405  	batchAt1 := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 1})
   406  	defer batchAt1.Close()
   407  
   408  	batchAt2 := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 2})
   409  	defer batchAt2.Close()
   410  
   411  	batchAt3 := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 3})
   412  	defer batchAt3.Close()
   413  
   414  	func() {
   415  		// When accessing at t=1, we're able to read through latches declared at t=1 and t=2.
   416  		iter := batchAt1.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax})
   417  		defer iter.Close()
   418  
   419  		iter.SeekGE(k1)
   420  		if ok, err := iter.Valid(); !ok {
   421  			t.Fatalf("expected valid iterator, err=%v", err)
   422  		}
   423  		if !reflect.DeepEqual(iter.Key(), k1) {
   424  			t.Fatalf("expected key %s, got %s", k1, iter.Key())
   425  		}
   426  
   427  		iter.Next()
   428  		if ok, err := iter.Valid(); !ok {
   429  			t.Fatalf("expected valid iterator, err=%v", err)
   430  		}
   431  		if !reflect.DeepEqual(iter.Key(), k2) {
   432  			t.Fatalf("expected key %s, got %s", k2, iter.Key())
   433  		}
   434  	}()
   435  
   436  	{
   437  		// When accessing at t=2, we're only able to read through the latch declared at t=2.
   438  		iter := batchAt2.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax})
   439  		defer iter.Close()
   440  
   441  		iter.SeekGE(k1)
   442  		if ok, _ := iter.Valid(); ok {
   443  			t.Fatalf("expected invalid iterator; found valid at key %s", iter.Key())
   444  		}
   445  
   446  		iter.SeekGE(k2)
   447  		if ok, err := iter.Valid(); !ok {
   448  			t.Fatalf("expected valid iterator, err=%v", err)
   449  		}
   450  		if !reflect.DeepEqual(iter.Key(), k2) {
   451  			t.Fatalf("expected key %s, got %s", k2, iter.Key())
   452  		}
   453  	}
   454  
   455  	for _, batch := range []storage.Batch{batchAt3, batchNonMVCC} {
   456  		// When accessing at t=3, we're unable to read through any of the declared latches.
   457  		// Same is true when accessing without a timestamp.
   458  		iter := batch.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax})
   459  		defer iter.Close()
   460  
   461  		iter.SeekGE(k1)
   462  		if ok, _ := iter.Valid(); ok {
   463  			t.Fatalf("expected invalid iterator; found valid at key %s", iter.Key())
   464  		}
   465  
   466  		iter.SeekGE(k2)
   467  		if ok, _ := iter.Valid(); ok {
   468  			t.Fatalf("expected invalid iterator; found valid at key %s", iter.Key())
   469  		}
   470  	}
   471  }
   472  
   473  func TestSpanSetNonMVCCBatch(t *testing.T) {
   474  	defer leaktest.AfterTest(t)()
   475  	eng := storage.NewDefaultInMem()
   476  	defer eng.Close()
   477  
   478  	var ss spanset.SpanSet
   479  	ss.AddNonMVCC(spanset.SpanReadOnly, roachpb.Span{Key: roachpb.Key("a"), EndKey: roachpb.Key("c")})
   480  	ss.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{Key: roachpb.Key("d"), EndKey: roachpb.Key("f")})
   481  
   482  	rkey := storage.MakeMVCCMetadataKey(roachpb.Key("b"))
   483  	wkey := storage.MakeMVCCMetadataKey(roachpb.Key("e"))
   484  
   485  	value := []byte("value")
   486  
   487  	// Write value that we can try to read later.
   488  	if err := eng.Put(rkey, value); err != nil {
   489  		t.Fatalf("direct write failed: %+v", err)
   490  	}
   491  
   492  	batchNonMVCC := spanset.NewBatch(eng.NewBatch(), &ss)
   493  	defer batchNonMVCC.Close()
   494  
   495  	batchMVCC := spanset.NewBatchAt(eng.NewBatch(), &ss, hlc.Timestamp{WallTime: 1})
   496  	defer batchMVCC.Close()
   497  
   498  	// Writes.
   499  	for _, batch := range []storage.Batch{batchNonMVCC, batchMVCC} {
   500  		if err := batch.Put(wkey, value); err != nil {
   501  			t.Fatalf("write disallowed through non-MVCC latch: %+v", err)
   502  		}
   503  	}
   504  
   505  	// Reads.
   506  	for _, batch := range []storage.Batch{batchNonMVCC, batchMVCC} {
   507  		//lint:ignore SA1019 historical usage of deprecated batch.Get is OK
   508  		if res, err := batch.Get(rkey); err != nil {
   509  			t.Errorf("read disallowed through non-MVCC latch: %+v", err)
   510  		} else if !bytes.Equal(res, value) {
   511  			t.Errorf("failed to read previously written value, got %q", res)
   512  		}
   513  	}
   514  }
   515  
   516  // TestSpanSetMVCCResolveWriteIntentRangeUsingIter verifies that
   517  // MVCCResolveWriteIntentRangeUsingIter does not stray outside of the passed-in
   518  // key range (which it only used to do in this corner case tested here).
   519  //
   520  // See #20894.
   521  func TestSpanSetMVCCResolveWriteIntentRangeUsingIter(t *testing.T) {
   522  	defer leaktest.AfterTest(t)()
   523  	eng := storage.NewDefaultInMem()
   524  	defer eng.Close()
   525  
   526  	ctx := context.Background()
   527  
   528  	value := roachpb.MakeValueFromString("irrelevant")
   529  
   530  	if err := storage.MVCCPut(
   531  		ctx,
   532  		eng,
   533  		nil, // ms
   534  		roachpb.Key("b"),
   535  		hlc.Timestamp{WallTime: 10}, // irrelevant
   536  		value,
   537  		nil, // txn
   538  	); err != nil {
   539  		t.Fatal(err)
   540  	}
   541  
   542  	var ss spanset.SpanSet
   543  	ss.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{Key: roachpb.Key("a"), EndKey: roachpb.Key("b\x00")})
   544  
   545  	batch := spanset.NewBatch(eng.NewBatch(), &ss)
   546  	defer batch.Close()
   547  
   548  	intent := roachpb.LockUpdate{
   549  		Span:   roachpb.Span{Key: roachpb.Key("a"), EndKey: roachpb.Key("b\x00")},
   550  		Txn:    enginepb.TxnMeta{}, // unused
   551  		Status: roachpb.PENDING,
   552  	}
   553  
   554  	iterAndBuf := storage.GetIterAndBuf(batch, storage.IterOptions{UpperBound: intent.Span.EndKey})
   555  	defer iterAndBuf.Cleanup()
   556  
   557  	if _, _, err := storage.MVCCResolveWriteIntentRangeUsingIter(
   558  		ctx, batch, iterAndBuf, nil /* ms */, intent, 0,
   559  	); err != nil {
   560  		t.Fatal(err)
   561  	}
   562  }