func (db *DB) prepareToSend(ba *roachpb.BatchRequest) *roachpb.Error { if ba.ReadConsistency == roachpb.INCONSISTENT { for _, ru := range ba.Requests { req := ru.GetInner() if req.Method() != roachpb.Get && req.Method() != roachpb.Scan && req.Method() != roachpb.ReverseScan { return roachpb.NewErrorf("method %s not allowed with INCONSISTENT batch", req.Method) } } } if db.ctx.UserPriority != 1 { ba.UserPriority = db.ctx.UserPriority } tracing.AnnotateTrace() return nil }
// sendInternal sends the batch and updates the transaction on error. Depending // on the error type, the transaction might be replaced by a new one. func (txn *Txn) sendInternal(ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { if len(ba.Requests) == 0 { return nil, nil } if pErr := txn.db.prepareToSend(&ba); pErr != nil { return nil, pErr } // Send call through the DB's sender. ba.Txn = &txn.Proto // For testing purposes, txn.UserPriority can be a negative value (see // MakePriority). if txn.UserPriority != 0 { ba.UserPriority = txn.UserPriority } // TODO(radu): when db.send supports a context, we can just use that (and // remove the prepareToSend call above). br, pErr := txn.db.sender.Send(txn.Context, ba) if br != nil && br.Error != nil { panic(roachpb.ErrorUnexpectedlySet(txn.db.sender, br)) } if br != nil { for _, encSp := range br.CollectedSpans { var newSp basictracer.RawSpan if err := tracing.DecodeRawSpan(encSp, &newSp); err != nil { return nil, roachpb.NewError(err) } txn.CollectedSpans = append(txn.CollectedSpans, newSp) } } // Only successful requests can carry an updated Txn in their response // header. Any error (e.g. a restart) can have a Txn attached to them as // well; those update our local state in the same way for the next attempt. // The exception is if our transaction was aborted and needs to restart // from scratch, in which case we do just that. if pErr == nil { txn.Proto.Update(br.Txn) return br, nil } if log.V(1) { log.Infof(txn.Context, "failed batch: %s", pErr) } if _, ok := pErr.GetDetail().(*roachpb.TransactionAbortedError); ok { // On Abort, reset the transaction so we start anew on restart. txn.Proto = roachpb.Transaction{ TxnMeta: enginepb.TxnMeta{ Isolation: txn.Proto.Isolation, }, Name: txn.Proto.Name, } // Acts as a minimum priority on restart. if pErr.GetTxn() != nil { txn.Proto.Priority = pErr.GetTxn().Priority } } else if pErr.TransactionRestart != roachpb.TransactionRestart_NONE { txn.Proto.Update(pErr.GetTxn()) } return nil, pErr }