// Send implements the batch.Sender interface. It subdivides the Batch // into batches admissible for sending (preventing certain illegal // mixtures of requests), executes each individual part (which may // span multiple ranges), and recombines the response. // // When the request spans ranges, it is split by range and a partial // subset of the batch request is sent to affected ranges in parallel. // // The first write in a transaction may not arrive before writes to // other ranges. This is relevant in the case of a BeginTransaction // request. Intents written to other ranges before the transaction // record is created will cause the transaction to abort early. func (ds *DistSender) Send( ctx context.Context, ba roachpb.BatchRequest, ) (*roachpb.BatchResponse, *roachpb.Error) { tracing.AnnotateTrace() if pErr := ds.initAndVerifyBatch(ctx, &ba); pErr != nil { return nil, pErr } ctx = ds.AnnotateCtx(ctx) ctx, cleanup := tracing.EnsureContext(ctx, ds.AmbientContext.Tracer) defer cleanup() var rplChunks []*roachpb.BatchResponse parts := ba.Split(false /* don't split ET */) if len(parts) > 1 && ba.MaxSpanRequestKeys != 0 { // We already verified above that the batch contains only scan requests of the same type. // Such a batch should never need splitting. panic("batch with MaxSpanRequestKeys needs splitting") } for len(parts) > 0 { part := parts[0] ba.Requests = part // The minimal key range encompassing all requests contained within. // Local addressing has already been resolved. // TODO(tschottdorf): consider rudimentary validation of the batch here // (for example, non-range requests with EndKey, or empty key ranges). rs, err := keys.Range(ba) if err != nil { return nil, roachpb.NewError(err) } rpl, pErr := ds.divideAndSendBatchToRanges(ctx, ba, rs, true /* isFirst */) if pErr == errNo1PCTxn { // If we tried to send a single round-trip EndTransaction but // it looks like it's going to hit multiple ranges, split it // here and try again. if len(parts) != 1 { panic("EndTransaction not in last chunk of batch") } parts = ba.Split(true /* split ET */) if len(parts) != 2 { panic("split of final EndTransaction chunk resulted in != 2 parts") } continue } if pErr != nil { return nil, pErr } // Propagate transaction from last reply to next request. The final // update is taken and put into the response's main header. ba.UpdateTxn(rpl.Txn) rplChunks = append(rplChunks, rpl) parts = parts[1:] } reply := rplChunks[0] for _, rpl := range rplChunks[1:] { reply.Responses = append(reply.Responses, rpl.Responses...) reply.CollectedSpans = append(reply.CollectedSpans, rpl.CollectedSpans...) } reply.BatchResponse_Header = rplChunks[len(rplChunks)-1].BatchResponse_Header return reply, nil }
// resolveIntents resolves the given intents. `wait` is currently a // no-op; all intents are resolved synchronously. // // TODO(bdarnell): Restore the wait=false optimization when/if #8360 // is fixed. `wait=false` requests a semi-synchronous operation, // returning when all local commands have been *proposed* but not yet // committed or executed. This ensures that if a waiting client // retries immediately after calling this function, it will not hit // the same intents again (in the absence of #8360, we provide this // guarantee by resolving the intents synchronously regardless of the // `wait` argument). func (ir *intentResolver) resolveIntents( ctx context.Context, intents []roachpb.Intent, wait bool, poison bool, ) error { // Force synchronous operation; see above TODO. wait = true if len(intents) == 0 { return nil } // We're doing async stuff below; those need new traces. ctx, cleanup := tracing.EnsureContext(ctx, ir.store.Tracer()) defer cleanup() log.Eventf(ctx, "resolving intents [wait=%t]", wait) var reqs []roachpb.Request for i := range intents { intent := intents[i] // avoids a race in `i, intent := range ...` var resolveArgs roachpb.Request { if len(intent.EndKey) == 0 { resolveArgs = &roachpb.ResolveIntentRequest{ Span: intent.Span, IntentTxn: intent.Txn, Status: intent.Status, Poison: poison, } } else { resolveArgs = &roachpb.ResolveIntentRangeRequest{ Span: intent.Span, IntentTxn: intent.Txn, Status: intent.Status, Poison: poison, } } } reqs = append(reqs, resolveArgs) } // Resolve all of the intents. if len(reqs) > 0 { b := &client.Batch{} b.AddRawRequest(reqs...) action := func() error { // TODO(tschottdorf): no tracing here yet. return ir.store.DB().Run(ctx, b) } if wait || ir.store.Stopper().RunLimitedAsyncTask( ctx, ir.sem, true /* wait */, func(ctx context.Context) { if err := action(); err != nil { log.Warningf(ctx, "unable to resolve external intents: %s", err) } }) != nil { // Try async to not keep the caller waiting, but when draining // just go ahead and do it synchronously. See #1684. // TODO(tschottdorf): This is ripe for removal. if err := action(); err != nil { return err } } } return nil }