diff --git a/pkg/kv/dist_sender.go b/pkg/kv/dist_sender.go index 5e1c97f830b7..fc30b312fdbb 100644 --- a/pkg/kv/dist_sender.go +++ b/pkg/kv/dist_sender.go @@ -22,6 +22,9 @@ import ( "time" "unsafe" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "github.com/pkg/errors" "golang.org/x/net/context" @@ -936,10 +939,11 @@ func fillSkippedResponses( } } -// sendToReplicas sends one or more RPCs to clients specified by the slice of -// replicas. On success, Send returns the first successful reply. Otherwise, -// Send returns an error if and as soon as the number of failed RPCs exceeds -// the available endpoints less the number of required replies. +// sendToReplicas sends one or more RPCs to clients specified by the +// slice of replicas. On success, Send returns the first successful +// reply. If an error occurs which is not specific to a single +// replica, it's returned immediately. Otherwise, when all replicas +// have been tried and failed, returns a send error. func (ds *DistSender) sendToReplicas( opts SendOptions, rangeID roachpb.RangeID, @@ -953,6 +957,14 @@ func (ds *DistSender) sendToReplicas( len(replicas), 1)) } + var ambiguousCommit bool + var haveCommit bool + // We only check for committed txns, not aborts because aborts may + // be retried without any risk of inconsistencies. + if etArg, ok := args.GetArg(roachpb.EndTransaction); ok && + etArg.(*roachpb.EndTransactionRequest).Commit { + haveCommit = true + } done := make(chan BatchCall, len(replicas)) transportFactory := opts.transportFactory @@ -1001,7 +1013,18 @@ func (ds *DistSender) sendToReplicas( log.Infof(opts.ctx, "application error: %s", call.Reply.Error) } - if !ds.handlePerReplicaError(rangeID, call.Reply.Error) { + if call.Reply.Error == nil { + return call.Reply, nil + } else if !ds.handlePerReplicaError(transport, rangeID, call.Reply.Error) { + // The error received is not specific to this replica, so we + // should return it instead of trying other replicas. However, + // if we're trying to commit a transaction and there are + // still other RPCs outstanding or an ambiguous RPC error + // was already received, we must return an ambiguous commit + // error instead of returned error. + if haveCommit && (pending > 0 || ambiguousCommit) { + return nil, roachpb.NewAmbiguousCommitError() + } return call.Reply, nil } @@ -1013,8 +1036,34 @@ func (ds *DistSender) sendToReplicas( // we've seen (for example, a NotLeaseHolderError conveys more // information than a RangeNotFound). err = call.Reply.Error.GoError() - } else if log.V(1) { - log.Warningf(opts.ctx, "RPC error: %s", err) + } else { + if log.V(1) { + log.Warningf(opts.ctx, "RPC error: %s", err) + } + // All connection errors except for an unavailable node (this + // is GRPC's fail-fast error), may mean that the request + // succeeded on the remote server, but we were unable to + // receive the reply. Set the ambiguous commit flag. + // + // We retry ambiguous commit batches to avoid returning the + // unrecoverable AmbiguousCommitError. This is safe because + // repeating an already-successfully applied batch is + // guaranteed to return either a TransactionReplayError (in + // case the replay happens at the original leader), or a + // TransactionRetryError (in case the replay happens at a new + // leader). If the original attempt merely timed out or was + // lost, then the batch will succeed and we can be assured the + // commit was applied just once. + // + // The Unavailable code is used by GRPC to indicate that a + // request fails fast and is not sent, so we can be sure there + // is no ambiguity on these errors. Note that these are common + // if a node is down. + // See https://github.com/grpc/grpc-go/blob/52f6504dc290bd928a8139ba94e3ab32ed9a6273/call.go#L182 + // See https://github.com/grpc/grpc-go/blob/52f6504dc290bd928a8139ba94e3ab32ed9a6273/stream.go#L158 + if haveCommit && grpc.Code(err) != codes.Unavailable { + ambiguousCommit = true + } } // Send to additional replicas if available. @@ -1024,12 +1073,17 @@ func (ds *DistSender) sendToReplicas( transport.SendNext(done) } if pending == 0 { - log.VEventf(opts.ctx, 2, - "sending to all %d replicas failed; last error: %s", - len(replicas), err) - return nil, roachpb.NewSendError( - fmt.Sprintf("sending to all %d replicas failed; last error: %v", - len(replicas), err)) + if ambiguousCommit { + err = roachpb.NewAmbiguousCommitError() + } else { + err = roachpb.NewSendError( + fmt.Sprintf("sending to all %d replicas failed; last error: %v", len(replicas), err), + ) + } + if log.V(2) { + log.ErrEvent(opts.ctx, err.Error()) + } + return nil, err } } } @@ -1040,7 +1094,9 @@ func (ds *DistSender) sendToReplicas( // replicas is likely to produce different results. This method should // be called only once for each error as it may have side effects such // as updating caches. -func (ds *DistSender) handlePerReplicaError(rangeID roachpb.RangeID, pErr *roachpb.Error) bool { +func (ds *DistSender) handlePerReplicaError( + transport Transport, rangeID roachpb.RangeID, pErr *roachpb.Error, +) bool { switch tErr := pErr.GetDetail().(type) { case *roachpb.RangeNotFoundError: return true @@ -1049,10 +1105,11 @@ func (ds *DistSender) handlePerReplicaError(rangeID roachpb.RangeID, pErr *roach case *roachpb.NotLeaseHolderError: if tErr.LeaseHolder != nil { // If the replica we contacted knows the new lease holder, update the cache. - ds.updateLeaseHolderCache(rangeID, *tErr.LeaseHolder) + leaseHolder := *tErr.LeaseHolder + ds.updateLeaseHolderCache(rangeID, leaseHolder) - // TODO(bdarnell): Move the new lease holder to the head of the queue - // for the next retry. + // Move the new lease holder to the head of the queue for the next retry. + transport.MoveToFront(leaseHolder) } return true } diff --git a/pkg/kv/dist_sender_test.go b/pkg/kv/dist_sender_test.go index a83a67e00f79..086fcbee9be0 100644 --- a/pkg/kv/dist_sender_test.go +++ b/pkg/kv/dist_sender_test.go @@ -110,6 +110,9 @@ func (l *legacyTransportAdapter) SendNext(done chan<- BatchCall) { } } +func (*legacyTransportAdapter) MoveToFront(roachpb.ReplicaDescriptor) { +} + func (*legacyTransportAdapter) Close() { } @@ -1838,6 +1841,9 @@ func (t *slowLeaseHolderTransport) SendNext(done chan<- BatchCall) { } } +func (t *slowLeaseHolderTransport) MoveToFront(replica roachpb.ReplicaDescriptor) { +} + func (t *slowLeaseHolderTransport) Close() { } diff --git a/pkg/kv/send_test.go b/pkg/kv/send_test.go index e7bc0d5ab375..7ed8d2518095 100644 --- a/pkg/kv/send_test.go +++ b/pkg/kv/send_test.go @@ -163,6 +163,9 @@ func (c *channelSaveTransport) SendNext(done chan<- BatchCall) { c.ch <- done } +func (*channelSaveTransport) MoveToFront(roachpb.ReplicaDescriptor) { +} + func (*channelSaveTransport) Close() { } @@ -417,6 +420,9 @@ func (f *firstNErrorTransport) SendNext(done chan<- BatchCall) { done <- call } +func (*firstNErrorTransport) MoveToFront(roachpb.ReplicaDescriptor) { +} + func (*firstNErrorTransport) Close() { } diff --git a/pkg/kv/transport.go b/pkg/kv/transport.go index 0da7a94fee12..1fa6ba88b8be 100644 --- a/pkg/kv/transport.go +++ b/pkg/kv/transport.go @@ -28,6 +28,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/rpc" "github.com/cockroachdb/cockroach/pkg/util/envutil" "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/util/syncutil" "github.com/opentracing/opentracing-go" "google.golang.org/grpc" ) @@ -55,6 +56,8 @@ type batchClient struct { client roachpb.InternalClient args roachpb.BatchRequest healthy bool + retried bool + pending bool } // BatchCall contains a response and an RPC error (note that the @@ -93,6 +96,12 @@ type Transport interface { // as needed. SendNext(chan<- BatchCall) + // MoveToFront locates the specified replica and moves it to the + // front of the ordering of replicas to try. If the replica has + // already been tried, it will be retried. If the specified replica + // can't be found, this is a noop. + MoveToFront(roachpb.ReplicaDescriptor) + // Close is called when the transport is no longer needed. It may // cancel any pending RPCs without writing any response to the channel. Close() @@ -135,21 +144,24 @@ func grpcTransportFactoryImpl( } type grpcTransport struct { - opts SendOptions - rpcContext *rpc.Context - orderedClients []batchClient + opts SendOptions + rpcContext *rpc.Context + clientIndex int + orderedClients []batchClient + clientPendingMu syncutil.Mutex // protects access to all batchClient pending flags } func (gt *grpcTransport) IsExhausted() bool { - return len(gt.orderedClients) == 0 + return gt.clientIndex == len(gt.orderedClients) } // SendNext invokes the specified RPC on the supplied client when the // client is ready. On success, the reply is sent on the channel; // otherwise an error is sent. func (gt *grpcTransport) SendNext(done chan<- BatchCall) { - client := gt.orderedClients[0] - gt.orderedClients = gt.orderedClients[1:] + client := gt.orderedClients[gt.clientIndex] + gt.clientIndex++ + gt.setPending(client.args.Replica, true) addr := client.remoteAddr if log.V(2) { @@ -170,6 +182,7 @@ func (gt *grpcTransport) SendNext(done chan<- BatchCall) { } reply, err := localServer.Batch(gt.opts.ctx, &client.args) + gt.setPending(client.args.Replica, false) done <- BatchCall{Reply: reply, Err: err} return } @@ -191,16 +204,51 @@ func (gt *grpcTransport) SendNext(done chan<- BatchCall) { } } } + gt.setPending(client.args.Replica, false) done <- BatchCall{Reply: reply, Err: err} }() } +func (gt *grpcTransport) MoveToFront(replica roachpb.ReplicaDescriptor) { + gt.clientPendingMu.Lock() + defer gt.clientPendingMu.Unlock() + for i := range gt.orderedClients { + if gt.orderedClients[i].args.Replica == replica { + // If a call to this replica is active or retried, don't move it. + if gt.orderedClients[i].pending || gt.orderedClients[i].retried { + return + } + // If we've already processed the replica, decrement the current + // index before we swap. + if i < gt.clientIndex { + gt.orderedClients[i].retried = true + gt.clientIndex-- + } + // Swap the client representing this replica to the front. + gt.orderedClients[i], gt.orderedClients[gt.clientIndex] = + gt.orderedClients[gt.clientIndex], gt.orderedClients[i] + return + } + } +} + func (*grpcTransport) Close() { // TODO(bdarnell): Save the cancel functions of all pending RPCs and // call them here. (it's fine to ignore them for now since they'll // time out anyway) } +func (gt *grpcTransport) setPending(replica roachpb.ReplicaDescriptor, pending bool) { + gt.clientPendingMu.Lock() + defer gt.clientPendingMu.Unlock() + for i := range gt.orderedClients { + if gt.orderedClients[i].args.Replica == replica { + gt.orderedClients[i].pending = pending + return + } + } +} + // splitHealthy splits the provided client slice into healthy clients and // unhealthy clients, based on their connection state. Healthy clients will // be rearranged first in the slice, and unhealthy clients will be rearranged @@ -270,5 +318,8 @@ func (s *senderTransport) SendNext(done chan<- BatchCall) { done <- BatchCall{Reply: br} } +func (s *senderTransport) MoveToFront(replica roachpb.ReplicaDescriptor) { +} + func (s *senderTransport) Close() { } diff --git a/pkg/kv/transport_test.go b/pkg/kv/transport_test.go new file mode 100644 index 000000000000..3716e4525c33 --- /dev/null +++ b/pkg/kv/transport_test.go @@ -0,0 +1,107 @@ +// Copyright 2016 The Cockroach Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing +// permissions and limitations under the License. +// +// Author: Spencer Kimball (spencer@cockroachlabs.com) + +package kv + +import ( + "runtime" + "testing" + + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/util/leaktest" +) + +func TestTransportMoveToFront(t *testing.T) { + defer leaktest.AfterTest(t)() + rd1 := roachpb.ReplicaDescriptor{NodeID: 1, StoreID: 1, ReplicaID: 1} + rd2 := roachpb.ReplicaDescriptor{NodeID: 2, StoreID: 2, ReplicaID: 2} + rd3 := roachpb.ReplicaDescriptor{NodeID: 3, StoreID: 3, ReplicaID: 3} + clients := []batchClient{ + {args: roachpb.BatchRequest{Header: roachpb.Header{Replica: rd1}}}, + {args: roachpb.BatchRequest{Header: roachpb.Header{Replica: rd2}}}, + {args: roachpb.BatchRequest{Header: roachpb.Header{Replica: rd3}}}, + } + gt := grpcTransport{orderedClients: clients} + + verifyOrder := func(replicas []roachpb.ReplicaDescriptor) { + _, fn, line, _ := runtime.Caller(1) + for i, bc := range gt.orderedClients { + if bc.args.Replica != replicas[i] { + t.Fatalf("%s:%d: expected order %+v; got mismatch at index %d: %+v", + fn, line, replicas, i, bc.args.Replica) + } + } + } + + verifyOrder([]roachpb.ReplicaDescriptor{rd1, rd2, rd3}) + + // Move replica 2 to the front. + gt.MoveToFront(rd2) + verifyOrder([]roachpb.ReplicaDescriptor{rd2, rd1, rd3}) + + // Now replica 3. + gt.MoveToFront(rd3) + verifyOrder([]roachpb.ReplicaDescriptor{rd3, rd1, rd2}) + + // Mark replica 2 pending. Shouldn't be able to move it. + clients[2].pending = true + gt.MoveToFront(rd2) + verifyOrder([]roachpb.ReplicaDescriptor{rd3, rd1, rd2}) + + // Advance the client index and move replica 3 back to front. + gt.clientIndex++ + gt.MoveToFront(rd3) + verifyOrder([]roachpb.ReplicaDescriptor{rd3, rd1, rd2}) + if gt.clientIndex != 0 { + t.Fatalf("expected cient index 0; got %d", gt.clientIndex) + } + + // Advance the client index again and verify replica 3 cannot + // be moved to front for a second retry. + gt.clientIndex++ + gt.MoveToFront(rd3) + verifyOrder([]roachpb.ReplicaDescriptor{rd3, rd1, rd2}) + if gt.clientIndex != 1 { + t.Fatalf("expected cient index 1; got %d", gt.clientIndex) + } + + // Mark replica 2 no longer pending. Should be able to move it. + clients[2].pending = false + gt.MoveToFront(rd2) + verifyOrder([]roachpb.ReplicaDescriptor{rd3, rd2, rd1}) + + // Advance client index and move rd1 front; should be no change. + gt.clientIndex++ + gt.MoveToFront(rd1) + verifyOrder([]roachpb.ReplicaDescriptor{rd3, rd2, rd1}) + + // Advance client index to and and move rd1 to front. Should move + // client index back for a retry. + gt.clientIndex++ + gt.MoveToFront(rd1) + verifyOrder([]roachpb.ReplicaDescriptor{rd3, rd2, rd1}) + if gt.clientIndex != 2 { + t.Fatalf("expected cient index 2; got %d", gt.clientIndex) + } + + // Advance client index once more; verify no second retry. + gt.clientIndex++ + gt.MoveToFront(rd1) + verifyOrder([]roachpb.ReplicaDescriptor{rd3, rd2, rd1}) + if gt.clientIndex != 3 { + t.Fatalf("expected cient index 3; got %d", gt.clientIndex) + } +} diff --git a/pkg/roachpb/api.pb.go b/pkg/roachpb/api.pb.go index bd7ef51ac535..e812523c7353 100644 --- a/pkg/roachpb/api.pb.go +++ b/pkg/roachpb/api.pb.go @@ -109,6 +109,7 @@ ConditionFailedError LeaseRejectedError SendError + AmbiguousCommitError RaftGroupDeletedError ReplicaCorruptionError ReplicaTooOldError diff --git a/pkg/roachpb/errors.go b/pkg/roachpb/errors.go index 1c31566318f8..72ddceccf917 100644 --- a/pkg/roachpb/errors.go +++ b/pkg/roachpb/errors.go @@ -327,6 +327,21 @@ func (e *RangeFrozenError) message(_ *Error) string { var _ ErrorDetailInterface = &RangeFrozenError{} +// NewAmbiguousCommitError initializes a new AmbiguousCommitError. +func NewAmbiguousCommitError() *AmbiguousCommitError { + return &AmbiguousCommitError{} +} + +func (e *AmbiguousCommitError) Error() string { + return "transaction commit result is ambiguous" +} + +func (e *AmbiguousCommitError) message(_ *Error) string { + return e.Error() +} + +var _ ErrorDetailInterface = &AmbiguousCommitError{} + func (e *TransactionAbortedError) Error() string { return "txn aborted" } diff --git a/pkg/roachpb/errors.pb.go b/pkg/roachpb/errors.pb.go index 28d80f4120f2..a352edd47ca7 100644 --- a/pkg/roachpb/errors.pb.go +++ b/pkg/roachpb/errors.pb.go @@ -295,6 +295,17 @@ func (m *SendError) String() string { return proto.CompactTextString( func (*SendError) ProtoMessage() {} func (*SendError) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{16} } +// An AmbiguousCommitError indicates that an EndTransaction request +// may have succeeded or failed, but the response was not received and +// the final result is ambiguous. +type AmbiguousCommitError struct { +} + +func (m *AmbiguousCommitError) Reset() { *m = AmbiguousCommitError{} } +func (m *AmbiguousCommitError) String() string { return proto.CompactTextString(m) } +func (*AmbiguousCommitError) ProtoMessage() {} +func (*AmbiguousCommitError) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{17} } + // A RaftGroupDeletedError indicates a raft group has been deleted for // the replica. type RaftGroupDeletedError struct { @@ -303,7 +314,7 @@ type RaftGroupDeletedError struct { func (m *RaftGroupDeletedError) Reset() { *m = RaftGroupDeletedError{} } func (m *RaftGroupDeletedError) String() string { return proto.CompactTextString(m) } func (*RaftGroupDeletedError) ProtoMessage() {} -func (*RaftGroupDeletedError) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{17} } +func (*RaftGroupDeletedError) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{18} } // A ReplicaCorruptionError indicates that the replica has experienced // an error which puts its integrity at risk. @@ -317,7 +328,7 @@ type ReplicaCorruptionError struct { func (m *ReplicaCorruptionError) Reset() { *m = ReplicaCorruptionError{} } func (m *ReplicaCorruptionError) String() string { return proto.CompactTextString(m) } func (*ReplicaCorruptionError) ProtoMessage() {} -func (*ReplicaCorruptionError) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{18} } +func (*ReplicaCorruptionError) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{19} } // ReplicaTooOldError is sent in response to a raft message when the // recipient of the raft message believes the sender of the raft @@ -328,7 +339,7 @@ type ReplicaTooOldError struct { func (m *ReplicaTooOldError) Reset() { *m = ReplicaTooOldError{} } func (m *ReplicaTooOldError) String() string { return proto.CompactTextString(m) } func (*ReplicaTooOldError) ProtoMessage() {} -func (*ReplicaTooOldError) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{19} } +func (*ReplicaTooOldError) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{20} } // ErrorDetail is a union type containing all available errors. type ErrorDetail struct { @@ -349,6 +360,7 @@ type ErrorDetail struct { NodeUnavailable *NodeUnavailableError `protobuf:"bytes,14,opt,name=node_unavailable,json=nodeUnavailable" json:"node_unavailable,omitempty"` Send *SendError `protobuf:"bytes,15,opt,name=send" json:"send,omitempty"` RangeFrozen *RangeFrozenError `protobuf:"bytes,25,opt,name=range_frozen,json=rangeFrozen" json:"range_frozen,omitempty"` + AmbiguousCommit *AmbiguousCommitError `protobuf:"bytes,26,opt,name=ambiguous_commit,json=ambiguousCommit" json:"ambiguous_commit,omitempty"` // TODO(kaneda): Following are added to preserve the type when // converting Go errors from/to proto Errors. Revisit this design. RaftGroupDeleted *RaftGroupDeletedError `protobuf:"bytes,16,opt,name=raft_group_deleted,json=raftGroupDeleted" json:"raft_group_deleted,omitempty"` @@ -359,7 +371,7 @@ type ErrorDetail struct { func (m *ErrorDetail) Reset() { *m = ErrorDetail{} } func (m *ErrorDetail) String() string { return proto.CompactTextString(m) } func (*ErrorDetail) ProtoMessage() {} -func (*ErrorDetail) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{20} } +func (*ErrorDetail) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{21} } // ErrPosition describes the position of an error in a Batch. A simple nullable // primitive field would break compatibility with proto3, where primitive fields @@ -371,7 +383,7 @@ type ErrPosition struct { func (m *ErrPosition) Reset() { *m = ErrPosition{} } func (m *ErrPosition) String() string { return proto.CompactTextString(m) } func (*ErrPosition) ProtoMessage() {} -func (*ErrPosition) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{21} } +func (*ErrPosition) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{22} } // Error is a generic representation including a string message // and information about retryability. @@ -398,7 +410,7 @@ type Error struct { func (m *Error) Reset() { *m = Error{} } func (*Error) ProtoMessage() {} -func (*Error) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{22} } +func (*Error) Descriptor() ([]byte, []int) { return fileDescriptorErrors, []int{23} } func init() { proto.RegisterType((*NotLeaseHolderError)(nil), "cockroach.roachpb.NotLeaseHolderError") @@ -418,6 +430,7 @@ func init() { proto.RegisterType((*ConditionFailedError)(nil), "cockroach.roachpb.ConditionFailedError") proto.RegisterType((*LeaseRejectedError)(nil), "cockroach.roachpb.LeaseRejectedError") proto.RegisterType((*SendError)(nil), "cockroach.roachpb.SendError") + proto.RegisterType((*AmbiguousCommitError)(nil), "cockroach.roachpb.AmbiguousCommitError") proto.RegisterType((*RaftGroupDeletedError)(nil), "cockroach.roachpb.RaftGroupDeletedError") proto.RegisterType((*ReplicaCorruptionError)(nil), "cockroach.roachpb.ReplicaCorruptionError") proto.RegisterType((*ReplicaTooOldError)(nil), "cockroach.roachpb.ReplicaTooOldError") @@ -904,6 +917,24 @@ func (m *SendError) MarshalTo(data []byte) (int, error) { return i, nil } +func (m *AmbiguousCommitError) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *AmbiguousCommitError) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + return i, nil +} + func (m *RaftGroupDeletedError) Marshal() (data []byte, err error) { size := m.Size() data = make([]byte, size) @@ -1195,6 +1226,18 @@ func (m *ErrorDetail) MarshalTo(data []byte) (int, error) { } i += n34 } + if m.AmbiguousCommit != nil { + data[i] = 0xd2 + i++ + data[i] = 0x1 + i++ + i = encodeVarintErrors(data, i, uint64(m.AmbiguousCommit.Size())) + n35, err := m.AmbiguousCommit.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n35 + } return i, nil } @@ -1245,11 +1288,11 @@ func (m *Error) MarshalTo(data []byte) (int, error) { data[i] = 0x22 i++ i = encodeVarintErrors(data, i, uint64(m.UnexposedTxn.Size())) - n35, err := m.UnexposedTxn.MarshalTo(data[i:]) + n36, err := m.UnexposedTxn.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n35 + i += n36 } data[i] = 0x28 i++ @@ -1258,30 +1301,30 @@ func (m *Error) MarshalTo(data []byte) (int, error) { data[i] = 0x32 i++ i = encodeVarintErrors(data, i, uint64(m.Detail.Size())) - n36, err := m.Detail.MarshalTo(data[i:]) + n37, err := m.Detail.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n36 + i += n37 } if m.Index != nil { data[i] = 0x3a i++ i = encodeVarintErrors(data, i, uint64(m.Index.Size())) - n37, err := m.Index.MarshalTo(data[i:]) + n38, err := m.Index.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n37 + i += n38 } data[i] = 0x42 i++ i = encodeVarintErrors(data, i, uint64(m.Now.Size())) - n38, err := m.Now.MarshalTo(data[i:]) + n39, err := m.Now.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n38 + i += n39 return i, nil } @@ -1475,6 +1518,12 @@ func (m *SendError) Size() (n int) { return n } +func (m *AmbiguousCommitError) Size() (n int) { + var l int + _ = l + return n +} + func (m *RaftGroupDeletedError) Size() (n int) { var l int _ = l @@ -1579,6 +1628,10 @@ func (m *ErrorDetail) Size() (n int) { l = m.RangeFrozen.Size() n += 2 + l + sovErrors(uint64(l)) } + if m.AmbiguousCommit != nil { + l = m.AmbiguousCommit.Size() + n += 2 + l + sovErrors(uint64(l)) + } return n } @@ -1687,6 +1740,9 @@ func (this *ErrorDetail) GetValue() interface{} { if this.RangeFrozen != nil { return this.RangeFrozen } + if this.AmbiguousCommit != nil { + return this.AmbiguousCommit + } return nil } @@ -1732,6 +1788,8 @@ func (this *ErrorDetail) SetValue(value interface{}) bool { this.TransactionReplay = vt case *RangeFrozenError: this.RangeFrozen = vt + case *AmbiguousCommitError: + this.AmbiguousCommit = vt default: return false } @@ -3260,6 +3318,56 @@ func (m *SendError) Unmarshal(data []byte) error { } return nil } +func (m *AmbiguousCommitError) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowErrors + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AmbiguousCommitError: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AmbiguousCommitError: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipErrors(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthErrors + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *RaftGroupDeletedError) Unmarshal(data []byte) error { l := len(data) iNdEx := 0 @@ -4148,6 +4256,39 @@ func (m *ErrorDetail) Unmarshal(data []byte) error { return err } iNdEx = postIndex + case 26: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AmbiguousCommit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowErrors + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthErrors + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AmbiguousCommit == nil { + m.AmbiguousCommit = &AmbiguousCommitError{} + } + if err := m.AmbiguousCommit.Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipErrors(data[iNdEx:]) @@ -4592,102 +4733,105 @@ var ( func init() { proto.RegisterFile("cockroach/pkg/roachpb/errors.proto", fileDescriptorErrors) } var fileDescriptorErrors = []byte{ - // 1552 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x57, 0x4b, 0x73, 0x1b, 0x45, - 0x10, 0xb6, 0x2c, 0x39, 0x92, 0x5a, 0x96, 0x25, 0x4f, 0x1c, 0x67, 0xe3, 0x22, 0xb2, 0xb3, 0x84, - 0xe0, 0xe4, 0x60, 0x85, 0x40, 0xa8, 0xc2, 0xe4, 0xe2, 0x67, 0x70, 0x1c, 0x3f, 0x6a, 0xec, 0x24, - 0x14, 0xa1, 0x6a, 0x6b, 0xb3, 0x3b, 0x96, 0x17, 0xaf, 0x77, 0x94, 0xd9, 0x59, 0x3f, 0x38, 0xf0, - 0x07, 0xb8, 0x70, 0x24, 0xb7, 0x5c, 0xb8, 0x51, 0xfc, 0x0a, 0x0e, 0x3e, 0x72, 0xe4, 0xe4, 0x02, - 0xf3, 0x2f, 0x72, 0xa2, 0xe6, 0x21, 0x69, 0x57, 0x5e, 0x29, 0xaa, 0xdc, 0x56, 0x3d, 0xdd, 0x5f, - 0xf7, 0xf4, 0xe3, 0xeb, 0x11, 0x98, 0x0e, 0x75, 0x0e, 0x18, 0xb5, 0x9d, 0xfd, 0x7a, 0xf3, 0xa0, - 0x51, 0x97, 0x5f, 0xcd, 0x57, 0x75, 0xc2, 0x18, 0x65, 0xe1, 0x5c, 0x93, 0x51, 0x4e, 0xd1, 0x78, - 0x5b, 0x67, 0x4e, 0x9f, 0x4f, 0xdd, 0x4e, 0x37, 0x3b, 0x24, 0xdc, 0x76, 0x6d, 0x6e, 0x2b, 0xc3, - 0xa9, 0x99, 0x74, 0xad, 0x98, 0xc6, 0x9d, 0xa4, 0x46, 0xc4, 0x3d, 0xbf, 0xbe, 0xef, 0x3b, 0x75, - 0xee, 0x1d, 0x92, 0x90, 0xdb, 0x87, 0x4d, 0xad, 0x37, 0xd1, 0xa0, 0x0d, 0x2a, 0x3f, 0xeb, 0xe2, - 0x4b, 0x49, 0xcd, 0x9f, 0x87, 0xe1, 0xea, 0x26, 0xe5, 0x4f, 0x89, 0x1d, 0x92, 0x6f, 0xa8, 0xef, - 0x12, 0xb6, 0x22, 0xe2, 0x46, 0xcb, 0x90, 0x67, 0xa4, 0xe9, 0x7b, 0x8e, 0x6d, 0x64, 0x66, 0x32, - 0xb3, 0xa5, 0x07, 0xb7, 0xe7, 0x2e, 0x5d, 0x61, 0x0e, 0x2b, 0x8d, 0x65, 0x12, 0x3a, 0xcc, 0x6b, - 0x72, 0xca, 0x16, 0x73, 0x67, 0xe7, 0xd3, 0x43, 0xb8, 0x65, 0x8a, 0x1e, 0xc3, 0xa8, 0x2f, 0x90, - 0xad, 0x7d, 0x09, 0x6d, 0x0c, 0x0f, 0x0e, 0x85, 0x4b, 0x7e, 0x27, 0x26, 0xf4, 0x10, 0x0a, 0xcc, - 0x0e, 0x1a, 0xc4, 0xf2, 0x5c, 0x23, 0x3b, 0x93, 0x99, 0xcd, 0x2e, 0x4e, 0x09, 0x4f, 0x17, 0xe7, - 0xd3, 0x79, 0x2c, 0xe4, 0x6b, 0xcb, 0xef, 0x3a, 0x9f, 0x38, 0x2f, 0x75, 0xd7, 0x5c, 0x34, 0x07, - 0x23, 0x12, 0xc5, 0xc8, 0x49, 0xc7, 0x46, 0x8a, 0x63, 0x79, 0x73, 0xac, 0xd4, 0xcc, 0x49, 0x98, - 0xd8, 0xa4, 0x2e, 0x79, 0x16, 0xd8, 0x47, 0xb6, 0xe7, 0xdb, 0xaf, 0x7c, 0x22, 0xb3, 0x61, 0xae, - 0x03, 0x92, 0xd8, 0x9b, 0x94, 0xaf, 0xd2, 0x28, 0x70, 0x55, 0x8e, 0xe2, 0x41, 0x65, 0x06, 0x0e, - 0xca, 0x7c, 0x33, 0x0c, 0xd7, 0xa4, 0x70, 0x9d, 0x9c, 0x6e, 0x78, 0xe1, 0xa1, 0xcd, 0x9d, 0x7d, - 0x05, 0xf8, 0x39, 0x8c, 0x33, 0xf2, 0x3a, 0x22, 0x21, 0xb7, 0x42, 0x6e, 0x33, 0x6e, 0x1d, 0x90, - 0x53, 0x89, 0x3c, 0xba, 0x98, 0x7f, 0x77, 0x3e, 0x9d, 0x5d, 0x27, 0xa7, 0xb8, 0xa2, 0x35, 0x76, - 0x84, 0xc2, 0x3a, 0x39, 0x45, 0x75, 0x68, 0x89, 0x2c, 0x12, 0xb8, 0xd2, 0x64, 0x38, 0x69, 0x52, - 0xd6, 0xe7, 0x2b, 0x81, 0x2b, 0x0c, 0x36, 0xa0, 0x7a, 0xa8, 0xdd, 0x12, 0xd7, 0x92, 0x51, 0xc9, - 0x9c, 0x96, 0x1e, 0x98, 0x69, 0x85, 0x11, 0xe7, 0xb1, 0xb2, 0x54, 0x3a, 0xb6, 0xf2, 0x08, 0xad, - 0x43, 0x25, 0x8c, 0x1a, 0x0d, 0x12, 0xf2, 0x36, 0x5a, 0x6e, 0x60, 0xb4, 0xb1, 0xb6, 0xa9, 0x3c, - 0x31, 0xb7, 0xa1, 0x2a, 0x3f, 0x56, 0x19, 0xfd, 0x91, 0x04, 0x2a, 0x2b, 0x8f, 0x20, 0xe7, 0x92, - 0xd0, 0xd1, 0x7d, 0x38, 0x00, 0xaa, 0xee, 0x42, 0x69, 0x65, 0xfe, 0x99, 0x01, 0x13, 0x13, 0xdb, - 0x7d, 0xe1, 0xf1, 0x7d, 0x2f, 0x78, 0x16, 0x38, 0x84, 0x71, 0xdb, 0x0b, 0xf8, 0xe9, 0x5a, 0xc0, - 0x09, 0x3b, 0xb2, 0x7d, 0xe5, 0xe4, 0x09, 0x8c, 0x31, 0x62, 0xbb, 0x56, 0x7b, 0x6a, 0xb4, 0xbb, - 0x9b, 0x31, 0x77, 0x62, 0xb4, 0xe6, 0xf6, 0x7d, 0x67, 0x6e, 0xb7, 0xa5, 0xa4, 0x3d, 0x95, 0x85, - 0x69, 0x5b, 0x88, 0x30, 0x20, 0x72, 0xe2, 0x85, 0xdc, 0x0b, 0x1a, 0x31, 0xbc, 0xe1, 0xc1, 0xf1, - 0xc6, 0x5b, 0xe6, 0xed, 0x03, 0xf3, 0x06, 0x5c, 0xdf, 0x65, 0x76, 0x10, 0xda, 0x0e, 0xf7, 0x68, - 0xb0, 0xf0, 0x8a, 0x32, 0x4e, 0x54, 0x1b, 0x9a, 0x2f, 0x61, 0x22, 0x76, 0xb4, 0x1d, 0x85, 0xba, - 0x9b, 0x96, 0x00, 0x9a, 0x51, 0xb8, 0x4f, 0x88, 0xc5, 0x4f, 0x02, 0x7d, 0x9d, 0x5a, 0x4a, 0xf6, - 0x62, 0xc6, 0xda, 0x7f, 0x51, 0xd9, 0xed, 0x9e, 0x04, 0xe6, 0x75, 0xb8, 0x16, 0x3b, 0xc7, 0x84, - 0xb3, 0x53, 0xe5, 0xd5, 0x80, 0xc9, 0xc4, 0x41, 0xd3, 0xb7, 0xf5, 0xc9, 0xfd, 0xc4, 0xc9, 0x0e, - 0xb7, 0x79, 0x14, 0xaa, 0x88, 0x26, 0x21, 0x7b, 0x18, 0x36, 0x64, 0x28, 0x45, 0xed, 0x4a, 0x08, - 0x4c, 0x0a, 0xd5, 0x17, 0xcc, 0xe3, 0x44, 0x94, 0x24, 0xe0, 0x4a, 0xf7, 0x2b, 0xc8, 0x7b, 0xf2, - 0x67, 0x68, 0x64, 0x66, 0xb2, 0xb3, 0xa5, 0x07, 0x37, 0x52, 0x42, 0x57, 0x06, 0x2d, 0xd6, 0xd1, - 0xfa, 0x68, 0x06, 0x0a, 0x8c, 0x84, 0xd4, 0x3f, 0x22, 0xae, 0xcc, 0x7a, 0x41, 0x2b, 0xb4, 0xa5, - 0xe6, 0x6f, 0x19, 0xed, 0x71, 0x97, 0xd2, 0x2d, 0x5f, 0x8f, 0xf3, 0x02, 0x14, 0x3f, 0xa8, 0xfa, - 0x1d, 0x2b, 0xb4, 0x09, 0x55, 0xdb, 0xe1, 0x91, 0xed, 0x7f, 0x58, 0xdd, 0x2b, 0xca, 0xb8, 0x53, - 0xf5, 0x09, 0x40, 0x5b, 0x4d, 0x4c, 0x5e, 0x47, 0x1e, 0x23, 0xe1, 0xee, 0x89, 0x1a, 0x08, 0x73, - 0x07, 0x26, 0x96, 0x68, 0xe0, 0x7a, 0x22, 0xbd, 0xab, 0xb6, 0xe7, 0xeb, 0x46, 0x40, 0x5f, 0xc3, - 0xa8, 0xf6, 0x7e, 0x64, 0xfb, 0x11, 0xd1, 0x77, 0x48, 0x23, 0xbd, 0xe7, 0xe2, 0x1c, 0x97, 0x94, - 0xb6, 0xfc, 0x61, 0xfe, 0x91, 0x01, 0xa4, 0xb8, 0x90, 0xfc, 0x40, 0x9c, 0x56, 0x73, 0xa1, 0x1a, - 0xe4, 0x0f, 0x49, 0x18, 0xda, 0x0d, 0x92, 0x28, 0x5b, 0x4b, 0x88, 0x1e, 0x41, 0x51, 0xb3, 0x8b, - 0x4e, 0x76, 0x1f, 0x96, 0x6d, 0xe5, 0xab, 0x6d, 0x80, 0xe6, 0xa1, 0xd0, 0x6a, 0x75, 0x4d, 0x41, - 0xef, 0x33, 0x6e, 0xeb, 0x9b, 0x9f, 0x41, 0x71, 0x87, 0x04, 0x83, 0x85, 0xf9, 0x24, 0x57, 0x18, - 0xae, 0x66, 0x45, 0x33, 0x63, 0x7b, 0x8f, 0x3f, 0x66, 0x34, 0x6a, 0x2e, 0x13, 0x9f, 0xb4, 0x47, - 0xc8, 0x82, 0x49, 0xbd, 0x80, 0x96, 0x28, 0x63, 0x51, 0x53, 0x64, 0x56, 0x01, 0xdf, 0x82, 0xa2, - 0x5c, 0xe4, 0x56, 0x77, 0xe3, 0x16, 0xa4, 0x78, 0x23, 0x6c, 0x20, 0x13, 0x8a, 0x4d, 0x46, 0x1d, - 0x12, 0x86, 0x5d, 0xfd, 0xd6, 0x11, 0x8b, 0x42, 0x6a, 0x07, 0xb1, 0x8e, 0x33, 0xdf, 0x94, 0xa1, - 0x24, 0xbf, 0x96, 0x09, 0xb7, 0x3d, 0x1f, 0x6d, 0x43, 0x35, 0xa0, 0xdc, 0x4a, 0xac, 0x4c, 0x55, - 0xc4, 0x3b, 0x29, 0x69, 0x49, 0x59, 0xdb, 0x78, 0x2c, 0x48, 0x08, 0xd1, 0x06, 0x54, 0xd4, 0x8a, - 0x12, 0xb8, 0x7b, 0x62, 0x75, 0xe9, 0x22, 0x7d, 0xd2, 0x8b, 0x46, 0x13, 0x2b, 0x0e, 0x97, 0x59, - 0x5c, 0x86, 0x9e, 0x03, 0x52, 0x70, 0x07, 0xe4, 0xd4, 0x6a, 0x2d, 0x02, 0x5d, 0xb9, 0xd9, 0x5e, - 0x88, 0xdd, 0x6b, 0x0e, 0x57, 0x59, 0x97, 0x18, 0xfd, 0x04, 0x33, 0x92, 0x7d, 0x8f, 0x25, 0x49, - 0x5b, 0x51, 0x87, 0xa5, 0x2d, 0x4f, 0xd3, 0xb4, 0x5e, 0x2a, 0x0f, 0x53, 0xdf, 0x0e, 0xef, 0xa3, - 0x77, 0x7c, 0x93, 0xf5, 0xd3, 0x41, 0x2f, 0xe1, 0x2a, 0xef, 0x50, 0x96, 0x65, 0x2b, 0x7a, 0x35, - 0x46, 0xa4, 0xcb, 0x7b, 0xfd, 0x39, 0x33, 0xce, 0xc5, 0x18, 0xf1, 0x4b, 0x07, 0x08, 0x43, 0x35, - 0x0e, 0x2e, 0xb8, 0xd5, 0xb8, 0x22, 0x91, 0x3f, 0xed, 0x8f, 0xdc, 0xa6, 0x72, 0x5c, 0xe1, 0x49, - 0x29, 0x7a, 0x06, 0xe3, 0x71, 0x4c, 0x26, 0x78, 0xd9, 0xc8, 0xf7, 0xac, 0x43, 0x2a, 0x85, 0xe3, - 0x78, 0x58, 0x52, 0x8c, 0xbe, 0x85, 0xf8, 0x05, 0xc4, 0x23, 0x84, 0x47, 0xa1, 0x51, 0x90, 0xb8, - 0x77, 0xfb, 0xe3, 0xc6, 0x78, 0x1e, 0xc7, 0x63, 0x53, 0x72, 0xb4, 0x0a, 0xa3, 0xc7, 0x82, 0x70, - 0x2d, 0x45, 0xd2, 0x46, 0x51, 0x62, 0x7e, 0x9c, 0x82, 0xd9, 0xbd, 0x09, 0x70, 0xe9, 0xb8, 0x23, - 0x41, 0x8f, 0xa1, 0xac, 0x70, 0x38, 0xa5, 0x16, 0xf5, 0x5d, 0x03, 0xfa, 0x03, 0xc5, 0xc6, 0x4d, - 0x03, 0x29, 0x89, 0x98, 0x0c, 0xda, 0xb4, 0x98, 0xe6, 0x56, 0xb9, 0x22, 0x4b, 0x3d, 0x27, 0xe3, - 0x32, 0x09, 0xe3, 0x32, 0x8d, 0xcb, 0x44, 0x91, 0x9d, 0x16, 0x27, 0x5b, 0x7b, 0x92, 0x94, 0x8d, - 0xd1, 0x9e, 0x45, 0x4e, 0xa3, 0x6f, 0x5c, 0x71, 0x92, 0x52, 0xf4, 0x14, 0xc6, 0x14, 0x15, 0x30, - 0x4d, 0xc9, 0x46, 0xb9, 0x67, 0x84, 0x97, 0xa9, 0x1b, 0x97, 0xfd, 0xb8, 0x4c, 0x44, 0x18, 0x50, - 0x97, 0x58, 0x51, 0xe7, 0x71, 0x6b, 0x8c, 0xf5, 0x8c, 0x30, 0xed, 0x19, 0x8c, 0x2b, 0x41, 0x52, - 0x8a, 0xee, 0x43, 0x2e, 0x24, 0x81, 0x6b, 0x54, 0x24, 0xce, 0x47, 0x29, 0x38, 0x6d, 0x8a, 0xc6, - 0x52, 0x53, 0x31, 0xc8, 0x1e, 0xb7, 0x1a, 0x82, 0x83, 0x2d, 0x57, 0x91, 0xb0, 0x51, 0xed, 0xc3, - 0x20, 0x29, 0x7c, 0x2d, 0x18, 0x24, 0x29, 0x16, 0x9d, 0xab, 0xff, 0x74, 0x58, 0x4e, 0x9b, 0xc2, - 0x8d, 0xf1, 0x9e, 0x9d, 0x9b, 0x4e, 0xf7, 0x78, 0x9c, 0x75, 0xcb, 0x25, 0x85, 0x6a, 0xe4, 0x56, - 0xcf, 0xa1, 0xde, 0x14, 0x7a, 0x89, 0xe4, 0xc5, 0xe3, 0x30, 0x26, 0xeb, 0x1e, 0x31, 0x26, 0x1f, - 0x4e, 0xc6, 0xe4, 0x20, 0x23, 0x16, 0x7b, 0x64, 0x25, 0x46, 0x4c, 0xc9, 0xc5, 0x88, 0x29, 0x72, - 0xde, 0x93, 0x8f, 0x67, 0xe3, 0x46, 0xcf, 0xc9, 0xe8, 0x7e, 0x62, 0xe3, 0x12, 0xeb, 0x48, 0xe6, - 0x73, 0x67, 0x6f, 0xa7, 0x33, 0xe6, 0x5d, 0xb9, 0x9a, 0xb6, 0x69, 0x28, 0x3b, 0x12, 0x4d, 0xc1, - 0x88, 0x17, 0xb8, 0xe4, 0x44, 0xee, 0xa3, 0x11, 0xbd, 0xe0, 0x94, 0xc8, 0xfc, 0x3d, 0x0b, 0x23, - 0x83, 0xbd, 0x16, 0xbe, 0x4f, 0xf2, 0x2c, 0x23, 0xf2, 0x6f, 0x8e, 0x5c, 0x20, 0x63, 0xa9, 0xf9, - 0x4c, 0xdc, 0x5e, 0x2a, 0x6b, 0x48, 0xc4, 0x2f, 0x9d, 0xa0, 0x25, 0x28, 0x47, 0x01, 0x39, 0x69, - 0xd2, 0x90, 0xb8, 0x72, 0xa0, 0x73, 0x83, 0xbc, 0x79, 0xf1, 0x68, 0xdb, 0x48, 0x0c, 0x72, 0x1d, - 0x4a, 0x94, 0x79, 0x0d, 0x2f, 0xb0, 0x44, 0xb3, 0xcb, 0x15, 0x30, 0xb2, 0x38, 0x26, 0x7c, 0xbe, - 0x3b, 0x9f, 0xbe, 0x22, 0xc6, 0x62, 0x6d, 0x19, 0x83, 0x52, 0x11, 0xbf, 0xd0, 0x97, 0x70, 0xc5, - 0x95, 0xeb, 0x5b, 0x93, 0x7a, 0x9a, 0xbb, 0xd8, 0x92, 0xc7, 0x5a, 0x1b, 0x7d, 0xd1, 0xca, 0x68, - 0xbe, 0x9f, 0x59, 0xab, 0x00, 0x3a, 0xd7, 0xe8, 0x21, 0x64, 0x03, 0x7a, 0xac, 0x29, 0x79, 0xa0, - 0x47, 0xa5, 0xd0, 0x9f, 0xcf, 0xfd, 0xfa, 0x76, 0x7a, 0x48, 0xbd, 0x82, 0xee, 0xcd, 0x03, 0xba, - 0x9c, 0x56, 0x54, 0x80, 0xdc, 0xe6, 0xd6, 0xe6, 0x4a, 0x75, 0x08, 0x95, 0x20, 0xbf, 0xb8, 0xb0, - 0xb4, 0xbe, 0xb5, 0xba, 0x5a, 0xcd, 0xa0, 0x32, 0x14, 0xd7, 0x36, 0x36, 0x56, 0x96, 0xd7, 0x16, - 0x76, 0x57, 0xaa, 0xc3, 0x8b, 0xb7, 0xce, 0xfe, 0xad, 0x0d, 0x9d, 0x5d, 0xd4, 0x32, 0x7f, 0x5d, - 0xd4, 0x32, 0x7f, 0x5f, 0xd4, 0x32, 0xff, 0x5c, 0xd4, 0x32, 0xbf, 0xfc, 0x57, 0x1b, 0xfa, 0x2e, - 0xaf, 0x83, 0xfe, 0x3f, 0x00, 0x00, 0xff, 0xff, 0x32, 0xb5, 0x67, 0x1c, 0x0f, 0x11, 0x00, 0x00, + // 1594 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x57, 0x3b, 0x73, 0x1b, 0x47, + 0x12, 0x26, 0x08, 0x50, 0x00, 0x1a, 0x04, 0x01, 0x8e, 0x28, 0x6a, 0xc5, 0x3a, 0x81, 0xd4, 0x9e, + 0x4e, 0x47, 0x29, 0x20, 0x74, 0xba, 0xd3, 0x55, 0x1d, 0x4f, 0x09, 0x9f, 0x3a, 0x8a, 0xe2, 0xa3, + 0x86, 0x94, 0x74, 0x65, 0xb9, 0x6a, 0x6b, 0xb9, 0x3b, 0x04, 0xd7, 0x5c, 0xec, 0x40, 0xb3, 0xb3, + 0x7c, 0x38, 0xf0, 0x1f, 0x70, 0xe2, 0xd0, 0xce, 0x94, 0x38, 0x73, 0xf9, 0x57, 0x38, 0x60, 0xe8, + 0xd0, 0x11, 0xcb, 0xa6, 0x63, 0xff, 0x01, 0x45, 0xae, 0x79, 0x00, 0xd8, 0x05, 0x17, 0x10, 0x4a, + 0xd9, 0xa0, 0xa7, 0xfb, 0xeb, 0xd9, 0x7e, 0x7c, 0xdd, 0x00, 0xd3, 0xa1, 0xce, 0x31, 0xa3, 0xb6, + 0x73, 0x54, 0x6f, 0x1d, 0x37, 0xea, 0xf2, 0xd4, 0x3a, 0xa8, 0x13, 0xc6, 0x28, 0x0b, 0x17, 0x5a, + 0x8c, 0x72, 0x8a, 0x26, 0x3b, 0x3a, 0x0b, 0xfa, 0x7e, 0xe6, 0x7e, 0xba, 0x59, 0x93, 0x70, 0xdb, + 0xb5, 0xb9, 0xad, 0x0c, 0x67, 0xe6, 0xd2, 0xb5, 0x62, 0x1a, 0x0f, 0x92, 0x1a, 0x11, 0xf7, 0xfc, + 0xfa, 0x91, 0xef, 0xd4, 0xb9, 0xd7, 0x24, 0x21, 0xb7, 0x9b, 0x2d, 0xad, 0x37, 0xd5, 0xa0, 0x0d, + 0x2a, 0x8f, 0x75, 0x71, 0x52, 0x52, 0xf3, 0xeb, 0x51, 0xb8, 0xb9, 0x4d, 0xf9, 0x4b, 0x62, 0x87, + 0xe4, 0x7f, 0xd4, 0x77, 0x09, 0x5b, 0x13, 0xef, 0x46, 0xab, 0x90, 0x67, 0xa4, 0xe5, 0x7b, 0x8e, + 0x6d, 0x64, 0xe6, 0x32, 0xf3, 0xa5, 0x27, 0xf7, 0x17, 0xae, 0x7d, 0xc2, 0x02, 0x56, 0x1a, 0xab, + 0x24, 0x74, 0x98, 0xd7, 0xe2, 0x94, 0x2d, 0xe7, 0x2e, 0x2e, 0x67, 0x47, 0x70, 0xdb, 0x14, 0x3d, + 0x87, 0x71, 0x5f, 0x20, 0x5b, 0x47, 0x12, 0xda, 0x18, 0x1d, 0x1e, 0x0a, 0x97, 0xfc, 0xee, 0x9b, + 0xd0, 0x53, 0x28, 0x30, 0x3b, 0x68, 0x10, 0xcb, 0x73, 0x8d, 0xec, 0x5c, 0x66, 0x3e, 0xbb, 0x3c, + 0x23, 0x3c, 0x5d, 0x5d, 0xce, 0xe6, 0xb1, 0x90, 0x6f, 0xac, 0x7e, 0xe8, 0x1e, 0x71, 0x5e, 0xea, + 0x6e, 0xb8, 0x68, 0x01, 0xc6, 0x24, 0x8a, 0x91, 0x93, 0x8e, 0x8d, 0x14, 0xc7, 0xf2, 0xcb, 0xb1, + 0x52, 0x33, 0xa7, 0x61, 0x6a, 0x9b, 0xba, 0xe4, 0x55, 0x60, 0x9f, 0xd8, 0x9e, 0x6f, 0x1f, 0xf8, + 0x44, 0x46, 0xc3, 0xdc, 0x04, 0x24, 0xb1, 0xb7, 0x29, 0x5f, 0xa7, 0x51, 0xe0, 0xaa, 0x18, 0xc5, + 0x1f, 0x95, 0x19, 0xfa, 0x51, 0xe6, 0x77, 0xa3, 0x70, 0x4b, 0x0a, 0x37, 0xc9, 0xf9, 0x96, 0x17, + 0x36, 0x6d, 0xee, 0x1c, 0x29, 0xc0, 0x7f, 0xc2, 0x24, 0x23, 0xef, 0x22, 0x12, 0x72, 0x2b, 0xe4, + 0x36, 0xe3, 0xd6, 0x31, 0x39, 0x97, 0xc8, 0xe3, 0xcb, 0xf9, 0x0f, 0x97, 0xb3, 0xd9, 0x4d, 0x72, + 0x8e, 0x2b, 0x5a, 0x63, 0x4f, 0x28, 0x6c, 0x92, 0x73, 0x54, 0x87, 0xb6, 0xc8, 0x22, 0x81, 0x2b, + 0x4d, 0x46, 0x93, 0x26, 0x65, 0x7d, 0xbf, 0x16, 0xb8, 0xc2, 0x60, 0x0b, 0xaa, 0x4d, 0xed, 0x96, + 0xb8, 0x96, 0x7c, 0x95, 0x8c, 0x69, 0xe9, 0x89, 0x99, 0x96, 0x18, 0x71, 0x1f, 0x4b, 0x4b, 0xa5, + 0x6b, 0x2b, 0xaf, 0xd0, 0x26, 0x54, 0xc2, 0xa8, 0xd1, 0x20, 0x21, 0xef, 0xa0, 0xe5, 0x86, 0x46, + 0x9b, 0xe8, 0x98, 0xca, 0x1b, 0x73, 0x17, 0xaa, 0xf2, 0xb0, 0xce, 0xe8, 0x97, 0x24, 0x50, 0x51, + 0x79, 0x06, 0x39, 0x97, 0x84, 0x8e, 0xae, 0xc3, 0x21, 0x50, 0x75, 0x15, 0x4a, 0x2b, 0xf3, 0xa7, + 0x0c, 0x98, 0x98, 0xd8, 0xee, 0x1b, 0x8f, 0x1f, 0x79, 0xc1, 0xab, 0xc0, 0x21, 0x8c, 0xdb, 0x5e, + 0xc0, 0xcf, 0x37, 0x02, 0x4e, 0xd8, 0x89, 0xed, 0x2b, 0x27, 0x2f, 0x60, 0x82, 0x11, 0xdb, 0xb5, + 0x3a, 0x5d, 0xa3, 0xdd, 0xdd, 0x8d, 0xb9, 0x13, 0xad, 0xb5, 0x70, 0xe4, 0x3b, 0x0b, 0xfb, 0x6d, + 0x25, 0xed, 0xa9, 0x2c, 0x4c, 0x3b, 0x42, 0x84, 0x01, 0x91, 0x33, 0x2f, 0xe4, 0x5e, 0xd0, 0x88, + 0xe1, 0x8d, 0x0e, 0x8f, 0x37, 0xd9, 0x36, 0xef, 0x5c, 0x98, 0x77, 0xe0, 0xf6, 0x3e, 0xb3, 0x83, + 0xd0, 0x76, 0xb8, 0x47, 0x83, 0xa5, 0x03, 0xca, 0x38, 0x51, 0x65, 0x68, 0xbe, 0x85, 0xa9, 0xd8, + 0xd5, 0x6e, 0x14, 0xea, 0x6a, 0x5a, 0x01, 0x68, 0x45, 0xe1, 0x11, 0x21, 0x16, 0x3f, 0x0b, 0xf4, + 0xe7, 0xd4, 0x52, 0xa2, 0x17, 0x33, 0xd6, 0xfe, 0x8b, 0xca, 0x6e, 0xff, 0x2c, 0x30, 0x6f, 0xc3, + 0xad, 0xd8, 0x3d, 0x26, 0x9c, 0x9d, 0x2b, 0xaf, 0x06, 0x4c, 0x27, 0x2e, 0x5a, 0xbe, 0xad, 0x6f, + 0x1e, 0x27, 0x6e, 0xf6, 0xb8, 0xcd, 0xa3, 0x50, 0xbd, 0x68, 0x1a, 0xb2, 0xcd, 0xb0, 0x21, 0x9f, + 0x52, 0xd4, 0xae, 0x84, 0xc0, 0xa4, 0x50, 0x7d, 0xc3, 0x3c, 0x4e, 0x44, 0x4a, 0x02, 0xae, 0x74, + 0xff, 0x03, 0x79, 0x4f, 0xfe, 0x0c, 0x8d, 0xcc, 0x5c, 0x76, 0xbe, 0xf4, 0xe4, 0x4e, 0xca, 0xd3, + 0x95, 0x41, 0x9b, 0x75, 0xb4, 0x3e, 0x9a, 0x83, 0x02, 0x23, 0x21, 0xf5, 0x4f, 0x88, 0x2b, 0xa3, + 0x5e, 0xd0, 0x0a, 0x1d, 0xa9, 0xf9, 0x7d, 0x46, 0x7b, 0xdc, 0xa7, 0x74, 0xc7, 0xd7, 0xed, 0xbc, + 0x04, 0xc5, 0x4f, 0xca, 0x7e, 0xd7, 0x0a, 0x6d, 0x43, 0xd5, 0x76, 0x78, 0x64, 0xfb, 0x9f, 0x96, + 0xf7, 0x8a, 0x32, 0xee, 0x66, 0x7d, 0x0a, 0xd0, 0x4e, 0x0b, 0x93, 0x77, 0x91, 0xc7, 0x48, 0xb8, + 0x7f, 0xa6, 0x1a, 0xc2, 0xdc, 0x83, 0xa9, 0x15, 0x1a, 0xb8, 0x9e, 0x08, 0xef, 0xba, 0xed, 0xf9, + 0xba, 0x10, 0xd0, 0x7f, 0x61, 0x5c, 0x7b, 0x3f, 0xb1, 0xfd, 0x88, 0xe8, 0x6f, 0x48, 0x23, 0xbd, + 0xd7, 0xe2, 0x1e, 0x97, 0x94, 0xb6, 0xfc, 0x61, 0xfe, 0x98, 0x01, 0xa4, 0xb8, 0x90, 0x7c, 0x41, + 0x9c, 0x76, 0x71, 0xa1, 0x1a, 0xe4, 0x9b, 0x24, 0x0c, 0xed, 0x06, 0x49, 0xa4, 0xad, 0x2d, 0x44, + 0xcf, 0xa0, 0xa8, 0xd9, 0x45, 0x07, 0x7b, 0x00, 0xcb, 0xb6, 0xe3, 0xd5, 0x31, 0x40, 0x8b, 0x50, + 0x68, 0x97, 0xba, 0xa6, 0xa0, 0x8f, 0x19, 0x77, 0xf4, 0xcd, 0x7f, 0x40, 0x71, 0x8f, 0x04, 0xc3, + 0x3d, 0xf3, 0x45, 0xae, 0x30, 0x5a, 0xcd, 0x0a, 0x7a, 0x5f, 0x6a, 0x1e, 0x78, 0x8d, 0x88, 0x46, + 0xe1, 0x0a, 0x6d, 0x36, 0x3d, 0x55, 0x6b, 0xa2, 0xc8, 0xb1, 0x7d, 0xc8, 0x9f, 0x33, 0x1a, 0xb5, + 0x56, 0x89, 0x4f, 0x3a, 0xad, 0x65, 0xc1, 0xb4, 0x1e, 0x4c, 0x2b, 0x94, 0xb1, 0xa8, 0x25, 0x22, + 0xae, 0x1c, 0xde, 0x83, 0xa2, 0x1c, 0xf0, 0x56, 0x6f, 0x41, 0x17, 0xa4, 0x78, 0x2b, 0x6c, 0x20, + 0x13, 0x8a, 0x2d, 0x46, 0x1d, 0x12, 0x86, 0x3d, 0x75, 0xd8, 0x15, 0x8b, 0x04, 0x6b, 0x07, 0xb1, + 0x4a, 0x34, 0xff, 0x28, 0x43, 0x49, 0x9e, 0x56, 0x09, 0xb7, 0x3d, 0x1f, 0xed, 0x42, 0x35, 0xa0, + 0xdc, 0x4a, 0x8c, 0x52, 0x95, 0xdc, 0x07, 0x29, 0xe1, 0x4a, 0x19, 0xe7, 0x78, 0x22, 0x48, 0x08, + 0xd1, 0x16, 0x54, 0xd4, 0xe8, 0x12, 0xb8, 0x87, 0x62, 0xa4, 0xe9, 0xe4, 0xfd, 0xad, 0x1f, 0xbd, + 0x26, 0x46, 0x1f, 0x2e, 0xb3, 0xb8, 0x0c, 0xbd, 0x06, 0xa4, 0xe0, 0x8e, 0xc9, 0xb9, 0xd5, 0x1e, + 0x10, 0x3a, 0xa3, 0xf3, 0xfd, 0x10, 0x7b, 0xc7, 0x1f, 0xae, 0xb2, 0x1e, 0x31, 0xfa, 0x0a, 0xe6, + 0x24, 0x2b, 0x9f, 0x4a, 0xf2, 0xb6, 0xa2, 0x2e, 0x7b, 0x5b, 0x9e, 0xa6, 0x6f, 0x3d, 0x6c, 0x9e, + 0xa6, 0xee, 0x14, 0x1f, 0xa3, 0x7d, 0x7c, 0x97, 0x0d, 0xd2, 0x41, 0x6f, 0xe1, 0x26, 0xef, 0x52, + 0x99, 0x65, 0x2b, 0xda, 0x35, 0xc6, 0xa4, 0xcb, 0x47, 0x83, 0xb9, 0x34, 0xce, 0xd1, 0x18, 0xf1, + 0x6b, 0x17, 0x08, 0x43, 0x35, 0x0e, 0x2e, 0x38, 0xd7, 0xb8, 0x21, 0x91, 0xff, 0x3e, 0x18, 0xb9, + 0x43, 0xf1, 0xb8, 0xc2, 0x93, 0x52, 0xf4, 0x0a, 0x26, 0xe3, 0x98, 0x4c, 0xf0, 0xb5, 0x91, 0xef, + 0x9b, 0x87, 0x54, 0x6a, 0xc7, 0xf1, 0x67, 0x49, 0x31, 0xfa, 0x3f, 0xc4, 0x3f, 0x40, 0x2c, 0x27, + 0x3c, 0x0a, 0x8d, 0x82, 0xc4, 0x7d, 0x38, 0x18, 0x37, 0xc6, 0xff, 0x38, 0xfe, 0x36, 0x25, 0x47, + 0xeb, 0x30, 0x7e, 0x2a, 0x88, 0xd8, 0x52, 0xe4, 0x6d, 0x14, 0x25, 0xe6, 0x5f, 0x53, 0x30, 0x7b, + 0x27, 0x04, 0x2e, 0x9d, 0x76, 0x25, 0xe8, 0x39, 0x94, 0x15, 0x0e, 0xa7, 0xd4, 0xa2, 0xbe, 0x6b, + 0xc0, 0x60, 0xa0, 0x58, 0xbb, 0x69, 0x20, 0x25, 0x11, 0x9d, 0x41, 0x5b, 0x16, 0xd3, 0x9c, 0x2b, + 0x47, 0x67, 0xa9, 0x6f, 0x67, 0x5c, 0x27, 0x67, 0x5c, 0xa6, 0x71, 0x99, 0x48, 0xb2, 0xd3, 0xe6, + 0x6a, 0xeb, 0x50, 0x92, 0xb5, 0x31, 0xde, 0x37, 0xc9, 0x69, 0xb4, 0x8e, 0x2b, 0x4e, 0x52, 0x8a, + 0x5e, 0xc2, 0x84, 0xa2, 0x02, 0xa6, 0xa9, 0xda, 0x28, 0xf7, 0x7d, 0xe1, 0x75, 0x4a, 0xc7, 0x65, + 0x3f, 0x2e, 0x13, 0x2f, 0x0c, 0xa8, 0x4b, 0xac, 0xa8, 0xbb, 0xf4, 0x1a, 0x13, 0x7d, 0x5f, 0x98, + 0xb6, 0x1e, 0xe3, 0x4a, 0x90, 0x94, 0xa2, 0xc7, 0x90, 0x0b, 0x49, 0xe0, 0x1a, 0x15, 0x89, 0xf3, + 0x97, 0x14, 0x9c, 0x0e, 0x75, 0x63, 0xa9, 0xa9, 0x18, 0xe4, 0x90, 0x5b, 0x0d, 0xc1, 0xc1, 0x96, + 0xab, 0x48, 0xd8, 0xa8, 0x0e, 0x60, 0x90, 0x14, 0xbe, 0x16, 0x0c, 0x92, 0x14, 0x8b, 0xca, 0xd5, + 0x7f, 0x46, 0x2c, 0xa7, 0x43, 0xe1, 0xc6, 0x64, 0xdf, 0xca, 0x4d, 0xa7, 0x7b, 0x3c, 0xc9, 0x7a, + 0xe5, 0x92, 0x42, 0x35, 0x72, 0xbb, 0xe6, 0x50, 0x7f, 0x0a, 0xbd, 0x46, 0xf2, 0x62, 0x69, 0x8c, + 0xc9, 0x7a, 0x5b, 0x8c, 0xc9, 0x85, 0xca, 0x98, 0x1e, 0xa6, 0xc5, 0x62, 0xcb, 0x57, 0xa2, 0xc5, + 0x94, 0x5c, 0xb4, 0x98, 0x22, 0xe7, 0x43, 0xb9, 0x54, 0x1b, 0x77, 0xfa, 0x76, 0x46, 0xef, 0xea, + 0x8d, 0x4b, 0xac, 0x2b, 0x11, 0x85, 0x62, 0xb7, 0xa7, 0xa7, 0xe5, 0xc8, 0xf1, 0x69, 0xcc, 0xf4, + 0x2d, 0x94, 0xb4, 0x41, 0x8b, 0x2b, 0x76, 0x52, 0xba, 0x98, 0xbb, 0x78, 0x3f, 0x9b, 0x31, 0x1f, + 0xca, 0x71, 0xb7, 0x4b, 0x43, 0x59, 0xe5, 0x68, 0x06, 0xc6, 0xbc, 0xc0, 0x25, 0x67, 0x72, 0xc6, + 0x8d, 0xe9, 0xa1, 0xa9, 0x44, 0xe6, 0x0f, 0x59, 0x18, 0x1b, 0x6e, 0x33, 0xf9, 0x3c, 0xc9, 0xdd, + 0x8c, 0xc8, 0xbf, 0x54, 0x72, 0x28, 0x4d, 0xa4, 0xe6, 0x28, 0x11, 0x51, 0xa9, 0xac, 0x21, 0x11, + 0xbf, 0x76, 0x83, 0x56, 0xa0, 0x1c, 0x05, 0xe4, 0xac, 0x45, 0x43, 0xe2, 0x4a, 0x92, 0xc8, 0x0d, + 0xb3, 0x5f, 0xe3, 0xf1, 0x8e, 0x91, 0x20, 0x87, 0x3a, 0x94, 0x28, 0xf3, 0x1a, 0x5e, 0x60, 0x89, + 0x06, 0x92, 0x63, 0x65, 0x6c, 0x79, 0x42, 0xf8, 0xfc, 0x70, 0x39, 0x7b, 0x43, 0xb4, 0xda, 0xc6, + 0x2a, 0x06, 0xa5, 0x22, 0x7e, 0xa1, 0x7f, 0xc3, 0x0d, 0x57, 0xae, 0x04, 0x7a, 0x50, 0xa4, 0xb9, + 0x8b, 0x2d, 0x0e, 0x58, 0x6b, 0xa3, 0x7f, 0xb5, 0x23, 0x9a, 0x1f, 0x64, 0xd6, 0x4e, 0x80, 0x8e, + 0x35, 0x7a, 0x0a, 0xd9, 0x80, 0x9e, 0x6a, 0x9a, 0x1f, 0x6a, 0x81, 0x15, 0xfa, 0x8b, 0xb9, 0x6f, + 0xdf, 0xcf, 0x8e, 0xa8, 0x8d, 0xeb, 0xd1, 0x22, 0xa0, 0xeb, 0x61, 0x45, 0x05, 0xc8, 0x6d, 0xef, + 0x6c, 0xaf, 0x55, 0x47, 0x50, 0x09, 0xf2, 0xcb, 0x4b, 0x2b, 0x9b, 0x3b, 0xeb, 0xeb, 0xd5, 0x0c, + 0x2a, 0x43, 0x71, 0x63, 0x6b, 0x6b, 0x6d, 0x75, 0x63, 0x69, 0x7f, 0xad, 0x3a, 0xba, 0x7c, 0xef, + 0xe2, 0xb7, 0xda, 0xc8, 0xc5, 0x55, 0x2d, 0xf3, 0xf3, 0x55, 0x2d, 0xf3, 0xcb, 0x55, 0x2d, 0xf3, + 0xeb, 0x55, 0x2d, 0xf3, 0xcd, 0xef, 0xb5, 0x91, 0xcf, 0xf2, 0xfa, 0xd1, 0x7f, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x96, 0x64, 0x9c, 0x1f, 0x7b, 0x11, 0x00, 0x00, } diff --git a/pkg/roachpb/errors.proto b/pkg/roachpb/errors.proto index de762cafbc8d..4ae5e705b744 100644 --- a/pkg/roachpb/errors.proto +++ b/pkg/roachpb/errors.proto @@ -176,6 +176,12 @@ message SendError { reserved 2; } +// An AmbiguousCommitError indicates that an EndTransaction request +// may have succeeded or failed, but the response was not received and +// the final result is ambiguous. +message AmbiguousCommitError { +} + // A RaftGroupDeletedError indicates a raft group has been deleted for // the replica. message RaftGroupDeletedError { @@ -217,6 +223,7 @@ message ErrorDetail { optional NodeUnavailableError node_unavailable = 14; optional SendError send = 15; optional RangeFrozenError range_frozen = 25; + optional AmbiguousCommitError ambiguous_commit = 26; // TODO(kaneda): Following are added to preserve the type when // converting Go errors from/to proto Errors. Revisit this design. diff --git a/pkg/sql/ambiguous_commit_test.go b/pkg/sql/ambiguous_commit_test.go new file mode 100644 index 000000000000..4d4a4579eac6 --- /dev/null +++ b/pkg/sql/ambiguous_commit_test.go @@ -0,0 +1,163 @@ +// Copyright 2016 The Cockroach Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing +// permissions and limitations under the License. +// +// Author: Spencer Kimball (spencer@cockroachlabs.com) + +package sql_test + +import ( + "bytes" + "sync/atomic" + "testing" + + "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/storage" + "github.com/cockroachdb/cockroach/pkg/testutils" + "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" + "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" + "github.com/cockroachdb/cockroach/pkg/util" + "github.com/cockroachdb/cockroach/pkg/util/leaktest" + "github.com/pkg/errors" +) + +// TestAmbiguousCommit verifies that an ambiguous commit error is +// returned from sql.Exec in situations where an EndTransaction is +// part of a batch and the disposition of the batch request is unknown +// after a network failure or timeout. The goal here is to prevent +// spurious transaction retries after the initial transaction actually +// succeeded. In cases where there's an auto-generated primary key, +// this can result in silent duplications. In cases where the primary +// key is specified in advance, it can result in violated uniqueness +// constraints, or duplicate key violations. See #6053, #7604, and #10023. +func TestAmbiguousCommit(t *testing.T) { + defer leaktest.AfterTest(t)() + + // Create a command filter which prevents EndTransaction from + // returning a response. + params := base.TestServerArgs{} + committed := make(chan struct{}) + wait := make(chan struct{}) + var tableStartKey atomic.Value + var responseCount int32 + + // Prevent the first conditional put on table 51 from returning to + // waiting client in order to simulate a lost update or slow network + // link. + params.Knobs.Store = &storage.StoreTestingKnobs{ + TestingResponseFilter: func(ba roachpb.BatchRequest, br *roachpb.BatchResponse) *roachpb.Error { + req, ok := ba.GetArg(roachpb.ConditionalPut) + tsk := tableStartKey.Load() + if tsk == nil { + return nil + } + if !ok || !bytes.HasPrefix(req.Header().Key, tsk.([]byte)) { + return nil + } + // If this is the first write to the table, wait to respond to the + // client in order to simulate a retry. + if atomic.AddInt32(&responseCount, 1) == 1 { + close(committed) + <-wait + } + return nil + }, + } + testClusterArgs := base.TestClusterArgs{ + ReplicationMode: base.ReplicationAuto, + ServerArgs: params, + } + const numReplicas = 3 + tc := testcluster.StartTestCluster(t, numReplicas, testClusterArgs) + defer tc.Stopper().Stop() + if err := tc.WaitForFullReplication(); err != nil { + t.Fatal(err) + } + + sqlDB := sqlutils.MakeSQLRunner(t, tc.Conns[0]) + + _ = sqlDB.Exec(`CREATE DATABASE test`) + _ = sqlDB.Exec(`CREATE TABLE test.t (k SERIAL PRIMARY KEY, v INT)`) + + tableID := sqlutils.QueryTableID(t, tc.Conns[0], "test", "t") + tableStartKey.Store(keys.MakeTablePrefix(tableID)) + + // Wait for new table to split. + util.SucceedsSoon(t, func() error { + desc, err := tc.LookupRange(keys.MakeRowSentinelKey(tableStartKey.Load().([]byte))) + if err != nil { + t.Fatal(err) + } + if !desc.StartKey.Equal(tableStartKey.Load().([]byte)) { + return errors.Errorf("expected range start key %s; got %s", + tableStartKey.Load().([]byte), desc.StartKey) + } + return nil + }) + + // Lookup the lease. + tableRangeDesc, err := tc.LookupRange(keys.MakeRowSentinelKey(tableStartKey.Load().([]byte))) + if err != nil { + t.Fatal(err) + } + leaseHolder, err := tc.FindRangeLeaseHolder( + &tableRangeDesc, + &testcluster.ReplicationTarget{ + NodeID: tc.Servers[0].GetNode().Descriptor.NodeID, + StoreID: tc.Servers[0].GetFirstStoreID(), + }) + if err != nil { + t.Fatal(err) + } + + // In a goroutine, send an insert which will commit but not return + // from the leader (due to the command filter we installed on node 0). + sqlErrCh := make(chan error, 1) + go func() { + // Use a connection other than through the node which is the current + // leaseholder to ensure that we use GRPC instead of the local server. + // If we use a local server, the hanging response we simulate takes + // up the dist sender thread of execution because local requests are + // executed synchronously. + sqlConn := tc.Conns[leaseHolder.NodeID%numReplicas] + _, err := sqlConn.Exec(`INSERT INTO test.t (v) VALUES (1)`) + sqlErrCh <- err + close(wait) + }() + // Wait until the insert has committed. + <-committed + + // Find a node other than the current lease holder to transfer the lease to. + for i, s := range tc.Servers { + if leaseHolder.StoreID != s.GetFirstStoreID() { + if err := tc.TransferRangeLease(&tableRangeDesc, tc.Target(i)); err != nil { + t.Fatal(err) + } + break + } + } + + // Close the wait channel and wait for the error from the pending SQL insert. + if err := <-sqlErrCh; !testutils.IsError(err, "transaction commit result is ambiguous") { + t.Errorf("expected ambiguous commit error; got %v", err) + } + + // Verify a single row exists in the table. + var rowCount int + sqlDB.QueryRow(`SELECT COUNT(*) FROM test.t`).Scan(&rowCount) + if rowCount != 1 { + t.Errorf("expected 1 row but found %d", rowCount) + } +} diff --git a/pkg/storage/client_test.go b/pkg/storage/client_test.go index 6417201675ee..2ed81aaa36d2 100644 --- a/pkg/storage/client_test.go +++ b/pkg/storage/client_test.go @@ -509,6 +509,9 @@ func (t *multiTestContextKVTransport) SendNext(done chan<- kv.BatchCall) { } } +func (t *multiTestContextKVTransport) MoveToFront(replica roachpb.ReplicaDescriptor) { +} + func (t *multiTestContextKVTransport) Close() { t.cancel() } diff --git a/pkg/storage/replica.go b/pkg/storage/replica.go index 8cbe0cf6f1f7..27f035834181 100644 --- a/pkg/storage/replica.go +++ b/pkg/storage/replica.go @@ -1116,6 +1116,10 @@ func (r *Replica) Send( } if pErr != nil { log.Eventf(ctx, "replica.Send got error: %s", pErr) + } else { + if filter := r.store.cfg.TestingKnobs.TestingResponseFilter; filter != nil { + pErr = filter(ba, br) + } } return br, pErr } diff --git a/pkg/storage/replica_command.go b/pkg/storage/replica_command.go index c98545312786..d1c047fad4cb 100644 --- a/pkg/storage/replica_command.go +++ b/pkg/storage/replica_command.go @@ -2839,7 +2839,7 @@ func (r *Replica) AdminMerge( } // Commit this batch on its own to ensure that the transaction record // is created in the right place (our triggers rely on this). - log.Event(ctx, "updating left descriptor") + log.Event(ctx, "updating LHS descriptor") if err := txn.Run(b); err != nil { return err } diff --git a/pkg/storage/storagebase/base.go b/pkg/storage/storagebase/base.go index 2bb660cd8db6..0166f67b5812 100644 --- a/pkg/storage/storagebase/base.go +++ b/pkg/storage/storagebase/base.go @@ -46,3 +46,8 @@ func (f *FilterArgs) InRaftCmd() bool { // with the returned error. Note that in a multi-replica test this filter will // be run once for each replica and must produce consistent results each time. type ReplicaCommandFilter func(args FilterArgs) *roachpb.Error + +// ReplicaResponseFilter is used in unittests to modify the outbound +// response returned to a waiting client after a replica command has +// been processed. This filter is invoked only by the command proposer. +type ReplicaResponseFilter func(roachpb.BatchRequest, *roachpb.BatchResponse) *roachpb.Error diff --git a/pkg/storage/store.go b/pkg/storage/store.go index 0c0288a22046..4ed33dcd80d2 100644 --- a/pkg/storage/store.go +++ b/pkg/storage/store.go @@ -602,6 +602,10 @@ type StoreTestingKnobs struct { // If your filter is not idempotent, consider wrapping it in a // ReplayProtectionFilterWrapper. TestingCommandFilter storagebase.ReplicaCommandFilter + // TestingResponseFilter is called after the replica processes a + // command in order for unittests to modify the batch response, + // error returned to the client, or to simulate network failures. + TestingResponseFilter storagebase.ReplicaResponseFilter // If non-nil, BadChecksumPanic is called by CheckConsistency() instead of // panicking on a checksum mismatch. BadChecksumPanic func(roachpb.StoreIdent) diff --git a/pkg/testutils/sqlutils/table_id.go b/pkg/testutils/sqlutils/table_id.go new file mode 100644 index 000000000000..ec15bd1cb754 --- /dev/null +++ b/pkg/testutils/sqlutils/table_id.go @@ -0,0 +1,38 @@ +// Copyright 2016 The Cockroach Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing +// permissions and limitations under the License. +// +// Author: Spencer Kimball (spencer@cockroachlabs.com) + +package sqlutils + +import ( + gosql "database/sql" + "testing" +) + +// QueryTableID returns the table ID of the specified database.table +// using the system.namespace table. +func QueryTableID(t *testing.T, sqlDB *gosql.DB, dbName, tableName string) uint32 { + tableIDQuery := ` + SELECT tables.id FROM system.namespace tables + JOIN system.namespace dbs ON dbs.id = tables.parentid + WHERE dbs.name = $1 AND tables.name = $2 + ` + var tableID uint32 + result := sqlDB.QueryRow(tableIDQuery, dbName, tableName) + if err := result.Scan(&tableID); err != nil { + t.Fatal(err) + } + return tableID +}