github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/intentresolver/intent_resolver.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  	"bytes"
    15  	"context"
    16  	"sort"
    17  	"time"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/internal/client/requestbatcher"
    20  	"github.com/cockroachdb/cockroach/pkg/keys"
    21  	"github.com/cockroachdb/cockroach/pkg/kv"
    22  	"github.com/cockroachdb/cockroach/pkg/kv/kvbase"
    23  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result"
    24  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase"
    25  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/txnwait"
    26  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    27  	"github.com/cockroachdb/cockroach/pkg/storage/enginepb"
    28  	"github.com/cockroachdb/cockroach/pkg/util/contextutil"
    29  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    30  	"github.com/cockroachdb/cockroach/pkg/util/log"
    31  	"github.com/cockroachdb/cockroach/pkg/util/quotapool"
    32  	"github.com/cockroachdb/cockroach/pkg/util/stop"
    33  	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
    34  	"github.com/cockroachdb/cockroach/pkg/util/uuid"
    35  	"github.com/cockroachdb/errors"
    36  )
    37  
    38  const (
    39  	// defaultTaskLimit is the maximum number of asynchronous tasks
    40  	// that may be started by intentResolver. When this limit is reached
    41  	// asynchronous tasks will start to block to apply backpressure.  This is a
    42  	// last line of defense against issues like #4925.
    43  	// TODO(bdarnell): how to determine best value?
    44  	defaultTaskLimit = 1000
    45  
    46  	// asyncIntentResolutionTimeout is the timeout when processing a group of
    47  	// intents asynchronously. The timeout prevents async intent resolution from
    48  	// getting stuck. Since processing intents is best effort, we'd rather give
    49  	// up than wait too long (this helps avoid deadlocks during test shutdown).
    50  	asyncIntentResolutionTimeout = 30 * time.Second
    51  
    52  	// gcBatchSize is the maximum number of transaction records that will be
    53  	// GCed in a single batch. Batches that span many ranges (which is possible
    54  	// for the transaction records that spans many ranges) will be split into
    55  	// many batches by the DistSender.
    56  	gcBatchSize = 1024
    57  
    58  	// intentResolverBatchSize is the maximum number of intents that will be
    59  	// resolved in a single batch. Batches that span many ranges (which is
    60  	// possible for the commit of a transaction that spans many ranges) will be
    61  	// split into many batches by the DistSender.
    62  	// TODO(ajwerner): justify this value
    63  	intentResolverBatchSize = 100
    64  
    65  	// cleanupIntentsTxnsPerBatch is the number of transactions whose
    66  	// corresponding intents will be resolved at a time. Intents are batched
    67  	// by transaction to avoid timeouts while resolving intents and ensure that
    68  	// progress is made.
    69  	cleanupIntentsTxnsPerBatch = 100
    70  
    71  	// defaultGCBatchIdle is the default duration which the gc request batcher
    72  	// will wait between requests for a range before sending it.
    73  	defaultGCBatchIdle = -1 // disabled
    74  
    75  	// defaultGCBatchWait is the default duration which the gc request batcher
    76  	// will wait between requests for a range before sending it.
    77  	defaultGCBatchWait = time.Second
    78  
    79  	// intentResolutionBatchWait is used to configure the RequestBatcher which
    80  	// batches intent resolution requests across transactions. Intent resolution
    81  	// needs to occur in a relatively short period of time after the completion
    82  	// of a transaction in order to minimize the contention footprint of the write
    83  	// for other contending reads or writes. The chosen value was selected based
    84  	// on some light experimentation to ensure that performance does not degrade
    85  	// in the face of highly contended workloads.
    86  	defaultIntentResolutionBatchWait = 10 * time.Millisecond
    87  
    88  	// intentResolutionBatchIdle is similar to the above setting but is used when
    89  	// when no additional traffic hits the batch.
    90  	defaultIntentResolutionBatchIdle = 5 * time.Millisecond
    91  )
    92  
    93  // Config contains the dependencies to construct an IntentResolver.
    94  type Config struct {
    95  	Clock                *hlc.Clock
    96  	DB                   *kv.DB
    97  	Stopper              *stop.Stopper
    98  	AmbientCtx           log.AmbientContext
    99  	TestingKnobs         kvserverbase.IntentResolverTestingKnobs
   100  	RangeDescriptorCache kvbase.RangeDescriptorCache
   101  
   102  	TaskLimit                    int
   103  	MaxGCBatchWait               time.Duration
   104  	MaxGCBatchIdle               time.Duration
   105  	MaxIntentResolutionBatchWait time.Duration
   106  	MaxIntentResolutionBatchIdle time.Duration
   107  }
   108  
   109  // IntentResolver manages the process of pushing transactions and
   110  // resolving intents.
   111  type IntentResolver struct {
   112  	Metrics Metrics
   113  
   114  	clock        *hlc.Clock
   115  	db           *kv.DB
   116  	stopper      *stop.Stopper
   117  	testingKnobs kvserverbase.IntentResolverTestingKnobs
   118  	ambientCtx   log.AmbientContext
   119  	sem          *quotapool.IntPool // semaphore to limit async goroutines
   120  
   121  	rdc kvbase.RangeDescriptorCache
   122  
   123  	gcBatcher      *requestbatcher.RequestBatcher
   124  	irBatcher      *requestbatcher.RequestBatcher
   125  	irRangeBatcher *requestbatcher.RequestBatcher
   126  
   127  	mu struct {
   128  		syncutil.Mutex
   129  		// Map from txn ID being pushed to a refcount of requests waiting on the
   130  		// push.
   131  		inFlightPushes map[uuid.UUID]int
   132  		// Set of txn IDs whose list of lock spans are being resolved. Note
   133  		// that this pertains only to EndTxn-style lock cleanups, whether
   134  		// called directly after EndTxn evaluation or during GC of txn spans.
   135  		inFlightTxnCleanups map[uuid.UUID]struct{}
   136  	}
   137  	every log.EveryN
   138  }
   139  
   140  func setConfigDefaults(c *Config) {
   141  	if c.TaskLimit == 0 {
   142  		c.TaskLimit = defaultTaskLimit
   143  	}
   144  	if c.TaskLimit == -1 || c.TestingKnobs.ForceSyncIntentResolution {
   145  		c.TaskLimit = 0
   146  	}
   147  	if c.MaxGCBatchIdle == 0 {
   148  		c.MaxGCBatchIdle = defaultGCBatchIdle
   149  	}
   150  	if c.MaxGCBatchWait == 0 {
   151  		c.MaxGCBatchWait = defaultGCBatchWait
   152  	}
   153  	if c.MaxIntentResolutionBatchIdle == 0 {
   154  		c.MaxIntentResolutionBatchIdle = defaultIntentResolutionBatchIdle
   155  	}
   156  	if c.MaxIntentResolutionBatchWait == 0 {
   157  		c.MaxIntentResolutionBatchWait = defaultIntentResolutionBatchWait
   158  	}
   159  	if c.RangeDescriptorCache == nil {
   160  		c.RangeDescriptorCache = nopRangeDescriptorCache{}
   161  	}
   162  }
   163  
   164  type nopRangeDescriptorCache struct{}
   165  
   166  var zeroRangeDescriptor = &roachpb.RangeDescriptor{}
   167  
   168  func (nrdc nopRangeDescriptorCache) LookupRangeDescriptor(
   169  	ctx context.Context, key roachpb.RKey,
   170  ) (*roachpb.RangeDescriptor, error) {
   171  	return zeroRangeDescriptor, nil
   172  }
   173  
   174  // New creates an new IntentResolver.
   175  func New(c Config) *IntentResolver {
   176  	setConfigDefaults(&c)
   177  	ir := &IntentResolver{
   178  		clock:        c.Clock,
   179  		db:           c.DB,
   180  		stopper:      c.Stopper,
   181  		sem:          quotapool.NewIntPool("intent resolver", uint64(c.TaskLimit)),
   182  		every:        log.Every(time.Minute),
   183  		Metrics:      makeMetrics(),
   184  		rdc:          c.RangeDescriptorCache,
   185  		testingKnobs: c.TestingKnobs,
   186  	}
   187  	c.Stopper.AddCloser(ir.sem.Closer("stopper"))
   188  	ir.mu.inFlightPushes = map[uuid.UUID]int{}
   189  	ir.mu.inFlightTxnCleanups = map[uuid.UUID]struct{}{}
   190  	gcBatchSize := gcBatchSize
   191  	if c.TestingKnobs.MaxIntentResolutionBatchSize > 0 {
   192  		gcBatchSize = c.TestingKnobs.MaxGCBatchSize
   193  	}
   194  	ir.gcBatcher = requestbatcher.New(requestbatcher.Config{
   195  		Name:            "intent_resolver_gc_batcher",
   196  		MaxMsgsPerBatch: gcBatchSize,
   197  		MaxWait:         c.MaxGCBatchWait,
   198  		MaxIdle:         c.MaxGCBatchIdle,
   199  		Stopper:         c.Stopper,
   200  		Sender:          c.DB.NonTransactionalSender(),
   201  	})
   202  	intentResolutionBatchSize := intentResolverBatchSize
   203  	if c.TestingKnobs.MaxIntentResolutionBatchSize > 0 {
   204  		intentResolutionBatchSize = c.TestingKnobs.MaxIntentResolutionBatchSize
   205  	}
   206  	ir.irBatcher = requestbatcher.New(requestbatcher.Config{
   207  		Name:            "intent_resolver_ir_batcher",
   208  		MaxMsgsPerBatch: intentResolutionBatchSize,
   209  		MaxWait:         c.MaxIntentResolutionBatchWait,
   210  		MaxIdle:         c.MaxIntentResolutionBatchIdle,
   211  		Stopper:         c.Stopper,
   212  		Sender:          c.DB.NonTransactionalSender(),
   213  	})
   214  	ir.irRangeBatcher = requestbatcher.New(requestbatcher.Config{
   215  		Name:            "intent_resolver_ir_range_batcher",
   216  		MaxMsgsPerBatch: intentResolutionBatchSize,
   217  		// NOTE: Allow each request sent in a batch to touch up to twice as
   218  		// many keys as messages in the batch to avoid pagination if only a
   219  		// few ResolveIntentRange requests touch multiple intents.
   220  		MaxKeysPerBatchReq: 2 * intentResolverBatchSize,
   221  		MaxWait:            c.MaxIntentResolutionBatchWait,
   222  		MaxIdle:            c.MaxIntentResolutionBatchIdle,
   223  		Stopper:            c.Stopper,
   224  		Sender:             c.DB.NonTransactionalSender(),
   225  	})
   226  	return ir
   227  }
   228  
   229  func getPusherTxn(h roachpb.Header) roachpb.Transaction {
   230  	// If the txn is nil, we communicate a priority by sending an empty
   231  	// txn with only the priority set. This is official usage of PushTxn.
   232  	txn := h.Txn
   233  	if txn == nil {
   234  		txn = &roachpb.Transaction{
   235  			TxnMeta: enginepb.TxnMeta{
   236  				Priority: roachpb.MakePriority(h.UserPriority),
   237  			},
   238  		}
   239  	}
   240  	return *txn
   241  }
   242  
   243  // updateIntentTxnStatus takes a slice of intents and a set of pushed
   244  // transactions (like returned from MaybePushTransactions) and updates
   245  // each intent with its corresponding TxnMeta and Status.
   246  // resultSlice is an optional value to allow the caller to preallocate
   247  // the returned intent slice.
   248  func updateIntentTxnStatus(
   249  	ctx context.Context,
   250  	pushedTxns map[uuid.UUID]*roachpb.Transaction,
   251  	intents []roachpb.Intent,
   252  	skipIfInFlight bool,
   253  	results []roachpb.LockUpdate,
   254  ) []roachpb.LockUpdate {
   255  	for _, intent := range intents {
   256  		pushee, ok := pushedTxns[intent.Txn.ID]
   257  		if !ok {
   258  			// The intent was not pushed.
   259  			if !skipIfInFlight {
   260  				log.Fatalf(ctx, "no PushTxn response for intent %+v", intent)
   261  			}
   262  			// It must have been skipped.
   263  			continue
   264  		}
   265  		up := roachpb.MakeLockUpdate(pushee, roachpb.Span{Key: intent.Key})
   266  		results = append(results, up)
   267  	}
   268  	return results
   269  }
   270  
   271  // PushTransaction takes a transaction and pushes its record using the specified
   272  // push type and request header. It returns the transaction proto corresponding
   273  // to the pushed transaction.
   274  func (ir *IntentResolver) PushTransaction(
   275  	ctx context.Context, pushTxn *enginepb.TxnMeta, h roachpb.Header, pushType roachpb.PushTxnType,
   276  ) (*roachpb.Transaction, *roachpb.Error) {
   277  	pushTxns := make(map[uuid.UUID]*enginepb.TxnMeta, 1)
   278  	pushTxns[pushTxn.ID] = pushTxn
   279  	pushedTxns, pErr := ir.MaybePushTransactions(ctx, pushTxns, h, pushType, false /* skipIfInFlight */)
   280  	if pErr != nil {
   281  		return nil, pErr
   282  	}
   283  	pushedTxn, ok := pushedTxns[pushTxn.ID]
   284  	if !ok {
   285  		log.Fatalf(ctx, "missing PushTxn responses for %s", pushTxn)
   286  	}
   287  	return pushedTxn, nil
   288  }
   289  
   290  // MaybePushTransactions tries to push the conflicting transaction(s):
   291  // either moving their timestamp forward on a read/write conflict, aborting
   292  // it on a write/write conflict, or doing nothing if the transaction is no
   293  // longer pending.
   294  //
   295  // Returns a set of transaction protos who correspond to the pushed
   296  // transactions and whose intents can now be resolved, and an error.
   297  //
   298  // If skipIfInFlight is true, then no PushTxns will be sent and no intents
   299  // will be returned for any transaction for which there is another push in
   300  // progress. This should only be used by callers who are not relying on the
   301  // side effect of a push (i.e. only pushType==PUSH_TOUCH), and who also
   302  // don't need to synchronize with the resolution of those intents (e.g.
   303  // asynchronous resolutions of intents skipped on inconsistent reads).
   304  //
   305  // Callers are involved with
   306  // a) conflict resolution for commands being executed at the Store with the
   307  //    client waiting,
   308  // b) resolving intents encountered during inconsistent operations, and
   309  // c) resolving intents upon EndTxn which are not local to the given range.
   310  //    This is the only path in which the transaction is going to be in
   311  //    non-pending state and doesn't require a push.
   312  func (ir *IntentResolver) MaybePushTransactions(
   313  	ctx context.Context,
   314  	pushTxns map[uuid.UUID]*enginepb.TxnMeta,
   315  	h roachpb.Header,
   316  	pushType roachpb.PushTxnType,
   317  	skipIfInFlight bool,
   318  ) (map[uuid.UUID]*roachpb.Transaction, *roachpb.Error) {
   319  	// Decide which transactions to push and which to ignore because
   320  	// of other in-flight requests. For those transactions that we
   321  	// will be pushing, increment their ref count in the in-flight
   322  	// pushes map.
   323  	ir.mu.Lock()
   324  	for txnID := range pushTxns {
   325  		_, pushTxnInFlight := ir.mu.inFlightPushes[txnID]
   326  		if pushTxnInFlight && skipIfInFlight {
   327  			// Another goroutine is working on this transaction so we can
   328  			// skip it.
   329  			if log.V(1) {
   330  				log.Infof(ctx, "skipping PushTxn for %s; attempt already in flight", txnID)
   331  			}
   332  			delete(pushTxns, txnID)
   333  		} else {
   334  			ir.mu.inFlightPushes[txnID]++
   335  		}
   336  	}
   337  	cleanupInFlightPushes := func() {
   338  		ir.mu.Lock()
   339  		for txnID := range pushTxns {
   340  			ir.mu.inFlightPushes[txnID]--
   341  			if ir.mu.inFlightPushes[txnID] == 0 {
   342  				delete(ir.mu.inFlightPushes, txnID)
   343  			}
   344  		}
   345  		ir.mu.Unlock()
   346  	}
   347  	ir.mu.Unlock()
   348  	if len(pushTxns) == 0 {
   349  		return nil, nil
   350  	}
   351  
   352  	pusherTxn := getPusherTxn(h)
   353  	log.Eventf(ctx, "pushing %d transaction(s)", len(pushTxns))
   354  
   355  	// Attempt to push the transaction(s).
   356  	b := &kv.Batch{}
   357  	b.Header.Timestamp = ir.clock.Now()
   358  	for _, pushTxn := range pushTxns {
   359  		b.AddRawRequest(&roachpb.PushTxnRequest{
   360  			RequestHeader: roachpb.RequestHeader{
   361  				Key: pushTxn.Key,
   362  			},
   363  			PusherTxn: pusherTxn,
   364  			PusheeTxn: *pushTxn,
   365  			PushTo:    h.Timestamp.Next(),
   366  			PushType:  pushType,
   367  		})
   368  	}
   369  	err := ir.db.Run(ctx, b)
   370  	cleanupInFlightPushes()
   371  	if err != nil {
   372  		return nil, b.MustPErr()
   373  	}
   374  
   375  	br := b.RawResponse()
   376  	pushedTxns := make(map[uuid.UUID]*roachpb.Transaction, len(br.Responses))
   377  	for _, resp := range br.Responses {
   378  		txn := &resp.GetInner().(*roachpb.PushTxnResponse).PusheeTxn
   379  		if _, ok := pushedTxns[txn.ID]; ok {
   380  			log.Fatalf(ctx, "have two PushTxn responses for %s", txn.ID)
   381  		}
   382  		pushedTxns[txn.ID] = txn
   383  		log.Eventf(ctx, "%s is now %s", txn.ID, txn.Status)
   384  	}
   385  	return pushedTxns, nil
   386  }
   387  
   388  // runAsyncTask semi-synchronously runs a generic task function. If
   389  // there is spare capacity in the limited async task semaphore, it's
   390  // run asynchronously; otherwise, it's run synchronously if
   391  // allowSyncProcessing is true; if false, an error is returned.
   392  func (ir *IntentResolver) runAsyncTask(
   393  	ctx context.Context, allowSyncProcessing bool, taskFn func(context.Context),
   394  ) error {
   395  	if ir.testingKnobs.DisableAsyncIntentResolution {
   396  		return errors.New("intents not processed as async resolution is disabled")
   397  	}
   398  	err := ir.stopper.RunLimitedAsyncTask(
   399  		// If we've successfully launched a background task, dissociate
   400  		// this work from our caller's context and timeout.
   401  		ir.ambientCtx.AnnotateCtx(context.Background()),
   402  		"storage.IntentResolver: processing intents",
   403  		ir.sem,
   404  		false, /* wait */
   405  		taskFn,
   406  	)
   407  	if err != nil {
   408  		if errors.Is(err, stop.ErrThrottled) {
   409  			ir.Metrics.IntentResolverAsyncThrottled.Inc(1)
   410  			if allowSyncProcessing {
   411  				// A limited task was not available. Rather than waiting for
   412  				// one, we reuse the current goroutine.
   413  				taskFn(ctx)
   414  				return nil
   415  			}
   416  		}
   417  		return errors.Wrapf(err, "during async intent resolution")
   418  	}
   419  	return nil
   420  }
   421  
   422  // CleanupIntentsAsync asynchronously processes intents which were
   423  // encountered during another command but did not interfere with the
   424  // execution of that command. This occurs during inconsistent
   425  // reads.
   426  func (ir *IntentResolver) CleanupIntentsAsync(
   427  	ctx context.Context, intents []roachpb.Intent, allowSyncProcessing bool,
   428  ) error {
   429  	if len(intents) == 0 {
   430  		return nil
   431  	}
   432  	now := ir.clock.Now()
   433  	return ir.runAsyncTask(ctx, allowSyncProcessing, func(ctx context.Context) {
   434  		err := contextutil.RunWithTimeout(ctx, "async intent resolution",
   435  			asyncIntentResolutionTimeout, func(ctx context.Context) error {
   436  				_, err := ir.CleanupIntents(ctx, intents, now, roachpb.PUSH_TOUCH)
   437  				return err
   438  			})
   439  		if err != nil && ir.every.ShouldLog() {
   440  			log.Warningf(ctx, "%v", err)
   441  		}
   442  	})
   443  }
   444  
   445  // CleanupIntents processes a collection of intents by pushing each
   446  // implicated transaction using the specified pushType. Intents
   447  // belonging to non-pending transactions after the push are resolved.
   448  // On success, returns the number of resolved intents. On error, a
   449  // subset of the intents may have been resolved, but zero will be
   450  // returned.
   451  func (ir *IntentResolver) CleanupIntents(
   452  	ctx context.Context, intents []roachpb.Intent, now hlc.Timestamp, pushType roachpb.PushTxnType,
   453  ) (int, error) {
   454  	h := roachpb.Header{Timestamp: now}
   455  
   456  	// All transactions in MaybePushTransactions will be sent in a single batch.
   457  	// In order to ensure that progress is made, we want to ensure that this
   458  	// batch does not become too big as to time out due to a deadline set above
   459  	// this call. If the attempt to push intents times out before any intents
   460  	// have been resolved, no progress is made. Since batches are atomic, a
   461  	// batch that times out has no effect. Hence, we chunk the work to ensure
   462  	// progress even when a timeout is eventually hit.
   463  	sort.Sort(intentsByTxn(intents))
   464  	resolved := 0
   465  	const skipIfInFlight = true
   466  	pushTxns := make(map[uuid.UUID]*enginepb.TxnMeta)
   467  	var resolveIntents []roachpb.LockUpdate
   468  	for unpushed := intents; len(unpushed) > 0; {
   469  		for k := range pushTxns { // clear the pushTxns map
   470  			delete(pushTxns, k)
   471  		}
   472  		var prevTxnID uuid.UUID
   473  		var i int
   474  		for i = 0; i < len(unpushed); i++ {
   475  			if curTxn := &unpushed[i].Txn; curTxn.ID != prevTxnID {
   476  				if len(pushTxns) == cleanupIntentsTxnsPerBatch {
   477  					break
   478  				}
   479  				prevTxnID = curTxn.ID
   480  				pushTxns[curTxn.ID] = curTxn
   481  			}
   482  		}
   483  
   484  		pushedTxns, pErr := ir.MaybePushTransactions(ctx, pushTxns, h, pushType, skipIfInFlight)
   485  		if pErr != nil {
   486  			return 0, errors.Wrapf(pErr.GoError(), "failed to push during intent resolution")
   487  		}
   488  		resolveIntents = updateIntentTxnStatus(ctx, pushedTxns, unpushed[:i],
   489  			skipIfInFlight, resolveIntents[:0])
   490  		// resolveIntents with poison=true because we're resolving
   491  		// intents outside of the context of an EndTxn.
   492  		//
   493  		// Naively, it doesn't seem like we need to poison the abort
   494  		// cache since we're pushing with PUSH_TOUCH - meaning that
   495  		// the primary way our Push leads to aborting intents is that
   496  		// of the transaction having timed out (and thus presumably no
   497  		// client being around any more, though at the time of writing
   498  		// we don't guarantee that). But there are other paths in which
   499  		// the Push comes back successful while the coordinating client
   500  		// may still be active. Examples of this are when:
   501  		//
   502  		// - the transaction was aborted by someone else, but the
   503  		//   coordinating client may still be running.
   504  		// - the transaction entry wasn't written yet, which at the
   505  		//   time of writing has our push abort it, leading to the
   506  		//   same situation as above.
   507  		//
   508  		// Thus, we must poison.
   509  		opts := ResolveOptions{Poison: true}
   510  		if pErr := ir.ResolveIntents(ctx, resolveIntents, opts); pErr != nil {
   511  			return 0, errors.Wrapf(pErr.GoError(), "failed to resolve intents")
   512  		}
   513  		resolved += len(resolveIntents)
   514  		unpushed = unpushed[i:]
   515  	}
   516  	return resolved, nil
   517  }
   518  
   519  // CleanupTxnIntentsAsync asynchronously cleans up intents owned by
   520  // a transaction on completion.
   521  func (ir *IntentResolver) CleanupTxnIntentsAsync(
   522  	ctx context.Context,
   523  	rangeID roachpb.RangeID,
   524  	endTxns []result.EndTxnIntents,
   525  	allowSyncProcessing bool,
   526  ) error {
   527  	now := ir.clock.Now()
   528  	for i := range endTxns {
   529  		et := &endTxns[i] // copy for goroutine
   530  		if err := ir.runAsyncTask(ctx, allowSyncProcessing, func(ctx context.Context) {
   531  			locked, release := ir.lockInFlightTxnCleanup(ctx, et.Txn.ID)
   532  			if !locked {
   533  				return
   534  			}
   535  			defer release()
   536  			intents := roachpb.AsLockUpdates(et.Txn, et.Txn.LockSpans)
   537  			if err := ir.cleanupFinishedTxnIntents(ctx, rangeID, et.Txn, intents, now, et.Poison, nil); err != nil {
   538  				if ir.every.ShouldLog() {
   539  					log.Warningf(ctx, "failed to cleanup transaction intents: %v", err)
   540  				}
   541  			}
   542  		}); err != nil {
   543  			return err
   544  		}
   545  	}
   546  	return nil
   547  }
   548  
   549  // lockInFlightTxnCleanup ensures that only a single attempt is being made
   550  // to cleanup the intents belonging to the specified transaction. Returns
   551  // whether this attempt to lock succeeded and if so, a function to release
   552  // the lock, to be invoked subsequently by the caller.
   553  func (ir *IntentResolver) lockInFlightTxnCleanup(
   554  	ctx context.Context, txnID uuid.UUID,
   555  ) (locked bool, release func()) {
   556  	ir.mu.Lock()
   557  	defer ir.mu.Unlock()
   558  	_, inFlight := ir.mu.inFlightTxnCleanups[txnID]
   559  	if inFlight {
   560  		log.Eventf(ctx, "skipping txn resolved; already in flight")
   561  		return false, nil
   562  	}
   563  	ir.mu.inFlightTxnCleanups[txnID] = struct{}{}
   564  	return true, func() {
   565  		ir.mu.Lock()
   566  		delete(ir.mu.inFlightTxnCleanups, txnID)
   567  		ir.mu.Unlock()
   568  	}
   569  }
   570  
   571  // CleanupTxnIntentsOnGCAsync cleans up extant intents owned by a single
   572  // transaction, asynchronously (but returning an error if the IntentResolver's
   573  // semaphore is maxed out). If the transaction is not finalized, but expired, it
   574  // is pushed first to abort it. onComplete is called if non-nil upon completion
   575  // of async task with the intention that it be used as a hook to update metrics.
   576  // It will not be called if an error is returned.
   577  func (ir *IntentResolver) CleanupTxnIntentsOnGCAsync(
   578  	ctx context.Context,
   579  	rangeID roachpb.RangeID,
   580  	txn *roachpb.Transaction,
   581  	intents []roachpb.LockUpdate,
   582  	now hlc.Timestamp,
   583  	onComplete func(pushed, succeeded bool),
   584  ) error {
   585  	return ir.stopper.RunLimitedAsyncTask(
   586  		// If we've successfully launched a background task,
   587  		// dissociate this work from our caller's context and
   588  		// timeout.
   589  		ir.ambientCtx.AnnotateCtx(context.Background()),
   590  		"processing txn intents",
   591  		ir.sem,
   592  		// We really do not want to hang up the GC queue on this kind of
   593  		// processing, so it's better to just skip txns which we can't
   594  		// pass to the async processor (wait=false). Their intents will
   595  		// get cleaned up on demand, and we'll eventually get back to
   596  		// them. Not much harm in having old txn records lying around in
   597  		// the meantime.
   598  		false, /* wait */
   599  		func(ctx context.Context) {
   600  			var pushed, succeeded bool
   601  			defer func() {
   602  				if onComplete != nil {
   603  					onComplete(pushed, succeeded)
   604  				}
   605  			}()
   606  			locked, release := ir.lockInFlightTxnCleanup(ctx, txn.ID)
   607  			if !locked {
   608  				return
   609  			}
   610  			defer release()
   611  			// If the transaction is not yet finalized, but expired, push it
   612  			// before resolving the intents.
   613  			if !txn.Status.IsFinalized() {
   614  				if !txnwait.IsExpired(now, txn) {
   615  					log.VErrEventf(ctx, 3, "cannot push a %s transaction which is not expired: %s", txn.Status, txn)
   616  					return
   617  				}
   618  				b := &kv.Batch{}
   619  				b.Header.Timestamp = now
   620  				b.AddRawRequest(&roachpb.PushTxnRequest{
   621  					RequestHeader: roachpb.RequestHeader{Key: txn.Key},
   622  					PusherTxn: roachpb.Transaction{
   623  						TxnMeta: enginepb.TxnMeta{Priority: enginepb.MaxTxnPriority},
   624  					},
   625  					PusheeTxn: txn.TxnMeta,
   626  					PushType:  roachpb.PUSH_ABORT,
   627  				})
   628  				pushed = true
   629  				if err := ir.db.Run(ctx, b); err != nil {
   630  					log.VErrEventf(ctx, 2, "failed to push %s, expired txn (%s): %s", txn.Status, txn, err)
   631  					return
   632  				}
   633  				// Get the pushed txn and update the intents slice.
   634  				txn = &b.RawResponse().Responses[0].GetInner().(*roachpb.PushTxnResponse).PusheeTxn
   635  				for i := range intents {
   636  					intents[i].SetTxn(txn)
   637  				}
   638  			}
   639  			var onCleanupComplete func(error)
   640  			if onComplete != nil {
   641  				onCompleteCopy := onComplete // copy onComplete for use in onCleanupComplete
   642  				onCleanupComplete = func(err error) {
   643  					onCompleteCopy(pushed, err == nil)
   644  				}
   645  			}
   646  			// Set onComplete to nil to disable the deferred call as the call has now
   647  			// been delegated to the callback passed to cleanupFinishedTxnIntents.
   648  			onComplete = nil
   649  			err := ir.cleanupFinishedTxnIntents(ctx, rangeID, txn, intents, now, false /* poison */, onCleanupComplete)
   650  			if err != nil {
   651  				if ir.every.ShouldLog() {
   652  					log.Warningf(ctx, "failed to cleanup transaction intents: %+v", err)
   653  				}
   654  			}
   655  		},
   656  	)
   657  }
   658  
   659  func (ir *IntentResolver) gcTxnRecord(
   660  	ctx context.Context, rangeID roachpb.RangeID, txn *roachpb.Transaction,
   661  ) error {
   662  	// We successfully resolved the intents, so we're able to GC from
   663  	// the txn span directly.
   664  	txnKey := keys.TransactionKey(txn.Key, txn.ID)
   665  	// This is pretty tricky. Transaction keys are range-local and
   666  	// so they are encoded specially. The key range addressed by
   667  	// (txnKey, txnKey.Next()) might be empty (since Next() does
   668  	// not imply monotonicity on the address side). Instead, we
   669  	// send this request to a range determined using the resolved
   670  	// transaction anchor, i.e. if the txn is anchored on
   671  	// /Local/RangeDescriptor/"a"/uuid, the key range below would
   672  	// be ["a", "a\x00"). However, the first range is special again
   673  	// because the above procedure results in KeyMin, but we need
   674  	// at least KeyLocalMax.
   675  	//
   676  	// #7880 will address this by making GCRequest less special and
   677  	// thus obviating the need to cook up an artificial range here.
   678  	var gcArgs roachpb.GCRequest
   679  	{
   680  		key := keys.MustAddr(txn.Key)
   681  		if localMax := keys.MustAddr(keys.LocalMax); key.Less(localMax) {
   682  			key = localMax
   683  		}
   684  		endKey := key.Next()
   685  
   686  		gcArgs.RequestHeader = roachpb.RequestHeader{
   687  			Key:    key.AsRawKey(),
   688  			EndKey: endKey.AsRawKey(),
   689  		}
   690  	}
   691  	gcArgs.Keys = append(gcArgs.Keys, roachpb.GCRequest_GCKey{
   692  		Key: txnKey,
   693  	})
   694  	// Although the IntentResolver has a RangeDescriptorCache it could consult to
   695  	// to determine the range to which this request corresponds, GCRequests are
   696  	// always issued on behalf of the range on which this record resides which is
   697  	// a strong signal that it is the range which will contain the transaction
   698  	// record now.
   699  	_, err := ir.gcBatcher.Send(ctx, rangeID, &gcArgs)
   700  	if err != nil {
   701  		return errors.Wrapf(err, "could not GC completed transaction anchored at %s",
   702  			roachpb.Key(txn.Key))
   703  	}
   704  	return nil
   705  }
   706  
   707  // cleanupFinishedTxnIntents cleans up extant intents owned by a single
   708  // transaction and when all intents have been successfully resolved, the
   709  // transaction record is GC'ed asynchronously. onComplete will be called when
   710  // all processing has completed which is likely to be after this call returns
   711  // in the case of success.
   712  func (ir *IntentResolver) cleanupFinishedTxnIntents(
   713  	ctx context.Context,
   714  	rangeID roachpb.RangeID,
   715  	txn *roachpb.Transaction,
   716  	intents []roachpb.LockUpdate,
   717  	now hlc.Timestamp,
   718  	poison bool,
   719  	onComplete func(error),
   720  ) (err error) {
   721  	defer func() {
   722  		// When err is non-nil we are guaranteed that the async task is not started
   723  		// so there is no race on calling onComplete.
   724  		if err != nil && onComplete != nil {
   725  			onComplete(err)
   726  		}
   727  	}()
   728  	// Resolve intents.
   729  	opts := ResolveOptions{Poison: poison, MinTimestamp: txn.MinTimestamp}
   730  	if pErr := ir.ResolveIntents(ctx, intents, opts); pErr != nil {
   731  		return errors.Wrapf(pErr.GoError(), "failed to resolve intents")
   732  	}
   733  	// Run transaction record GC outside of ir.sem.
   734  	return ir.stopper.RunAsyncTask(
   735  		ctx,
   736  		"storage.IntentResolver: cleanup txn records",
   737  		func(ctx context.Context) {
   738  			err := ir.gcTxnRecord(ctx, rangeID, txn)
   739  			if onComplete != nil {
   740  				onComplete(err)
   741  			}
   742  			if err != nil {
   743  				if ir.every.ShouldLog() {
   744  					log.Warningf(ctx, "failed to gc transaction record: %v", err)
   745  				}
   746  			}
   747  		})
   748  }
   749  
   750  // ResolveOptions is used during intent resolution. It specifies whether the
   751  // caller wants the call to block, and whether the ranges containing the intents
   752  // are to be poisoned.
   753  type ResolveOptions struct {
   754  	Poison bool
   755  	// The original transaction timestamp from the earliest txn epoch; if
   756  	// supplied, resolution of intent ranges can be optimized in some cases.
   757  	MinTimestamp hlc.Timestamp
   758  }
   759  
   760  // lookupRangeID maps a key to a RangeID for best effort batching of intent
   761  // resolution requests.
   762  func (ir *IntentResolver) lookupRangeID(ctx context.Context, key roachpb.Key) roachpb.RangeID {
   763  	rKey, err := keys.Addr(key)
   764  	if err != nil {
   765  		if ir.every.ShouldLog() {
   766  			log.Warningf(ctx, "failed to resolve addr for key %q: %+v", key, err)
   767  		}
   768  		return 0
   769  	}
   770  	rDesc, err := ir.rdc.LookupRangeDescriptor(ctx, rKey)
   771  	if err != nil {
   772  		if ir.every.ShouldLog() {
   773  			log.Warningf(ctx, "failed to look up range descriptor for key %q: %+v", key, err)
   774  		}
   775  		return 0
   776  	}
   777  	return rDesc.RangeID
   778  }
   779  
   780  // ResolveIntent synchronously resolves an intent according to opts.
   781  func (ir *IntentResolver) ResolveIntent(
   782  	ctx context.Context, intent roachpb.LockUpdate, opts ResolveOptions,
   783  ) *roachpb.Error {
   784  	return ir.ResolveIntents(ctx, []roachpb.LockUpdate{intent}, opts)
   785  }
   786  
   787  // ResolveIntents synchronously resolves intents according to opts.
   788  func (ir *IntentResolver) ResolveIntents(
   789  	ctx context.Context, intents []roachpb.LockUpdate, opts ResolveOptions,
   790  ) *roachpb.Error {
   791  	if len(intents) == 0 {
   792  		return nil
   793  	}
   794  	// Avoid doing any work on behalf of expired contexts. See
   795  	// https://github.com/cockroachdb/cockroach/issues/15997.
   796  	if err := ctx.Err(); err != nil {
   797  		return roachpb.NewError(err)
   798  	}
   799  	log.Eventf(ctx, "resolving intents")
   800  	ctx, cancel := context.WithCancel(ctx)
   801  	defer cancel()
   802  
   803  	respChan := make(chan requestbatcher.Response, len(intents))
   804  	for _, intent := range intents {
   805  		rangeID := ir.lookupRangeID(ctx, intent.Key)
   806  		var req roachpb.Request
   807  		var batcher *requestbatcher.RequestBatcher
   808  		if len(intent.EndKey) == 0 {
   809  			req = &roachpb.ResolveIntentRequest{
   810  				RequestHeader:  roachpb.RequestHeaderFromSpan(intent.Span),
   811  				IntentTxn:      intent.Txn,
   812  				Status:         intent.Status,
   813  				Poison:         opts.Poison,
   814  				IgnoredSeqNums: intent.IgnoredSeqNums,
   815  			}
   816  			batcher = ir.irBatcher
   817  		} else {
   818  			req = &roachpb.ResolveIntentRangeRequest{
   819  				RequestHeader:  roachpb.RequestHeaderFromSpan(intent.Span),
   820  				IntentTxn:      intent.Txn,
   821  				Status:         intent.Status,
   822  				Poison:         opts.Poison,
   823  				MinTimestamp:   opts.MinTimestamp,
   824  				IgnoredSeqNums: intent.IgnoredSeqNums,
   825  			}
   826  			batcher = ir.irRangeBatcher
   827  		}
   828  		if err := batcher.SendWithChan(ctx, respChan, rangeID, req); err != nil {
   829  			return roachpb.NewError(err)
   830  		}
   831  	}
   832  	for seen := 0; seen < len(intents); seen++ {
   833  		select {
   834  		case resp := <-respChan:
   835  			if resp.Err != nil {
   836  				return roachpb.NewError(resp.Err)
   837  			}
   838  			_ = resp.Resp // ignore the response
   839  		case <-ctx.Done():
   840  			return roachpb.NewError(ctx.Err())
   841  		}
   842  	}
   843  	return nil
   844  }
   845  
   846  // intentsByTxn implements sort.Interface to sort intents based on txnID.
   847  type intentsByTxn []roachpb.Intent
   848  
   849  var _ sort.Interface = intentsByTxn(nil)
   850  
   851  func (s intentsByTxn) Len() int      { return len(s) }
   852  func (s intentsByTxn) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   853  func (s intentsByTxn) Less(i, j int) bool {
   854  	return bytes.Compare(s[i].Txn.ID[:], s[j].Txn.ID[:]) < 0
   855  }