// processWriteIntentError tries to push the conflicting // transaction(s) responsible for the given WriteIntentError, and to // resolve those intents if possible. Returns a new error to be used // in place of the original. // // The returned error may be a copy of the original WriteIntentError, // with or without the Resolved flag set, which governs the client's // retry behavior (if the transaction is pushed, the Resolved flag is // set to tell the client to retry immediately; otherwise it is false // to cause the client to back off). func (ir *intentResolver) processWriteIntentError(ctx context.Context, wiPErr *roachpb.Error, args roachpb.Request, h roachpb.Header, pushType roachpb.PushTxnType) *roachpb.Error { wiErr, ok := wiPErr.GetDetail().(*roachpb.WriteIntentError) if !ok { return roachpb.NewErrorf("not a WriteIntentError: %v", wiPErr) } if log.V(6) { log.Infof(ctx, "resolving write intent %s", wiErr) } method := args.Method() readOnly := roachpb.IsReadOnly(args) // TODO(tschottdorf): pass as param resolveIntents, pushErr := ir.maybePushTransactions(ctx, wiErr.Intents, h, pushType, false) if resErr := ir.resolveIntents(ctx, resolveIntents, false /* !wait */, pushType == roachpb.PUSH_ABORT /* poison */); resErr != nil { // When resolving without waiting, errors should not // usually be returned here, although there are some cases // when they may be (especially when a test cluster is in // the process of shutting down). log.Warningf(ctx, "asynchronous resolveIntents failed: %s", resErr) } if pushErr != nil { if log.V(1) { log.Infof(ctx, "on %s: %s", method, pushErr) } if _, isExpected := pushErr.GetDetail().(*roachpb.TransactionPushError); !isExpected { // If an unexpected error occurred, make sure it bubbles up to the // client. Examples are timeouts and logic errors. return pushErr } // For write/write conflicts within a transaction, propagate the // push failure, not the original write intent error. The push // failure will instruct the client to restart the transaction // with a backoff. if h.Txn != nil && h.Txn.ID != nil && !readOnly { return pushErr } // For read/write conflicts, and non-transactional write/write // conflicts, return the write intent error which engages // backoff/retry (with !Resolved). We don't need to restart the // txn, only resend the read with a backoff. return wiPErr } // We pushed all transactions, so tell the client everything's // resolved and it can retry immediately. wiErr.Resolved = true return wiPErr // references wiErr }
// UpdateForBatch updates the first argument (the header of a request contained // in a batch) from the second one (the batch header), returning an error when // inconsistencies are found. // It is checked that the individual call does not have a UserPriority // or Txn set that differs from the batch's. // TODO(tschottdorf): will go with #2143. func updateForBatch(args roachpb.Request, bHeader roachpb.BatchRequest_Header) error { // Disallow transaction, user and priority on individual calls, unless // equal. aHeader := args.Header() if aPrio := aHeader.GetUserPriority(); aPrio != roachpb.Default_RequestHeader_UserPriority && aPrio != bHeader.GetUserPriority() { return util.Errorf("conflicting user priority on call in batch") } aHeader.UserPriority = bHeader.UserPriority aHeader.Txn = bHeader.Txn // reqs always take Txn from batch return nil }
// processWriteIntentError tries to push the conflicting // transaction(s) responsible for the given WriteIntentError, and to // resolve those intents if possible. Returns a new error to be used // in place of the original. // // The returned error may be a copy of the original WriteIntentError, // with or without the Resolved flag set, which governs the client's // retry behavior (if the transaction is pushed, the Resolved flag is // set to tell the client to retry immediately; otherwise it is false // to cause the client to back off). func (ir *intentResolver) processWriteIntentError(ctx context.Context, wiErr roachpb.WriteIntentError, r *Replica, args roachpb.Request, h roachpb.Header, pushType roachpb.PushTxnType) *roachpb.Error { if log.V(6) { log.Infoc(ctx, "resolving write intent %s", wiErr) } method := args.Method() readOnly := roachpb.IsReadOnly(args) // TODO(tschottdorf): pass as param resolveIntents, pushErr := ir.maybePushTransactions(ctx, wiErr.Intents, h, pushType, false) if resErr := ir.resolveIntents(ctx, r, resolveIntents, false /* !wait */, true /* poison */); resErr != nil { // When resolving without waiting, errors should not // usually be returned here, although there are some cases // when they may be (especially when a test cluster is in // the process of shutting down). log.Warningf("asynchronous resolveIntents failed: %s", resErr) } if pushErr != nil { if log.V(1) { log.Infoc(ctx, "on %s: %s", method, pushErr) } // For write/write conflicts within a transaction, propagate the // push failure, not the original write intent error. The push // failure will instruct the client to restart the transaction // with a backoff. if h.Txn != nil && h.Txn.ID != nil && !readOnly { return pushErr } // For read/write conflicts, and non-transactional write/write // conflicts, return the write intent error which engages // backoff/retry (with !Resolved). We don't need to restart the // txn, only resend the read with a backoff. return roachpb.NewError(&wiErr) } // We pushed all transactions, so tell the client everything's // resolved and it can retry immediately. wiErr.Resolved = true return roachpb.NewError(&wiErr) }
// SendWrappedAt is a convenience function which wraps the request in a batch // and sends it via the provided Sender at the given timestamp. It returns the // unwrapped response or an error. It's valid to pass a `nil` context; // context.Background() is used in that case. func SendWrappedAt(sender Sender, ctx context.Context, ts roachpb.Timestamp, args roachpb.Request) (roachpb.Response, error) { if ctx == nil { ctx = context.Background() } ba, unwrap := func(args roachpb.Request) (*roachpb.BatchRequest, func(*roachpb.BatchResponse) roachpb.Response) { ba := &roachpb.BatchRequest{} ba.Timestamp = ts { h := args.Header() ba.Key, ba.EndKey = h.Key, h.EndKey ba.CmdID = h.CmdID ba.Replica = h.Replica ba.RangeID = h.RangeID ba.UserPriority = h.UserPriority ba.Txn = h.Txn ba.ReadConsistency = h.ReadConsistency } ba.Add(args) return ba, func(br *roachpb.BatchResponse) roachpb.Response { unwrappedReply := br.Responses[0].GetInner() // The ReplyTxn is propagated from one response to the next request, // and we adopt the mechanism that whenever the Txn changes, it needs // to be set in the reply, for example to ratchet up the transaction // timestamp on writes when necessary. // This is internally necessary to sequentially execute the batch, // so it makes some sense to take the burden of updating the Txn // from TxnCoordSender - it will only need to act on retries/aborts // in the future. unwrappedReply.Header().Txn = br.Txn return unwrappedReply } }(args) br, pErr := sender.Send(ctx, *ba) if err := pErr.GoError(); err != nil { return nil, err } return unwrap(br), nil }