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

     1  // Copyright 2016 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 intentresolver
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"reflect"
    17  	"sort"
    18  	"sync"
    19  	"sync/atomic"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/cockroachdb/cockroach/pkg/kv"
    24  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result"
    25  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase"
    26  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    27  	"github.com/cockroachdb/cockroach/pkg/storage/enginepb"
    28  	"github.com/cockroachdb/cockroach/pkg/testutils"
    29  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    30  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    31  	"github.com/cockroachdb/cockroach/pkg/util/log"
    32  	"github.com/cockroachdb/cockroach/pkg/util/stop"
    33  	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
    34  	"github.com/cockroachdb/cockroach/pkg/util/tracing"
    35  	"github.com/cockroachdb/cockroach/pkg/util/uuid"
    36  	"github.com/cockroachdb/errors"
    37  	"github.com/stretchr/testify/assert"
    38  )
    39  
    40  // TestCleanupTxnIntentsOnGCAsync exercises the code which is used to
    41  // asynchronously clean up transaction intents and then transaction records.
    42  // This method is invoked from the storage GC queue.
    43  func TestCleanupTxnIntentsOnGCAsync(t *testing.T) {
    44  	defer leaktest.AfterTest(t)()
    45  
    46  	ctx, cancel := context.WithCancel(context.Background())
    47  	defer cancel()
    48  	stopper := stop.NewStopper()
    49  	defer stopper.Stop(ctx)
    50  	clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond)
    51  	cfg := Config{
    52  		Stopper: stopper,
    53  		Clock:   clock,
    54  	}
    55  	type testCase struct {
    56  		txn           *roachpb.Transaction
    57  		intents       []roachpb.LockUpdate
    58  		sendFuncs     *sendFuncs
    59  		expectPushed  bool
    60  		expectSucceed bool
    61  	}
    62  
    63  	// This test creates 3 transaction for use in the below test cases.
    64  	// A new intent resolver is created for each test case so they operate
    65  	// completely independently.
    66  	key := roachpb.Key("a")
    67  	// Txn0 is in the pending state and is not old enough to have expired so the
    68  	// code ought to send nothing.
    69  	txn0 := newTransaction("txn0", key, 1, clock)
    70  	// Txn1 is in the pending state but is expired.
    71  	txn1 := newTransaction("txn1", key, 1, clock)
    72  	txn1.ReadTimestamp.WallTime -= int64(100 * time.Second)
    73  	txn1.DeprecatedOrigTimestamp = txn1.ReadTimestamp
    74  	txn1.LastHeartbeat = txn1.ReadTimestamp
    75  	// Txn2 is in the staging state and is not old enough to have expired so the
    76  	// code ought to send nothing.
    77  	txn2 := newTransaction("txn2", key, 1, clock)
    78  	txn2.Status = roachpb.STAGING
    79  	// Txn3 is in the staging state but is expired.
    80  	txn3 := newTransaction("txn3", key, 1, clock)
    81  	txn3.Status = roachpb.STAGING
    82  	txn3.ReadTimestamp.WallTime -= int64(100 * time.Second)
    83  	txn3.DeprecatedOrigTimestamp = txn3.ReadTimestamp
    84  	txn3.LastHeartbeat = txn3.ReadTimestamp
    85  	// Txn4 is in the committed state.
    86  	txn4 := newTransaction("txn4", key, 1, clock)
    87  	txn4.Status = roachpb.COMMITTED
    88  	cases := []*testCase{
    89  		// This one has an unexpired pending transaction so it's skipped.
    90  		{
    91  			txn:       txn0,
    92  			sendFuncs: newSendFuncs(t),
    93  		},
    94  		// Txn1 is pending and expired so the code should attempt to push the txn.
    95  		// The provided sender will fail on the first request. The callback should
    96  		// indicate that the transaction was pushed but that the resolution was not
    97  		// successful.
    98  		{
    99  			txn:          txn1,
   100  			sendFuncs:    newSendFuncs(t, failSendFunc),
   101  			expectPushed: true,
   102  		},
   103  		// Txn1 is pending and expired so the code should attempt to push the txn
   104  		// and then work to resolve its intents. The intent resolution happens in
   105  		// different requests for individual keys and then for spans. This case will
   106  		// allow the individual key intent to be resolved completely but will fail
   107  		// to resolve the span. The callback should indicate that the transaction
   108  		// has been pushed but that the garbage collection was not successful.
   109  		{
   110  			txn: txn1,
   111  			intents: []roachpb.LockUpdate{
   112  				roachpb.MakeLockUpdate(txn1, roachpb.Span{Key: key}),
   113  				roachpb.MakeLockUpdate(txn1, roachpb.Span{Key: key, EndKey: roachpb.Key("b")}),
   114  			},
   115  			sendFuncs: newSendFuncs(t,
   116  				singlePushTxnSendFunc(t),
   117  				resolveIntentsSendFunc(t),
   118  				failSendFunc,
   119  			),
   120  			expectPushed: true,
   121  		},
   122  		// Txn1 is pending and expired so the code should attempt to push the txn
   123  		// and then work to resolve its intents. The intent resolution happens in
   124  		// different requests for individual keys and then for spans. This case will
   125  		// allow the individual key intents to be resolved in one request and for
   126  		// the span request to be resolved in another. Finally it will succeed on
   127  		// the GCRequest. This is a positive case and the callback should indicate
   128  		// that the txn has both been pushed and successfully resolved.
   129  		{
   130  			txn: txn1,
   131  			intents: []roachpb.LockUpdate{
   132  				roachpb.MakeLockUpdate(txn1, roachpb.Span{Key: key}),
   133  				roachpb.MakeLockUpdate(txn1, roachpb.Span{Key: roachpb.Key("aa")}),
   134  				roachpb.MakeLockUpdate(txn1, roachpb.Span{Key: key, EndKey: roachpb.Key("b")}),
   135  			},
   136  			sendFuncs: func() *sendFuncs {
   137  				s := newSendFuncs(t)
   138  				s.pushFrontLocked(
   139  					singlePushTxnSendFunc(t),
   140  					resolveIntentsSendFuncs(s, 3, 2),
   141  					gcSendFunc(t),
   142  				)
   143  				return s
   144  			}(),
   145  			expectPushed:  true,
   146  			expectSucceed: true,
   147  		},
   148  		// This one has an unexpired staging transaction so it's skipped.
   149  		{
   150  			txn:       txn2,
   151  			sendFuncs: newSendFuncs(t),
   152  		},
   153  		// Txn3 is staging and expired so the code should attempt to push the txn.
   154  		// The provided sender will fail on the first request. The callback should
   155  		// indicate that the transaction was pushed but that the resolution was not
   156  		// successful.
   157  		{
   158  			txn:          txn3,
   159  			sendFuncs:    newSendFuncs(t, failSendFunc),
   160  			expectPushed: true,
   161  		},
   162  		// Txn3 is staging and expired so the code should attempt to push the txn
   163  		// and then work to resolve its intents. The intent resolution happens in
   164  		// different requests for individual keys and then for spans. This case will
   165  		// allow the individual key intent to be resolved completely but will fail
   166  		// to resolve the span. The callback should indicate that the transaction
   167  		// has been pushed but that the garbage collection was not successful.
   168  		{
   169  			txn: txn3,
   170  			intents: []roachpb.LockUpdate{
   171  				roachpb.MakeLockUpdate(txn3, roachpb.Span{Key: key}),
   172  				roachpb.MakeLockUpdate(txn3, roachpb.Span{Key: key, EndKey: roachpb.Key("b")}),
   173  			},
   174  			sendFuncs: newSendFuncs(t,
   175  				singlePushTxnSendFunc(t),
   176  				resolveIntentsSendFunc(t),
   177  				failSendFunc,
   178  			),
   179  			expectPushed: true,
   180  		},
   181  		// Txn3 is staging and expired so the code should attempt to push the txn
   182  		// and then work to resolve its intents. The intent resolution happens in
   183  		// different requests for individual keys and then for spans. This case will
   184  		// allow the individual key intents to be resolved in one request and for
   185  		// the span request to be resolved in another. Finally it will succeed on
   186  		// the GCRequest. This is a positive case and the callback should indicate
   187  		// that the txn has both been pushed and successfully resolved.
   188  		{
   189  			txn: txn3,
   190  			intents: []roachpb.LockUpdate{
   191  				roachpb.MakeLockUpdate(txn3, roachpb.Span{Key: key}),
   192  				roachpb.MakeLockUpdate(txn3, roachpb.Span{Key: roachpb.Key("aa")}),
   193  				roachpb.MakeLockUpdate(txn3, roachpb.Span{Key: key, EndKey: roachpb.Key("b")}),
   194  			},
   195  			sendFuncs: func() *sendFuncs {
   196  				s := newSendFuncs(t)
   197  				s.pushFrontLocked(
   198  					singlePushTxnSendFunc(t),
   199  					resolveIntentsSendFuncs(s, 3, 2),
   200  					gcSendFunc(t),
   201  				)
   202  				return s
   203  			}(),
   204  			expectPushed:  true,
   205  			expectSucceed: true,
   206  		},
   207  		// Txn4 is committed so it should not be pushed. Also it has no intents so
   208  		// it should only send a GCRequest. The callback should indicate that there
   209  		// is no push but that the gc has occurred successfully.
   210  		{
   211  			txn:           txn4,
   212  			intents:       []roachpb.LockUpdate{},
   213  			sendFuncs:     newSendFuncs(t, gcSendFunc(t)),
   214  			expectSucceed: true,
   215  		},
   216  	}
   217  
   218  	for _, c := range cases {
   219  		t.Run("", func(t *testing.T) {
   220  			ir := newIntentResolverWithSendFuncs(cfg, c.sendFuncs)
   221  			var didPush, didSucceed bool
   222  			done := make(chan struct{})
   223  			onComplete := func(pushed, succeeded bool) {
   224  				didPush, didSucceed = pushed, succeeded
   225  				close(done)
   226  			}
   227  			err := ir.CleanupTxnIntentsOnGCAsync(ctx, 1, c.txn, c.intents, clock.Now(), onComplete)
   228  			if err != nil {
   229  				t.Fatalf("unexpected error sending async transaction")
   230  			}
   231  			<-done
   232  			if c.sendFuncs.len() != 0 {
   233  				t.Errorf("Not all send funcs called")
   234  			}
   235  			if didSucceed != c.expectSucceed {
   236  				t.Fatalf("unexpected success value: got %v, expected %v", didSucceed, c.expectSucceed)
   237  			}
   238  			if didPush != c.expectPushed {
   239  				t.Fatalf("unexpected pushed value: got %v, expected %v", didPush, c.expectPushed)
   240  			}
   241  		})
   242  	}
   243  }
   244  
   245  // TestCleanupIntentsAsync verifies that CleanupIntentsAsync either runs
   246  // synchronously or returns an error when there are too many concurrently
   247  // running tasks.
   248  func TestCleanupIntentsAsyncThrottled(t *testing.T) {
   249  	clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond)
   250  	stopper := stop.NewStopper()
   251  	defer stopper.Stop(context.Background())
   252  	cfg := Config{
   253  		Stopper: stopper,
   254  		Clock:   clock,
   255  	}
   256  	txn := newTransaction("txn", roachpb.Key("a"), 1, clock)
   257  	sf := newSendFuncs(t,
   258  		pushTxnSendFunc(t, 1),
   259  		resolveIntentsSendFunc(t),
   260  	)
   261  	ir := newIntentResolverWithSendFuncs(cfg, sf)
   262  	// Run defaultTaskLimit tasks which will block until blocker is closed.
   263  	blocker := make(chan struct{})
   264  	defer close(blocker)
   265  	var wg sync.WaitGroup
   266  	wg.Add(defaultTaskLimit)
   267  	for i := 0; i < defaultTaskLimit; i++ {
   268  		if err := ir.runAsyncTask(context.Background(), false, func(context.Context) {
   269  			wg.Done()
   270  			<-blocker
   271  		}); err != nil {
   272  			t.Fatalf("Failed to run blocking async task: %+v", err)
   273  		}
   274  	}
   275  	wg.Wait()
   276  	testIntents := []roachpb.Intent{
   277  		roachpb.MakeIntent(&txn.TxnMeta, roachpb.Key("a")),
   278  	}
   279  	// Running with allowSyncProcessing = false should result in an error and no
   280  	// requests being sent.
   281  	err := ir.CleanupIntentsAsync(context.Background(), testIntents, false)
   282  	assert.True(t, errors.Is(err, stop.ErrThrottled))
   283  	// Running with allowSyncProcessing = true should result in the synchronous
   284  	// processing of the intents resulting in no error and the consumption of the
   285  	// sendFuncs.
   286  	err = ir.CleanupIntentsAsync(context.Background(), testIntents, true)
   287  	assert.Nil(t, err)
   288  	assert.Equal(t, sf.len(), 0)
   289  }
   290  
   291  // TestCleanupIntentsAsync verifies that CleanupIntentsAsync sends the expected
   292  // requests.
   293  func TestCleanupIntentsAsync(t *testing.T) {
   294  	defer leaktest.AfterTest(t)()
   295  	type testCase struct {
   296  		intents   []roachpb.Intent
   297  		sendFuncs []sendFunc
   298  	}
   299  	clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond)
   300  	txn := newTransaction("txn", roachpb.Key("a"), 1, clock)
   301  	testIntents := []roachpb.Intent{
   302  		roachpb.MakeIntent(&txn.TxnMeta, roachpb.Key("a")),
   303  	}
   304  	cases := []testCase{
   305  		{
   306  			intents: testIntents,
   307  			sendFuncs: []sendFunc{
   308  				singlePushTxnSendFunc(t),
   309  				resolveIntentsSendFunc(t),
   310  			},
   311  		},
   312  		{
   313  			intents: testIntents,
   314  			sendFuncs: []sendFunc{
   315  				singlePushTxnSendFunc(t),
   316  				failSendFunc,
   317  			},
   318  		},
   319  		{
   320  			intents: testIntents,
   321  			sendFuncs: []sendFunc{
   322  				failSendFunc,
   323  			},
   324  		},
   325  	}
   326  	for _, c := range cases {
   327  		t.Run("", func(t *testing.T) {
   328  			stopper := stop.NewStopper()
   329  			sf := newSendFuncs(t, c.sendFuncs...)
   330  			cfg := Config{
   331  				Stopper: stopper,
   332  				Clock:   clock,
   333  			}
   334  			ir := newIntentResolverWithSendFuncs(cfg, sf)
   335  			err := ir.CleanupIntentsAsync(context.Background(), c.intents, true)
   336  			sf.drain(t)
   337  			stopper.Stop(context.Background())
   338  			assert.Nil(t, err, "error from CleanupIntentsAsync")
   339  		})
   340  	}
   341  }
   342  
   343  // TestCleanupMultipleIntentsAsync verifies that CleanupIntentsAsync sends the
   344  // expected requests when multiple intents are provided to it.
   345  func TestCleanupMultipleIntentsAsync(t *testing.T) {
   346  	defer leaktest.AfterTest(t)()
   347  	ctx := context.Background()
   348  	clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond)
   349  	txn1 := newTransaction("txn1", roachpb.Key("a"), 1, clock)
   350  	txn2 := newTransaction("txn2", roachpb.Key("c"), 1, clock)
   351  	testIntents := []roachpb.Intent{
   352  		roachpb.MakeIntent(&txn1.TxnMeta, roachpb.Key("a")),
   353  		roachpb.MakeIntent(&txn1.TxnMeta, roachpb.Key("b")),
   354  		roachpb.MakeIntent(&txn2.TxnMeta, roachpb.Key("c")),
   355  		roachpb.MakeIntent(&txn2.TxnMeta, roachpb.Key("d")),
   356  	}
   357  
   358  	// We expect to see a single PushTxn req for all four intents and a
   359  	// ResolveIntent req for each intent. However, because these requests are
   360  	// all async, it's unclear which order these will be issued in. Handle all
   361  	// orders and record the resolved intents.
   362  	var reqs struct {
   363  		syncutil.Mutex
   364  		pushed   []string
   365  		resolved []string
   366  	}
   367  	pushOrResolveFunc := func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) {
   368  		switch ba.Requests[0].GetInner().Method() {
   369  		case roachpb.PushTxn:
   370  			for _, ru := range ba.Requests {
   371  				reqs.Lock()
   372  				reqs.pushed = append(reqs.pushed, string(ru.GetPushTxn().Key))
   373  				reqs.Unlock()
   374  			}
   375  			return pushTxnSendFunc(t, len(ba.Requests))(ba)
   376  		case roachpb.ResolveIntent:
   377  			for _, ru := range ba.Requests {
   378  				reqs.Lock()
   379  				reqs.resolved = append(reqs.resolved, string(ru.GetResolveIntent().Key))
   380  				reqs.Unlock()
   381  			}
   382  			return resolveIntentsSendFunc(t)(ba)
   383  		default:
   384  			return nil, roachpb.NewErrorf("unexpected")
   385  		}
   386  	}
   387  	sf := newSendFuncs(t, repeat(pushOrResolveFunc, 5)...)
   388  
   389  	stopper := stop.NewStopper()
   390  	cfg := Config{
   391  		Stopper: stopper,
   392  		Clock:   clock,
   393  		// Don't let the  intent resolution requests be batched with each other.
   394  		// This would make it harder to determine how to drain sf.
   395  		TestingKnobs: kvserverbase.IntentResolverTestingKnobs{
   396  			MaxIntentResolutionBatchSize: 1,
   397  		},
   398  	}
   399  	ir := newIntentResolverWithSendFuncs(cfg, sf)
   400  	err := ir.CleanupIntentsAsync(ctx, testIntents, false)
   401  	sf.drain(t)
   402  	stopper.Stop(ctx)
   403  	assert.Nil(t, err)
   404  
   405  	// Both txns should be pushed and all four intents should be resolved.
   406  	sort.Strings(reqs.pushed)
   407  	sort.Strings(reqs.resolved)
   408  	assert.Equal(t, []string{"a", "c"}, reqs.pushed)
   409  	assert.Equal(t, []string{"a", "b", "c", "d"}, reqs.resolved)
   410  }
   411  
   412  func repeat(f sendFunc, n int) []sendFunc {
   413  	fns := make([]sendFunc, n)
   414  	for i := range fns {
   415  		fns[i] = f
   416  	}
   417  	return fns
   418  }
   419  
   420  func newSendFuncs(t *testing.T, sf ...sendFunc) *sendFuncs {
   421  	return &sendFuncs{t: t, sendFuncs: sf}
   422  }
   423  
   424  type sendFuncs struct {
   425  	t         *testing.T
   426  	mu        syncutil.Mutex
   427  	sendFuncs []sendFunc
   428  }
   429  
   430  func (sf *sendFuncs) len() int {
   431  	sf.mu.Lock()
   432  	defer sf.mu.Unlock()
   433  	return len(sf.sendFuncs)
   434  }
   435  
   436  func (sf *sendFuncs) pushFrontLocked(f ...sendFunc) {
   437  	sf.sendFuncs = append(f, sf.sendFuncs...)
   438  }
   439  
   440  func (sf *sendFuncs) popLocked() sendFunc {
   441  	if len(sf.sendFuncs) == 0 {
   442  		sf.t.Errorf("No send funcs left!")
   443  	}
   444  	ret := sf.sendFuncs[0]
   445  	sf.sendFuncs = sf.sendFuncs[1:]
   446  	return ret
   447  }
   448  
   449  func (sf *sendFuncs) drain(t *testing.T) {
   450  	testutils.SucceedsSoon(t, func() error {
   451  		if l := sf.len(); l > 0 {
   452  			return errors.Errorf("still have %d funcs to send", l)
   453  		}
   454  		return nil
   455  	})
   456  }
   457  
   458  // TestTxnCleanupIntentsAsyncWithPartialRollback verifies that
   459  // CleanupIntentsAsync properly forwards the ignored seqnum list in
   460  // the resolve intent requests.
   461  func TestCleanupTxnIntentsAsyncWithPartialRollback(t *testing.T) {
   462  	clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond)
   463  	txn := newTransaction("txn", roachpb.Key("a"), 1, clock)
   464  	txn.LockSpans = []roachpb.Span{
   465  		{Key: roachpb.Key("a")},
   466  		{Key: roachpb.Key("b"), EndKey: roachpb.Key("c")},
   467  	}
   468  	txn.IgnoredSeqNums = []enginepb.IgnoredSeqNumRange{{Start: 1, End: 1}}
   469  
   470  	var gotResolveIntent, gotResolveIntentRange int32
   471  	check := func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) {
   472  		for _, r := range ba.Requests {
   473  			if ri, ok := r.GetInner().(*roachpb.ResolveIntentRequest); ok {
   474  				atomic.StoreInt32(&gotResolveIntent, 1)
   475  				if !reflect.DeepEqual(ri.IgnoredSeqNums, txn.IgnoredSeqNums) {
   476  					t.Errorf("expected ignored list %v, got %v", txn.IgnoredSeqNums, ri.IgnoredSeqNums)
   477  				}
   478  			} else if rir, ok := r.GetInner().(*roachpb.ResolveIntentRangeRequest); ok {
   479  				atomic.StoreInt32(&gotResolveIntentRange, 1)
   480  				if !reflect.DeepEqual(rir.IgnoredSeqNums, txn.IgnoredSeqNums) {
   481  					t.Errorf("expected ignored list %v, got %v", txn.IgnoredSeqNums, rir.IgnoredSeqNums)
   482  				}
   483  			}
   484  		}
   485  		return respForResolveIntentBatch(t, ba), nil
   486  	}
   487  	sf := newSendFuncs(t,
   488  		sendFunc(check),
   489  		sendFunc(check),
   490  		gcSendFunc(t),
   491  	)
   492  	stopper := stop.NewStopper()
   493  	defer stopper.Stop(context.Background())
   494  	cfg := Config{
   495  		Stopper: stopper,
   496  		Clock:   clock,
   497  	}
   498  	ir := newIntentResolverWithSendFuncs(cfg, sf)
   499  
   500  	intents := []result.EndTxnIntents{{Txn: txn}}
   501  
   502  	if err := ir.CleanupTxnIntentsAsync(context.Background(), 1, intents, true /*allowAsyncProcessing*/); err != nil {
   503  		t.Fatal(err)
   504  	}
   505  	testutils.SucceedsSoon(t, func() error {
   506  		if atomic.LoadInt32(&gotResolveIntent) == 0 {
   507  			return errors.New("still waiting for resolve intent req")
   508  		}
   509  		if atomic.LoadInt32(&gotResolveIntentRange) == 0 {
   510  			return errors.New("still waiting for resolve intent range req")
   511  		}
   512  		return nil
   513  	})
   514  }
   515  
   516  // TestCleanupTxnIntentsAsync verifies that CleanupTxnIntentsAsync sends the
   517  // expected requests.
   518  func TestCleanupTxnIntentsAsync(t *testing.T) {
   519  	defer leaktest.AfterTest(t)()
   520  	type testCase struct {
   521  		intents   []result.EndTxnIntents
   522  		before    func(*testCase, *IntentResolver) func()
   523  		sendFuncs *sendFuncs
   524  	}
   525  	testEndTxnIntents := []result.EndTxnIntents{
   526  		{
   527  			Txn: &roachpb.Transaction{
   528  				TxnMeta: enginepb.TxnMeta{
   529  					ID:           uuid.MakeV4(),
   530  					MinTimestamp: hlc.Timestamp{WallTime: 123},
   531  				},
   532  				LockSpans: []roachpb.Span{
   533  					{Key: roachpb.Key("a")},
   534  					{Key: roachpb.Key("b")},
   535  					{Key: roachpb.Key("c"), EndKey: roachpb.Key("d")},
   536  					{Key: roachpb.Key("e"), EndKey: roachpb.Key("f")},
   537  				},
   538  			},
   539  		},
   540  	}
   541  
   542  	cases := []testCase{
   543  		{
   544  			intents:   testEndTxnIntents,
   545  			sendFuncs: newSendFuncs(t),
   546  			before: func(tc *testCase, ir *IntentResolver) func() {
   547  				_, f := ir.lockInFlightTxnCleanup(context.Background(), tc.intents[0].Txn.ID)
   548  				return f
   549  			},
   550  		},
   551  		{
   552  			intents: testEndTxnIntents,
   553  			sendFuncs: func() *sendFuncs {
   554  				s := newSendFuncs(t)
   555  				s.pushFrontLocked(
   556  					resolveIntentsSendFuncs(s, 4, 2),
   557  					gcSendFunc(t),
   558  				)
   559  				return s
   560  			}(),
   561  		},
   562  	}
   563  
   564  	for _, c := range cases {
   565  		t.Run("", func(t *testing.T) {
   566  			stopper := stop.NewStopper()
   567  			clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond)
   568  			cfg := Config{
   569  				Stopper: stopper,
   570  				Clock:   clock,
   571  			}
   572  			ir := newIntentResolverWithSendFuncs(cfg, c.sendFuncs)
   573  			if c.before != nil {
   574  				defer c.before(&c, ir)()
   575  			}
   576  			err := ir.CleanupTxnIntentsAsync(context.Background(), 1, c.intents, false)
   577  			testutils.SucceedsSoon(t, func() error {
   578  				if left := c.sendFuncs.len(); left != 0 {
   579  					return fmt.Errorf("still waiting for %d calls", left)
   580  				}
   581  				return nil
   582  			})
   583  			stopper.Stop(context.Background())
   584  			assert.Nil(t, err)
   585  		})
   586  	}
   587  }
   588  
   589  // TestCleanupMultipleTxnIntentsAsync verifies that CleanupTxnIntentsAsync sends
   590  // the expected requests when multiple EndTxnIntents are provided to it.
   591  func TestCleanupMultipleTxnIntentsAsync(t *testing.T) {
   592  	defer leaktest.AfterTest(t)()
   593  	ctx := context.Background()
   594  	clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond)
   595  	txn1 := newTransaction("txn1", roachpb.Key("a"), 1, clock)
   596  	txn2 := newTransaction("txn2", roachpb.Key("c"), 1, clock)
   597  	testEndTxnIntents := []result.EndTxnIntents{
   598  		{
   599  			Txn: &roachpb.Transaction{
   600  				TxnMeta: txn1.TxnMeta,
   601  				LockSpans: []roachpb.Span{
   602  					{Key: roachpb.Key("a")},
   603  					{Key: roachpb.Key("b")},
   604  					{Key: roachpb.Key("c"), EndKey: roachpb.Key("d")},
   605  				},
   606  			},
   607  		},
   608  		{
   609  			Txn: &roachpb.Transaction{
   610  				TxnMeta: txn2.TxnMeta,
   611  				LockSpans: []roachpb.Span{
   612  					{Key: roachpb.Key("e")},
   613  					{Key: roachpb.Key("f")},
   614  					{Key: roachpb.Key("g"), EndKey: roachpb.Key("h")},
   615  				},
   616  			},
   617  		},
   618  	}
   619  
   620  	// We expect to see a ResolveIntent req for each intent and a GC req for
   621  	// each txn. However, because these requests are all async, it's unclear
   622  	// which order these will be issued in. Handle all orders and record the
   623  	// GCed transaction records.
   624  	var reqs struct {
   625  		syncutil.Mutex
   626  		resolved []string
   627  		gced     []string
   628  	}
   629  	resolveOrGCFunc := func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) {
   630  		if len(ba.Requests) != 1 {
   631  			return nil, roachpb.NewErrorf("unexpected")
   632  		}
   633  		ru := ba.Requests[0]
   634  		switch ru.GetInner().Method() {
   635  		case roachpb.ResolveIntent:
   636  			reqs.Lock()
   637  			reqs.resolved = append(reqs.resolved, string(ru.GetResolveIntent().Key))
   638  			reqs.Unlock()
   639  			return resolveIntentsSendFunc(t)(ba)
   640  		case roachpb.ResolveIntentRange:
   641  			reqs.Lock()
   642  			req := ru.GetResolveIntentRange()
   643  			reqs.resolved = append(reqs.resolved,
   644  				fmt.Sprintf("%s-%s", string(req.Key), string(req.EndKey)))
   645  			reqs.Unlock()
   646  			return resolveIntentsSendFunc(t)(ba)
   647  		case roachpb.GC:
   648  			reqs.Lock()
   649  			reqs.gced = append(reqs.gced, string(ru.GetGc().Key))
   650  			reqs.Unlock()
   651  			return gcSendFunc(t)(ba)
   652  		default:
   653  			return nil, roachpb.NewErrorf("unexpected")
   654  		}
   655  	}
   656  	sf := newSendFuncs(t, repeat(resolveOrGCFunc, 8)...)
   657  
   658  	stopper := stop.NewStopper()
   659  	cfg := Config{
   660  		Stopper: stopper,
   661  		Clock:   clock,
   662  		// Don't let the transaction record GC requests or the intent resolution
   663  		// requests be batched with each other. This would make it harder to
   664  		// determine how to drain sf.
   665  		TestingKnobs: kvserverbase.IntentResolverTestingKnobs{
   666  			MaxGCBatchSize:               1,
   667  			MaxIntentResolutionBatchSize: 1,
   668  		},
   669  	}
   670  	ir := newIntentResolverWithSendFuncs(cfg, sf)
   671  	err := ir.CleanupTxnIntentsAsync(ctx, 1, testEndTxnIntents, false)
   672  	sf.drain(t)
   673  	stopper.Stop(ctx)
   674  	assert.Nil(t, err)
   675  
   676  	// All four intents should be resolved and both txn records should be GCed.
   677  	sort.Strings(reqs.resolved)
   678  	sort.Strings(reqs.gced)
   679  	assert.Equal(t, []string{"a", "b", "c-d", "e", "f", "g-h"}, reqs.resolved)
   680  	assert.Equal(t, []string{"a", "c"}, reqs.gced)
   681  }
   682  
   683  // TestCleanupIntents verifies that CleanupIntents sends the expected requests
   684  // and returns the appropriate errors.
   685  func TestCleanupIntents(t *testing.T) {
   686  	defer leaktest.AfterTest(t)()
   687  	clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond)
   688  	txn := newTransaction("txn", roachpb.Key("a"), roachpb.MinUserPriority, clock)
   689  	// Set txn.ID to a very small value so it's sorted deterministically first.
   690  	txn.ID = uuid.UUID{15: 0x01}
   691  	testIntents := []roachpb.Intent{
   692  		roachpb.MakeIntent(&txn.TxnMeta, roachpb.Key("a")),
   693  	}
   694  	type testCase struct {
   695  		intents     []roachpb.Intent
   696  		sendFuncs   *sendFuncs
   697  		expectedErr bool
   698  		expectedNum int
   699  		cfg         Config
   700  	}
   701  	cases := []testCase{
   702  		{
   703  			intents: testIntents,
   704  			sendFuncs: newSendFuncs(t,
   705  				singlePushTxnSendFunc(t),
   706  				resolveIntentsSendFunc(t),
   707  			),
   708  			expectedNum: 1,
   709  		},
   710  		{
   711  			intents: testIntents,
   712  			sendFuncs: newSendFuncs(t,
   713  				failSendFunc,
   714  			),
   715  			expectedErr: true,
   716  		},
   717  		{
   718  			intents: append(makeTxnIntents(t, clock, 3*intentResolverBatchSize),
   719  				// Three intents with the same transaction will only attempt to push the
   720  				// txn 1 time. Hence 3 full batches plus 1 extra.
   721  				testIntents[0], testIntents[0], testIntents[0]),
   722  			sendFuncs: func() *sendFuncs {
   723  				sf := newSendFuncs(t)
   724  				sf.pushFrontLocked( // don't need to lock
   725  					pushTxnSendFuncs(sf, intentResolverBatchSize),
   726  					resolveIntentsSendFuncs(sf, 102 /* numIntents */, 2 /* minNumReqs */),
   727  					pushTxnSendFuncs(sf, intentResolverBatchSize),
   728  					resolveIntentsSendFuncs(sf, 100 /* numIntents */, 1 /* minNumReqs */),
   729  					pushTxnSendFuncs(sf, intentResolverBatchSize),
   730  					resolveIntentsSendFuncs(sf, 100 /* numIntents */, 1 /* minNumReqs */),
   731  					pushTxnSendFuncs(sf, 1),
   732  					resolveIntentsSendFuncs(sf, 1 /* numIntents */, 1 /* minNumReqs */),
   733  				)
   734  				return sf
   735  			}(),
   736  			expectedNum: 3*intentResolverBatchSize + 3,
   737  			cfg: Config{
   738  				MaxIntentResolutionBatchWait: -1, // disabled
   739  				MaxIntentResolutionBatchIdle: 1 * time.Microsecond,
   740  			},
   741  		},
   742  	}
   743  	stopper := stop.NewStopper()
   744  	defer stopper.Stop(context.Background())
   745  	for _, c := range cases {
   746  		t.Run("", func(t *testing.T) {
   747  			c.cfg.Stopper = stopper
   748  			c.cfg.Clock = clock
   749  			ir := newIntentResolverWithSendFuncs(c.cfg, c.sendFuncs)
   750  			num, err := ir.CleanupIntents(context.Background(), c.intents, clock.Now(), roachpb.PUSH_ABORT)
   751  			assert.Equal(t, num, c.expectedNum, "number of resolved intents")
   752  			assert.Equal(t, err != nil, c.expectedErr, "error during CleanupIntents: %v", err)
   753  		})
   754  	}
   755  }
   756  
   757  func newTransaction(
   758  	name string, baseKey roachpb.Key, userPriority roachpb.UserPriority, clock *hlc.Clock,
   759  ) *roachpb.Transaction {
   760  	var offset int64
   761  	var now hlc.Timestamp
   762  	if clock != nil {
   763  		offset = clock.MaxOffset().Nanoseconds()
   764  		now = clock.Now()
   765  	}
   766  	txn := roachpb.MakeTransaction(name, baseKey, userPriority, now, offset)
   767  	return &txn
   768  }
   769  
   770  // makeTxnIntents creates a slice of Intent which each have a unique txn.
   771  func makeTxnIntents(t *testing.T, clock *hlc.Clock, numIntents int) []roachpb.Intent {
   772  	ret := make([]roachpb.Intent, 0, numIntents)
   773  	for i := 0; i < numIntents; i++ {
   774  		txn := newTransaction("test", roachpb.Key("a"), 1, clock)
   775  		ret = append(ret,
   776  			roachpb.MakeIntent(&txn.TxnMeta, txn.Key))
   777  	}
   778  	return ret
   779  }
   780  
   781  // sendFunc is a function used to control behavior for a specific request that
   782  // the IntentResolver tries to send. They are used in conjunction with the below
   783  // function to create an IntentResolver with a slice of sendFuncs.
   784  // A library of useful sendFuncs are defined below.
   785  type sendFunc func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error)
   786  
   787  func newIntentResolverWithSendFuncs(c Config, sf *sendFuncs) *IntentResolver {
   788  	txnSenderFactory := kv.NonTransactionalFactoryFunc(
   789  		func(_ context.Context, ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) {
   790  			sf.mu.Lock()
   791  			defer sf.mu.Unlock()
   792  			f := sf.popLocked()
   793  			return f(ba)
   794  		})
   795  	db := kv.NewDB(log.AmbientContext{
   796  		Tracer: tracing.NewTracer(),
   797  	}, txnSenderFactory, c.Clock)
   798  	c.DB = db
   799  	c.MaxGCBatchWait = time.Nanosecond
   800  	return New(c)
   801  }
   802  
   803  // pushTxnSendFuncs allows the pushing of N txns across several invocations.
   804  func pushTxnSendFuncs(sf *sendFuncs, N int) sendFunc {
   805  	toPush := int64(N)
   806  	var f sendFunc
   807  	f = func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) {
   808  		if remaining := atomic.LoadInt64(&toPush); len(ba.Requests) > int(remaining) {
   809  			sf.t.Errorf("expected at most %d PushTxnRequests in batch, got %d",
   810  				remaining, len(ba.Requests))
   811  		}
   812  		nowRemaining := atomic.AddInt64(&toPush, -1*int64(len(ba.Requests)))
   813  		if nowRemaining > 0 {
   814  			sf.pushFrontLocked(f)
   815  		}
   816  		return respForPushTxnBatch(sf.t, ba), nil
   817  	}
   818  	return f
   819  }
   820  
   821  func pushTxnSendFunc(t *testing.T, numPushes int) sendFunc {
   822  	return func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) {
   823  		if len(ba.Requests) != numPushes {
   824  			t.Errorf("expected %d PushTxnRequests in batch, got %d",
   825  				numPushes, len(ba.Requests))
   826  		}
   827  		return respForPushTxnBatch(t, ba), nil
   828  	}
   829  }
   830  
   831  func singlePushTxnSendFunc(t *testing.T) sendFunc {
   832  	return pushTxnSendFunc(t, 1)
   833  }
   834  
   835  func resolveIntentsSendFuncs(sf *sendFuncs, numIntents int, minRequests int) sendFunc {
   836  	toResolve := int64(numIntents)
   837  	reqsSeen := int64(0)
   838  	var f sendFunc
   839  	f = func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) {
   840  		if remaining := atomic.LoadInt64(&toResolve); len(ba.Requests) > int(remaining) {
   841  			sf.t.Errorf("expected at most %d ResolveIntentRequests in batch, got %d",
   842  				remaining, len(ba.Requests))
   843  		}
   844  		nowRemaining := atomic.AddInt64(&toResolve, -1*int64(len(ba.Requests)))
   845  		seen := atomic.AddInt64(&reqsSeen, 1)
   846  		if nowRemaining > 0 {
   847  			sf.pushFrontLocked(f)
   848  		} else if seen < int64(minRequests) {
   849  			sf.t.Errorf("expected at least %d requests to resolve %d intents, only saw %d",
   850  				minRequests, numIntents, seen)
   851  		}
   852  		return respForResolveIntentBatch(sf.t, ba), nil
   853  	}
   854  	return f
   855  }
   856  
   857  func resolveIntentsSendFunc(t *testing.T) sendFunc {
   858  	return func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) {
   859  		return respForResolveIntentBatch(t, ba), nil
   860  	}
   861  }
   862  
   863  func failSendFunc(roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) {
   864  	return nil, roachpb.NewError(fmt.Errorf("boom"))
   865  }
   866  
   867  func gcSendFunc(t *testing.T) sendFunc {
   868  	return func(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) {
   869  		resp := &roachpb.BatchResponse{}
   870  		for _, r := range ba.Requests {
   871  			if _, ok := r.GetInner().(*roachpb.GCRequest); !ok {
   872  				t.Errorf("Unexpected request type %T, expected GCRequest", r.GetInner())
   873  			}
   874  			resp.Add(&roachpb.GCResponse{})
   875  		}
   876  		return resp, nil
   877  	}
   878  }
   879  
   880  func respForPushTxnBatch(t *testing.T, ba roachpb.BatchRequest) *roachpb.BatchResponse {
   881  	resp := &roachpb.BatchResponse{}
   882  	for _, r := range ba.Requests {
   883  		var txn enginepb.TxnMeta
   884  		if req, ok := r.GetInner().(*roachpb.PushTxnRequest); ok {
   885  			txn = req.PusheeTxn
   886  		} else {
   887  			t.Errorf("Unexpected request type %T, expected PushTxnRequest", r.GetInner())
   888  		}
   889  		resp.Add(&roachpb.PushTxnResponse{
   890  			PusheeTxn: roachpb.Transaction{
   891  				Status:  roachpb.ABORTED,
   892  				TxnMeta: txn,
   893  			},
   894  		})
   895  	}
   896  	return resp
   897  }
   898  
   899  func respForResolveIntentBatch(t *testing.T, ba roachpb.BatchRequest) *roachpb.BatchResponse {
   900  	resp := &roachpb.BatchResponse{}
   901  	for _, r := range ba.Requests {
   902  		if _, ok := r.GetInner().(*roachpb.ResolveIntentRequest); ok {
   903  			resp.Add(&roachpb.ResolveIntentResponse{})
   904  		} else if _, ok := r.GetInner().(*roachpb.ResolveIntentRangeRequest); ok {
   905  			resp.Add(&roachpb.ResolveIntentRangeResponse{})
   906  		} else {
   907  			t.Errorf("Unexpected request in batch for intent resolution: %T", r.GetInner())
   908  		}
   909  	}
   910  	return resp
   911  }