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

     1  // Copyright 2020 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  	"context"
    15  	"fmt"
    16  	"testing"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/abortspan"
    19  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval"
    20  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result"
    21  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/lock"
    22  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase"
    23  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    24  	"github.com/cockroachdb/cockroach/pkg/storage"
    25  	"github.com/cockroachdb/cockroach/pkg/storage/enginepb"
    26  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    27  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  func TestEvaluateBatch(t *testing.T) {
    32  	defer leaktest.AfterTest(t)()
    33  
    34  	ts := hlc.Timestamp{WallTime: 1}
    35  	txn := roachpb.MakeTransaction("test", roachpb.Key("a"), 0, ts, 0)
    36  
    37  	tcs := []testCase{
    38  		//
    39  		// Test suite for MaxRequestSpans.
    40  		//
    41  		{
    42  			// We should never evaluate empty batches, but here's what would happen
    43  			// if we did.
    44  			name:  "all empty",
    45  			setup: func(t *testing.T, d *data) {},
    46  			check: func(t *testing.T, r resp) {
    47  				require.Nil(t, r.pErr)
    48  				require.NotNil(t, r.br)
    49  				require.Empty(t, r.br.Responses)
    50  			},
    51  		}, {
    52  			// Scanning without limit should return everything.
    53  			name: "scan without MaxSpanRequestKeys",
    54  			setup: func(t *testing.T, d *data) {
    55  				writeABCDEF(t, d)
    56  				req := scanArgsString("a", "z")
    57  				d.ba.Add(req)
    58  			},
    59  			check: func(t *testing.T, r resp) {
    60  				verifyScanResult(t, r, []string{"a", "b", "c", "d", "e", "f"})
    61  				verifyResumeSpans(t, r, "")
    62  			},
    63  		}, {
    64  			// Ditto in reverse.
    65  			name: "reverse scan without MaxSpanRequestKeys",
    66  			setup: func(t *testing.T, d *data) {
    67  				writeABCDEF(t, d)
    68  				req := revScanArgsString("a", "z")
    69  				d.ba.Add(req)
    70  			},
    71  			check: func(t *testing.T, r resp) {
    72  				verifyScanResult(t, r, []string{"f", "e", "d", "c", "b", "a"})
    73  				verifyResumeSpans(t, r, "")
    74  			},
    75  		}, {
    76  			// Scanning with "giant" limit should return everything.
    77  			name: "scan with giant MaxSpanRequestKeys",
    78  			setup: func(t *testing.T, d *data) {
    79  				writeABCDEF(t, d)
    80  				req := scanArgsString("a", "z")
    81  				d.ba.Add(req)
    82  				d.ba.MaxSpanRequestKeys = 100000
    83  			},
    84  			check: func(t *testing.T, r resp) {
    85  				verifyScanResult(t, r, []string{"a", "b", "c", "d", "e", "f"})
    86  				verifyResumeSpans(t, r, "")
    87  			},
    88  		}, {
    89  			// Ditto in reverse.
    90  			name: "reverse scan with giant MaxSpanRequestKeys",
    91  			setup: func(t *testing.T, d *data) {
    92  				writeABCDEF(t, d)
    93  				req := revScanArgsString("a", "z")
    94  				d.ba.Add(req)
    95  				d.ba.MaxSpanRequestKeys = 100000
    96  			},
    97  			check: func(t *testing.T, r resp) {
    98  				verifyScanResult(t, r, []string{"f", "e", "d", "c", "b", "a"})
    99  				verifyResumeSpans(t, r, "")
   100  			},
   101  		}, {
   102  			// Similar to above, just two scans.
   103  			name: "scans with giant MaxSpanRequestKeys",
   104  			setup: func(t *testing.T, d *data) {
   105  				writeABCDEF(t, d)
   106  				d.ba.Add(scanArgsString("a", "c"))
   107  				d.ba.Add(scanArgsString("d", "g"))
   108  				d.ba.MaxSpanRequestKeys = 100000
   109  			},
   110  			check: func(t *testing.T, r resp) {
   111  				verifyScanResult(t, r, []string{"a", "b"}, []string{"d", "e", "f"})
   112  				verifyResumeSpans(t, r, "", "")
   113  			},
   114  		}, {
   115  			// Ditto in reverse.
   116  			name: "reverse scans with giant MaxSpanRequestKeys",
   117  			setup: func(t *testing.T, d *data) {
   118  				writeABCDEF(t, d)
   119  				d.ba.Add(revScanArgsString("d", "g"))
   120  				d.ba.Add(revScanArgsString("a", "c"))
   121  				d.ba.MaxSpanRequestKeys = 100000
   122  			},
   123  			check: func(t *testing.T, r resp) {
   124  				verifyScanResult(t, r, []string{"f", "e", "d"}, []string{"b", "a"})
   125  				verifyResumeSpans(t, r, "", "")
   126  			},
   127  		}, {
   128  			// A batch limited to return only one key. Throw in a Get which is
   129  			// not subject to limitation and should thus have returned a value.
   130  			// However, the second scan comes up empty because there's no quota left.
   131  			//
   132  			// Note that there is currently a lot of undesirable behavior in the KV
   133  			// API for pretty much any batch that's not a nonoverlapping sorted run
   134  			// of only scans or only reverse scans. For example, in the example
   135  			// below, one would get a response for get(f) even though the resume
   136  			// span on the first scan is `[c,...)`. The higher layers of KV don't
   137  			// handle that correctly. Right now we just trust that nobody will
   138  			// send such requests.
   139  			name: "scans with MaxSpanRequestKeys=1",
   140  			setup: func(t *testing.T, d *data) {
   141  				writeABCDEF(t, d)
   142  				d.ba.Add(scanArgsString("a", "c"))
   143  				d.ba.Add(getArgsString("f"))
   144  				d.ba.Add(scanArgsString("d", "f"))
   145  				d.ba.MaxSpanRequestKeys = 1
   146  			},
   147  			check: func(t *testing.T, r resp) {
   148  				verifyScanResult(t, r, []string{"a"}, []string{"f"}, nil)
   149  				verifyResumeSpans(t, r, "b-c", "", "d-f")
   150  				b, err := r.br.Responses[1].GetGet().Value.GetBytes()
   151  				require.NoError(t, err)
   152  				require.Equal(t, "value-f", string(b))
   153  			},
   154  		}, {
   155  			// Ditto in reverse.
   156  			name: "reverse scans with MaxSpanRequestKeys=1",
   157  			setup: func(t *testing.T, d *data) {
   158  				writeABCDEF(t, d)
   159  				d.ba.Add(revScanArgsString("d", "f"))
   160  				d.ba.Add(getArgsString("f"))
   161  				d.ba.Add(revScanArgsString("a", "c"))
   162  				d.ba.MaxSpanRequestKeys = 1
   163  			},
   164  			check: func(t *testing.T, r resp) {
   165  				verifyScanResult(t, r, []string{"e"}, []string{"f"}, nil)
   166  				verifyResumeSpans(t, r, "d-d\x00", "", "a-c")
   167  				b, err := r.br.Responses[1].GetGet().Value.GetBytes()
   168  				require.NoError(t, err)
   169  				require.Equal(t, "value-f", string(b))
   170  			},
   171  		}, {
   172  			// Similar, but this time the request allows the second scan to
   173  			// return one (but not more) remaining key. Again there's a Get
   174  			// that isn't counted against the limit.
   175  			name: "scans with MaxSpanRequestKeys=3",
   176  			setup: func(t *testing.T, d *data) {
   177  				writeABCDEF(t, d)
   178  				d.ba.Add(scanArgsString("a", "c"))
   179  				d.ba.Add(getArgsString("e"))
   180  				d.ba.Add(scanArgsString("c", "e"))
   181  				d.ba.MaxSpanRequestKeys = 3
   182  			},
   183  			check: func(t *testing.T, r resp) {
   184  				verifyScanResult(t, r, []string{"a", "b"}, []string{"e"}, []string{"c"})
   185  				verifyResumeSpans(t, r, "", "", "d-e")
   186  				b, err := r.br.Responses[1].GetGet().Value.GetBytes()
   187  				require.NoError(t, err)
   188  				require.Equal(t, "value-e", string(b))
   189  			},
   190  		}, {
   191  			// Ditto in reverse.
   192  			name: "reverse scans with MaxSpanRequestKeys=3",
   193  			setup: func(t *testing.T, d *data) {
   194  				writeABCDEF(t, d)
   195  				d.ba.Add(revScanArgsString("c", "e"))
   196  				d.ba.Add(getArgsString("e"))
   197  				d.ba.Add(revScanArgsString("a", "c"))
   198  				d.ba.MaxSpanRequestKeys = 3
   199  			},
   200  			check: func(t *testing.T, r resp) {
   201  				verifyScanResult(t, r, []string{"d", "c"}, []string{"e"}, []string{"b"})
   202  				verifyResumeSpans(t, r, "", "", "a-a\x00")
   203  				b, err := r.br.Responses[1].GetGet().Value.GetBytes()
   204  				require.NoError(t, err)
   205  				require.Equal(t, "value-e", string(b))
   206  			},
   207  		},
   208  		//
   209  		// Test suite for TargetBytes.
   210  		//
   211  		{
   212  			// Two scans and a target bytes limit that saturates during the
   213  			// first.
   214  			name: "scans with TargetBytes=1",
   215  			setup: func(t *testing.T, d *data) {
   216  				writeABCDEF(t, d)
   217  				d.ba.Add(scanArgsString("a", "c"))
   218  				d.ba.Add(getArgsString("e"))
   219  				d.ba.Add(scanArgsString("c", "e"))
   220  				d.ba.TargetBytes = 1
   221  				// Also set a nontrivial MaxSpanRequestKeys, just to make sure
   222  				// there's no weird interaction (like it overriding TargetBytes).
   223  				// The stricter one ought to win.
   224  				d.ba.MaxSpanRequestKeys = 3
   225  			},
   226  			check: func(t *testing.T, r resp) {
   227  				verifyScanResult(t, r, []string{"a"}, []string{"e"}, nil)
   228  				verifyResumeSpans(t, r, "b-c", "", "c-e")
   229  				b, err := r.br.Responses[1].GetGet().Value.GetBytes()
   230  				require.NoError(t, err)
   231  				require.Equal(t, "value-e", string(b))
   232  			},
   233  		}, {
   234  			// Ditto in reverse.
   235  			name: "reverse scans with TargetBytes=1",
   236  			setup: func(t *testing.T, d *data) {
   237  				writeABCDEF(t, d)
   238  				d.ba.Add(revScanArgsString("c", "e"))
   239  				d.ba.Add(getArgsString("e"))
   240  				d.ba.Add(revScanArgsString("a", "c"))
   241  				d.ba.TargetBytes = 1
   242  				d.ba.MaxSpanRequestKeys = 3
   243  			},
   244  			check: func(t *testing.T, r resp) {
   245  				verifyScanResult(t, r, []string{"d"}, []string{"e"}, nil)
   246  				verifyResumeSpans(t, r, "c-c\x00", "", "a-c")
   247  				b, err := r.br.Responses[1].GetGet().Value.GetBytes()
   248  				require.NoError(t, err)
   249  				require.Equal(t, "value-e", string(b))
   250  			},
   251  		},
   252  		//
   253  		// Test suite for KeyLocking.
   254  		//
   255  		{
   256  			// Three scans that observe 3, 1, and 0 keys, respectively. An
   257  			// unreplicated lock should be acquired on each key that is scanned.
   258  			name: "scans with key locking",
   259  			setup: func(t *testing.T, d *data) {
   260  				writeABCDEF(t, d)
   261  				scanAD := scanArgsString("a", "d")
   262  				scanAD.KeyLocking = lock.Exclusive
   263  				d.ba.Add(scanAD)
   264  				scanEF := scanArgsString("e", "f")
   265  				scanEF.KeyLocking = lock.Exclusive
   266  				d.ba.Add(scanEF)
   267  				scanHJ := scanArgsString("h", "j")
   268  				scanHJ.KeyLocking = lock.Exclusive
   269  				d.ba.Add(scanHJ)
   270  				d.ba.Txn = &txn
   271  			},
   272  			check: func(t *testing.T, r resp) {
   273  				verifyScanResult(t, r, []string{"a", "b", "c"}, []string{"e"}, nil)
   274  				verifyAcquiredLocks(t, r, lock.Unreplicated, "a", "b", "c", "e")
   275  				verifyAcquiredLocks(t, r, lock.Replicated, []string(nil)...)
   276  			},
   277  		},
   278  		{
   279  			// Ditto in reverse.
   280  			name: "reverse scans with key locking",
   281  			setup: func(t *testing.T, d *data) {
   282  				writeABCDEF(t, d)
   283  				scanAD := revScanArgsString("a", "d")
   284  				scanAD.KeyLocking = lock.Exclusive
   285  				d.ba.Add(scanAD)
   286  				scanEF := revScanArgsString("e", "f")
   287  				scanEF.KeyLocking = lock.Exclusive
   288  				d.ba.Add(scanEF)
   289  				scanHJ := revScanArgsString("h", "j")
   290  				scanHJ.KeyLocking = lock.Exclusive
   291  				d.ba.Add(scanHJ)
   292  				d.ba.Txn = &txn
   293  			},
   294  			check: func(t *testing.T, r resp) {
   295  				verifyScanResult(t, r, []string{"c", "b", "a"}, []string{"e"}, nil)
   296  				verifyAcquiredLocks(t, r, lock.Unreplicated, "c", "b", "a", "e")
   297  				verifyAcquiredLocks(t, r, lock.Replicated, []string(nil)...)
   298  			},
   299  		},
   300  		{
   301  			// Three scans that observe 3, 1, and 0 keys, respectively. No
   302  			// transaction set, so no locks should be acquired.
   303  			name: "scans with key locking without txn",
   304  			setup: func(t *testing.T, d *data) {
   305  				writeABCDEF(t, d)
   306  				scanAD := scanArgsString("a", "d")
   307  				scanAD.KeyLocking = lock.Exclusive
   308  				d.ba.Add(scanAD)
   309  				scanEF := scanArgsString("e", "f")
   310  				scanEF.KeyLocking = lock.Exclusive
   311  				d.ba.Add(scanEF)
   312  				scanHJ := scanArgsString("h", "j")
   313  				scanHJ.KeyLocking = lock.Exclusive
   314  				d.ba.Add(scanHJ)
   315  			},
   316  			check: func(t *testing.T, r resp) {
   317  				verifyScanResult(t, r, []string{"a", "b", "c"}, []string{"e"}, nil)
   318  				verifyAcquiredLocks(t, r, lock.Unreplicated, []string(nil)...)
   319  				verifyAcquiredLocks(t, r, lock.Replicated, []string(nil)...)
   320  			},
   321  		},
   322  		{
   323  			// Ditto in reverse.
   324  			name: "reverse scans with key locking without txn",
   325  			setup: func(t *testing.T, d *data) {
   326  				writeABCDEF(t, d)
   327  				scanAD := revScanArgsString("a", "d")
   328  				scanAD.KeyLocking = lock.Exclusive
   329  				d.ba.Add(scanAD)
   330  				scanEF := revScanArgsString("e", "f")
   331  				scanEF.KeyLocking = lock.Exclusive
   332  				d.ba.Add(scanEF)
   333  				scanHJ := revScanArgsString("h", "j")
   334  				scanHJ.KeyLocking = lock.Exclusive
   335  				d.ba.Add(scanHJ)
   336  			},
   337  			check: func(t *testing.T, r resp) {
   338  				verifyScanResult(t, r, []string{"c", "b", "a"}, []string{"e"}, nil)
   339  				verifyAcquiredLocks(t, r, lock.Unreplicated, []string(nil)...)
   340  				verifyAcquiredLocks(t, r, lock.Replicated, []string(nil)...)
   341  			},
   342  		},
   343  		{
   344  
   345  			// Scanning with key locking and a MaxSpanRequestKeys limit should
   346  			// acquire an unreplicated lock on each key returned and no locks on
   347  			// keys past the limit.
   348  			name: "scan with key locking and MaxSpanRequestKeys=3",
   349  			setup: func(t *testing.T, d *data) {
   350  				writeABCDEF(t, d)
   351  				scanAE := scanArgsString("a", "e")
   352  				scanAE.KeyLocking = lock.Exclusive
   353  				d.ba.Add(scanAE)
   354  				d.ba.Txn = &txn
   355  				d.ba.MaxSpanRequestKeys = 3
   356  			},
   357  			check: func(t *testing.T, r resp) {
   358  				verifyScanResult(t, r, []string{"a", "b", "c"})
   359  				verifyResumeSpans(t, r, "d-e")
   360  				verifyAcquiredLocks(t, r, lock.Unreplicated, "a", "b", "c")
   361  				verifyAcquiredLocks(t, r, lock.Replicated, []string(nil)...)
   362  			},
   363  		},
   364  		{
   365  			// Ditto in reverse.
   366  			name: "reverse scan with key locking and MaxSpanRequestKeys=3",
   367  			setup: func(t *testing.T, d *data) {
   368  				writeABCDEF(t, d)
   369  				scanAE := revScanArgsString("a", "e")
   370  				scanAE.KeyLocking = lock.Exclusive
   371  				d.ba.Add(scanAE)
   372  				d.ba.Txn = &txn
   373  				d.ba.MaxSpanRequestKeys = 3
   374  			},
   375  			check: func(t *testing.T, r resp) {
   376  				verifyScanResult(t, r, []string{"d", "c", "b"})
   377  				verifyResumeSpans(t, r, "a-a\x00")
   378  				verifyAcquiredLocks(t, r, lock.Unreplicated, "d", "c", "b")
   379  				verifyAcquiredLocks(t, r, lock.Replicated, []string(nil)...)
   380  			},
   381  		},
   382  		{
   383  
   384  			// Scanning with key locking and a MaxSpanRequestKeys limit should
   385  			// acquire an unreplicated lock on each key returned and no locks on
   386  			// keys past the limit. One the batch's limit is exhausted, no more
   387  			// rows are scanner nor locks acquired.
   388  			name: "scans with key locking and MaxSpanRequestKeys=3",
   389  			setup: func(t *testing.T, d *data) {
   390  				writeABCDEF(t, d)
   391  				scanAE := scanArgsString("a", "e")
   392  				scanAE.KeyLocking = lock.Exclusive
   393  				d.ba.Add(scanAE)
   394  				scanHJ := scanArgsString("h", "j")
   395  				scanHJ.KeyLocking = lock.Exclusive
   396  				d.ba.Add(scanHJ)
   397  				d.ba.Txn = &txn
   398  				d.ba.MaxSpanRequestKeys = 3
   399  			},
   400  			check: func(t *testing.T, r resp) {
   401  				verifyScanResult(t, r, []string{"a", "b", "c"}, nil)
   402  				verifyResumeSpans(t, r, "d-e", "h-j")
   403  				verifyAcquiredLocks(t, r, lock.Unreplicated, "a", "b", "c")
   404  				verifyAcquiredLocks(t, r, lock.Replicated, []string(nil)...)
   405  			},
   406  		},
   407  		{
   408  			// Ditto in reverse.
   409  			name: "reverse scans with key locking and MaxSpanRequestKeys=3",
   410  			setup: func(t *testing.T, d *data) {
   411  				writeABCDEF(t, d)
   412  				scanAE := revScanArgsString("a", "e")
   413  				scanAE.KeyLocking = lock.Exclusive
   414  				d.ba.Add(scanAE)
   415  				scanHJ := scanArgsString("h", "j")
   416  				scanHJ.KeyLocking = lock.Exclusive
   417  				d.ba.Add(scanHJ)
   418  				d.ba.Txn = &txn
   419  				d.ba.MaxSpanRequestKeys = 3
   420  			},
   421  			check: func(t *testing.T, r resp) {
   422  				verifyScanResult(t, r, []string{"d", "c", "b"}, nil)
   423  				verifyResumeSpans(t, r, "a-a\x00", "h-j")
   424  				verifyAcquiredLocks(t, r, lock.Unreplicated, "d", "c", "b")
   425  				verifyAcquiredLocks(t, r, lock.Replicated, []string(nil)...)
   426  			},
   427  		},
   428  		{
   429  			// Scanning with key locking and a TargetBytes limit should acquire
   430  			// an unreplicated lock on each key returned and no locks on keys
   431  			// past the limit.
   432  			name: "scan with key locking and TargetBytes=1",
   433  			setup: func(t *testing.T, d *data) {
   434  				writeABCDEF(t, d)
   435  				scanAE := scanArgsString("a", "e")
   436  				scanAE.KeyLocking = lock.Exclusive
   437  				d.ba.Add(scanAE)
   438  				d.ba.Txn = &txn
   439  				d.ba.TargetBytes = 1
   440  			},
   441  			check: func(t *testing.T, r resp) {
   442  				verifyScanResult(t, r, []string{"a"})
   443  				verifyResumeSpans(t, r, "b-e")
   444  				verifyAcquiredLocks(t, r, lock.Unreplicated, "a")
   445  				verifyAcquiredLocks(t, r, lock.Replicated, []string(nil)...)
   446  			},
   447  		},
   448  		{
   449  			// Ditto in reverse.
   450  			name: "reverse scan with key locking and TargetBytes=1",
   451  			setup: func(t *testing.T, d *data) {
   452  				writeABCDEF(t, d)
   453  				scanAE := revScanArgsString("a", "e")
   454  				scanAE.KeyLocking = lock.Exclusive
   455  				d.ba.Add(scanAE)
   456  				d.ba.Txn = &txn
   457  				d.ba.TargetBytes = 1
   458  			},
   459  			check: func(t *testing.T, r resp) {
   460  				verifyScanResult(t, r, []string{"d"})
   461  				verifyResumeSpans(t, r, "a-c\x00")
   462  				verifyAcquiredLocks(t, r, lock.Unreplicated, "d")
   463  				verifyAcquiredLocks(t, r, lock.Replicated, []string(nil)...)
   464  			},
   465  		},
   466  		{
   467  			// Scanning with key locking and a TargetBytes limit should acquire
   468  			// an unreplicated lock on each key returned and no locks on keys
   469  			// past the limit. One the batch's limit is exhausted, no more rows
   470  			// are scanner nor locks acquired.
   471  			name: "scans with key locking and TargetBytes=1",
   472  			setup: func(t *testing.T, d *data) {
   473  				writeABCDEF(t, d)
   474  				scanAE := scanArgsString("a", "e")
   475  				scanAE.KeyLocking = lock.Exclusive
   476  				d.ba.Add(scanAE)
   477  				scanHJ := scanArgsString("h", "j")
   478  				scanHJ.KeyLocking = lock.Exclusive
   479  				d.ba.Add(scanHJ)
   480  				d.ba.Txn = &txn
   481  				d.ba.TargetBytes = 1
   482  			},
   483  			check: func(t *testing.T, r resp) {
   484  				verifyScanResult(t, r, []string{"a"}, nil)
   485  				verifyResumeSpans(t, r, "b-e", "h-j")
   486  				verifyAcquiredLocks(t, r, lock.Unreplicated, "a")
   487  				verifyAcquiredLocks(t, r, lock.Replicated, []string(nil)...)
   488  			},
   489  		},
   490  		{
   491  			// Ditto in reverse.
   492  			name: "reverse scans with key locking and TargetBytes=1",
   493  			setup: func(t *testing.T, d *data) {
   494  				writeABCDEF(t, d)
   495  				scanAE := revScanArgsString("a", "e")
   496  				scanAE.KeyLocking = lock.Exclusive
   497  				d.ba.Add(scanAE)
   498  				scanHJ := scanArgsString("h", "j")
   499  				scanHJ.KeyLocking = lock.Exclusive
   500  				d.ba.Add(scanHJ)
   501  				d.ba.Txn = &txn
   502  				d.ba.TargetBytes = 1
   503  			},
   504  			check: func(t *testing.T, r resp) {
   505  				verifyScanResult(t, r, []string{"d"}, nil)
   506  				verifyResumeSpans(t, r, "a-c\x00", "h-j")
   507  				verifyAcquiredLocks(t, r, lock.Unreplicated, "d")
   508  				verifyAcquiredLocks(t, r, lock.Replicated, []string(nil)...)
   509  			},
   510  		},
   511  		//
   512  		// Test suite for ResolveIntentRange with and without limits.
   513  		//
   514  		{
   515  			// Three range intent resolutions that observe 3, 1, and 0 intent,
   516  			// respectively. All intents should be resolved.
   517  			name: "ranged intent resolution",
   518  			setup: func(t *testing.T, d *data) {
   519  				writeABCDEFIntents(t, d, &txn)
   520  				d.ba.Add(resolveIntentRangeArgsString("a", "d", txn.TxnMeta, roachpb.COMMITTED))
   521  				d.ba.Add(resolveIntentRangeArgsString("e", "f", txn.TxnMeta, roachpb.COMMITTED))
   522  				d.ba.Add(resolveIntentRangeArgsString("h", "j", txn.TxnMeta, roachpb.COMMITTED))
   523  			},
   524  			check: func(t *testing.T, r resp) {
   525  				verifyNumKeys(t, r, 3, 1, 0)
   526  				verifyResumeSpans(t, r, "", "", "")
   527  			},
   528  		},
   529  		{
   530  			// Resolving intents with a giant limit should resolve everything.
   531  			name: "ranged intent resolution with giant MaxSpanRequestKeys",
   532  			setup: func(t *testing.T, d *data) {
   533  				writeABCDEFIntents(t, d, &txn)
   534  				d.ba.Add(resolveIntentRangeArgsString("a", "d", txn.TxnMeta, roachpb.COMMITTED))
   535  				d.ba.Add(resolveIntentRangeArgsString("e", "f", txn.TxnMeta, roachpb.COMMITTED))
   536  				d.ba.Add(resolveIntentRangeArgsString("h", "j", txn.TxnMeta, roachpb.COMMITTED))
   537  				d.ba.MaxSpanRequestKeys = 100000
   538  			},
   539  			check: func(t *testing.T, r resp) {
   540  				verifyNumKeys(t, r, 3, 1, 0)
   541  				verifyResumeSpans(t, r, "", "", "")
   542  			},
   543  		},
   544  		{
   545  			// A batch limited to resolve only up to 3 keys should respect that
   546  			// limit. The limit is saturated by the first request in the batch.
   547  			name: "ranged intent resolution with MaxSpanRequestKeys=3",
   548  			setup: func(t *testing.T, d *data) {
   549  				writeABCDEFIntents(t, d, &txn)
   550  				d.ba.Add(resolveIntentRangeArgsString("a", "d", txn.TxnMeta, roachpb.COMMITTED))
   551  				d.ba.Add(resolveIntentRangeArgsString("e", "f", txn.TxnMeta, roachpb.COMMITTED))
   552  				d.ba.Add(resolveIntentRangeArgsString("h", "j", txn.TxnMeta, roachpb.COMMITTED))
   553  				d.ba.MaxSpanRequestKeys = 3
   554  			},
   555  			check: func(t *testing.T, r resp) {
   556  				verifyNumKeys(t, r, 3, 0, 0)
   557  				verifyResumeSpans(t, r, "c\x00-d", "e-f", "h-j")
   558  			},
   559  		},
   560  	}
   561  
   562  	for _, tc := range tcs {
   563  		t.Run(tc.name, func(t *testing.T) {
   564  			ctx := context.Background()
   565  			eng := storage.NewDefaultInMem()
   566  			defer eng.Close()
   567  
   568  			d := &data{
   569  				idKey: kvserverbase.CmdIDKey("testing"),
   570  				eng:   eng,
   571  			}
   572  			d.AbortSpan = abortspan.New(1)
   573  			d.ba.Header.Timestamp = ts
   574  
   575  			tc.setup(t, d)
   576  
   577  			var r resp
   578  			r.d = d
   579  			r.br, r.res, r.pErr = evaluateBatch(
   580  				ctx,
   581  				d.idKey,
   582  				d.eng,
   583  				d.MockEvalCtx.EvalContext(),
   584  				&d.ms,
   585  				&d.ba,
   586  				d.readOnly,
   587  			)
   588  
   589  			tc.check(t, r)
   590  		})
   591  	}
   592  }
   593  
   594  type data struct {
   595  	batcheval.MockEvalCtx
   596  	ba       roachpb.BatchRequest
   597  	idKey    kvserverbase.CmdIDKey
   598  	eng      storage.Engine
   599  	ms       enginepb.MVCCStats
   600  	readOnly bool
   601  }
   602  
   603  type resp struct {
   604  	d    *data
   605  	br   *roachpb.BatchResponse
   606  	res  result.Result
   607  	pErr *roachpb.Error
   608  }
   609  
   610  type testCase struct {
   611  	name  string
   612  	setup func(*testing.T, *data)
   613  	check func(*testing.T, resp)
   614  }
   615  
   616  func writeABCDEF(t *testing.T, d *data) {
   617  	writeABCDEFIntents(t, d, nil /* txn */)
   618  }
   619  
   620  func writeABCDEFIntents(t *testing.T, d *data, txn *roachpb.Transaction) {
   621  	for _, k := range []string{"a", "b", "c", "d", "e", "f"} {
   622  		require.NoError(t, storage.MVCCPut(
   623  			context.Background(), d.eng, nil /* ms */, roachpb.Key(k), d.ba.Timestamp,
   624  			roachpb.MakeValueFromString("value-"+k), txn))
   625  	}
   626  }
   627  
   628  func verifyScanResult(t *testing.T, r resp, keysPerResp ...[]string) {
   629  	require.Nil(t, r.pErr)
   630  	require.NotNil(t, r.br)
   631  	require.Len(t, r.br.Responses, len(keysPerResp))
   632  	for i, keys := range keysPerResp {
   633  		var isGet bool
   634  		scan := r.br.Responses[i].GetInner()
   635  		var rows []roachpb.KeyValue
   636  		switch req := scan.(type) {
   637  		case *roachpb.ScanResponse:
   638  			rows = req.Rows
   639  		case *roachpb.ReverseScanResponse:
   640  			rows = req.Rows
   641  		case *roachpb.GetResponse:
   642  			isGet = true
   643  			rows = []roachpb.KeyValue{{
   644  				Key:   r.d.ba.Requests[i].GetGet().Key,
   645  				Value: *req.Value,
   646  			}}
   647  		default:
   648  		}
   649  
   650  		if !isGet {
   651  			require.EqualValues(t, len(keys), scan.Header().NumKeys, "in response #%d", i+1)
   652  		} else {
   653  			require.Zero(t, scan.Header().NumKeys, "in response #%d", i+1)
   654  		}
   655  		var actKeys []string
   656  		for _, row := range rows {
   657  			actKeys = append(actKeys, string(row.Key))
   658  		}
   659  		require.Equal(t, keys, actKeys, "in response #%i", i+1)
   660  	}
   661  }
   662  
   663  func verifyNumKeys(t *testing.T, r resp, keysPerResp ...int) {
   664  	require.Nil(t, r.pErr)
   665  	require.NotNil(t, r.br)
   666  	require.Len(t, r.br.Responses, len(keysPerResp))
   667  	for i, keys := range keysPerResp {
   668  		actKeys := int(r.br.Responses[i].GetInner().Header().NumKeys)
   669  		require.Equal(t, keys, actKeys, "in response #%i", i+1)
   670  	}
   671  }
   672  
   673  func verifyResumeSpans(t *testing.T, r resp, resumeSpans ...string) {
   674  	for i, span := range resumeSpans {
   675  		rs := r.br.Responses[i].GetInner().Header().ResumeSpan
   676  		if span == "" {
   677  			require.Nil(t, rs)
   678  		} else {
   679  			require.NotNil(t, rs)
   680  			act := fmt.Sprintf("%s-%s", string(rs.Key), string(rs.EndKey))
   681  			require.Equal(t, span, act, "#%d", i+1)
   682  		}
   683  	}
   684  }
   685  
   686  func verifyAcquiredLocks(t *testing.T, r resp, dur lock.Durability, lockedKeys ...string) {
   687  	var foundLocked []string
   688  	for _, l := range r.res.Local.AcquiredLocks {
   689  		if l.Durability == dur {
   690  			foundLocked = append(foundLocked, string(l.Key))
   691  		}
   692  	}
   693  	require.Equal(t, lockedKeys, foundLocked)
   694  }