// sendBatch unrolls a batched command and sends each constituent // command in parallel. // TODO(tschottdorf): modify sendBatch so that it sends truly parallel requests // when outside of a Transaction. This can then be used to address the TODO in // (*TxnCoordSender).resolve(). func (tc *TxnCoordSender) sendBatch(ctx context.Context, batchArgs *proto.BatchRequest, batchReply *proto.BatchResponse) { // Prepare the calls by unrolling the batch. If the batchReply is // pre-initialized with replies, use those; otherwise create replies // as needed. // TODO(spencer): send calls in parallel. batchReply.Txn = batchArgs.Txn for i := range batchArgs.Requests { args := batchArgs.Requests[i].GetValue().(proto.Request) if err := updateForBatch(args, batchArgs.RequestHeader); err != nil { batchReply.Header().SetGoError(err) return } call := proto.Call{Args: args} // Create a reply from the method type and add to batch response. if i >= len(batchReply.Responses) { call.Reply = args.CreateReply() batchReply.Add(call.Reply) } else { call.Reply = batchReply.Responses[i].GetValue().(proto.Response) } tc.sendOne(ctx, call) // Amalgamate transaction updates and propagate first error, if applicable. if batchReply.Txn != nil { batchReply.Txn.Update(call.Reply.Header().Txn) } if call.Reply.Header().Error != nil { batchReply.Error = call.Reply.Header().Error return } } }
func newTestSender(pre, post func(proto.BatchRequest) (*proto.BatchResponse, *proto.Error)) SenderFunc { txnKey := proto.Key("test-txn") txnID := []byte(uuid.NewUUID4()) return func(_ context.Context, ba proto.BatchRequest) (*proto.BatchResponse, *proto.Error) { ba.UserPriority = gogoproto.Int32(-1) if ba.Txn != nil && len(ba.Txn.ID) == 0 { ba.Txn.Key = txnKey ba.Txn.ID = txnID } var br *proto.BatchResponse var pErr *proto.Error if pre != nil { br, pErr = pre(ba) } else { br = &proto.BatchResponse{} } if pErr != nil { return nil, pErr } var writing bool status := proto.PENDING if _, ok := ba.GetArg(proto.Put); ok { br.Add(gogoproto.Clone(testPutResp).(proto.Response)) writing = true } if args, ok := ba.GetArg(proto.EndTransaction); ok { et := args.(*proto.EndTransactionRequest) writing = true if et.Commit { status = proto.COMMITTED } else { status = proto.ABORTED } } br.Txn = gogoproto.Clone(ba.Txn).(*proto.Transaction) if br.Txn != nil && pErr == nil { br.Txn.Writing = writing br.Txn.Status = status } if post != nil { br, pErr = post(ba) } return br, pErr } }
// sendBatch unrolls a batched command and sends each constituent // command in parallel. func (tc *TxnCoordSender) sendBatch(batchArgs *proto.BatchRequest, batchReply *proto.BatchResponse) { // Prepare the calls by unrolling the batch. If the batchReply is // pre-initialized with replies, use those; otherwise create replies // as needed. // TODO(spencer): send calls in parallel. batchReply.Txn = batchArgs.Txn for i := range batchArgs.Requests { // Initialize args header values where appropriate. args := batchArgs.Requests[i].GetValue().(proto.Request) method, err := proto.MethodForRequest(args) call := &client.Call{Method: method, Args: args} if err != nil { batchReply.SetGoError(err) return } if args.Header().User == "" { args.Header().User = batchArgs.User } if args.Header().UserPriority == nil { args.Header().UserPriority = batchArgs.UserPriority } args.Header().Txn = batchArgs.Txn // Create a reply from the method type and add to batch response. if i >= len(batchReply.Responses) { if call.Reply, err = proto.CreateReply(method); err != nil { batchReply.SetGoError(util.Errorf("unsupported method in batch: %s", method)) return } batchReply.Add(call.Reply) } else { call.Reply = batchReply.Responses[i].GetValue().(proto.Response) } tc.sendOne(call) // Amalgamate transaction updates and propagate first error, if applicable. if batchReply.Txn != nil { batchReply.Txn.Update(call.Reply.Header().Txn) } if call.Reply.Header().Error != nil { batchReply.Error = call.Reply.Header().Error return } } }