mirror of https://github.com/tikv/client-go.git
clear RPC error only if prewrite succeeds (#187)
Signed-off-by: Yilin Chen <sticnarf@gmail.com>
This commit is contained in:
parent
666340265a
commit
97edee854c
|
|
@ -259,51 +259,3 @@ func (s *testAsyncCommitFailSuite) TestAsyncCommitContextCancelCausingUndetermin
|
||||||
s.NotNil(err)
|
s.NotNil(err)
|
||||||
s.NotNil(txn.GetCommitter().GetUndeterminedErr())
|
s.NotNil(txn.GetCommitter().GetUndeterminedErr())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAsyncCommitRPCErrorThenWriteConflict verifies that the determined failure error overwrites undetermined error.
|
|
||||||
func (s *testAsyncCommitFailSuite) TestAsyncCommitRPCErrorThenWriteConflict() {
|
|
||||||
// This test doesn't support tikv mode because it needs setting failpoint in unistore.
|
|
||||||
if *mockstore.WithTiKV {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
txn := s.beginAsyncCommit()
|
|
||||||
err := txn.Set([]byte("a"), []byte("va"))
|
|
||||||
s.Nil(err)
|
|
||||||
|
|
||||||
s.Nil(failpoint.Enable("tikvclient/rpcPrewriteResult", `1*return("timeout")->return("writeConflict")`))
|
|
||||||
defer func() {
|
|
||||||
s.Nil(failpoint.Disable("tikvclient/rpcPrewriteResult"))
|
|
||||||
}()
|
|
||||||
|
|
||||||
ctx := context.WithValue(context.Background(), util.SessionID, uint64(1))
|
|
||||||
err = txn.Commit(ctx)
|
|
||||||
s.NotNil(err)
|
|
||||||
s.Nil(txn.GetCommitter().GetUndeterminedErr())
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestAsyncCommitRPCErrorThenWriteConflictInChild verifies that the determined failure error in a child recursion
|
|
||||||
// overwrites the undetermined error in the parent.
|
|
||||||
func (s *testAsyncCommitFailSuite) TestAsyncCommitRPCErrorThenWriteConflictInChild() {
|
|
||||||
// This test doesn't support tikv mode because it needs setting failpoint in unistore.
|
|
||||||
if *mockstore.WithTiKV {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
txn := s.beginAsyncCommit()
|
|
||||||
err := txn.Set([]byte("a"), []byte("va"))
|
|
||||||
s.Nil(err)
|
|
||||||
|
|
||||||
s.Nil(failpoint.Enable("tikvclient/rpcPrewriteResult", `1*return("timeout")->return("writeConflict")`))
|
|
||||||
s.Nil(failpoint.Enable("tikvclient/forceRecursion", `return`))
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
s.Nil(failpoint.Disable("tikvclient/rpcPrewriteResult"))
|
|
||||||
s.Nil(failpoint.Disable("tikvclient/forceRecursion"))
|
|
||||||
}()
|
|
||||||
|
|
||||||
ctx := context.WithValue(context.Background(), util.SessionID, uint64(1))
|
|
||||||
err = txn.Commit(ctx)
|
|
||||||
s.NotNil(err)
|
|
||||||
s.Nil(txn.GetCommitter().GetUndeterminedErr())
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -584,8 +584,6 @@ func (s *RegionRequestSender) SendReqCtx(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Clear the RPC Error since the request is evaluated successfully on a store.
|
|
||||||
s.rpcError = nil
|
|
||||||
if s.leaderReplicaSelector != nil {
|
if s.leaderReplicaSelector != nil {
|
||||||
s.leaderReplicaSelector.OnSendSuccess()
|
s.leaderReplicaSelector.OnSendSuccess()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,8 +163,6 @@ func (s *testRegionRequestToSingleStoreSuite) TestOnSendFailedWithStoreRestart()
|
||||||
resp, err = s.regionRequestSender.SendReq(s.bo, req, region.Region, time.Second)
|
resp, err = s.regionRequestSender.SendReq(s.bo, req, region.Region, time.Second)
|
||||||
s.Nil(err)
|
s.Nil(err)
|
||||||
s.NotNil(resp.Resp)
|
s.NotNil(resp.Resp)
|
||||||
// The RPC error should be nil since it's evaluated successfully.
|
|
||||||
s.Nil(s.regionRequestSender.rpcError)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *testRegionRequestToSingleStoreSuite) TestOnSendFailedWithCloseKnownStoreThenUseNewOne() {
|
func (s *testRegionRequestToSingleStoreSuite) TestOnSendFailedWithCloseKnownStoreThenUseNewOne() {
|
||||||
|
|
|
||||||
10
tikv/2pc.go
10
tikv/2pc.go
|
|
@ -120,7 +120,6 @@ type twoPhaseCommitter struct {
|
||||||
maxCommitTS uint64
|
maxCommitTS uint64
|
||||||
prewriteStarted bool
|
prewriteStarted bool
|
||||||
prewriteCancelled uint32
|
prewriteCancelled uint32
|
||||||
prewriteFailed uint32
|
|
||||||
useOnePC uint32
|
useOnePC uint32
|
||||||
onePCCommitTS uint64
|
onePCCommitTS uint64
|
||||||
|
|
||||||
|
|
@ -1126,13 +1125,10 @@ func (c *twoPhaseCommitter) execute(ctx context.Context) (err error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
err = c.prewriteMutations(bo, c.mutations)
|
err = c.prewriteMutations(bo, c.mutations)
|
||||||
|
|
||||||
// Return an undetermined error only if we don't know the transaction fails.
|
|
||||||
// If it fails due to a write conflict or a already existed unique key, we
|
|
||||||
// needn't return an undetermined error even if such an error is set.
|
|
||||||
if atomic.LoadUint32(&c.prewriteFailed) == 1 {
|
|
||||||
c.setUndeterminedErr(nil)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// TODO: Now we return an undetermined error as long as one of the prewrite
|
||||||
|
// RPCs fails. However, if there are multiple errors and some of the errors
|
||||||
|
// are not RPC failures, we can return the actual error instead of undetermined.
|
||||||
if undeterminedErr := c.getUndeterminedErr(); undeterminedErr != nil {
|
if undeterminedErr := c.getUndeterminedErr(); undeterminedErr != nil {
|
||||||
logutil.Logger(ctx).Error("2PC commit result undetermined",
|
logutil.Logger(ctx).Error("2PC commit result undetermined",
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
|
|
|
||||||
|
|
@ -223,9 +223,6 @@ func (action actionPrewrite) handleSingleBatch(c *twoPhaseCommitter, bo *Backoff
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Trace(err)
|
return errors.Trace(err)
|
||||||
}
|
}
|
||||||
if _, err := util.EvalFailpoint("forceRecursion"); err == nil {
|
|
||||||
same = false
|
|
||||||
}
|
|
||||||
if same {
|
if same {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -239,6 +236,9 @@ func (action actionPrewrite) handleSingleBatch(c *twoPhaseCommitter, bo *Backoff
|
||||||
prewriteResp := resp.Resp.(*kvrpcpb.PrewriteResponse)
|
prewriteResp := resp.Resp.(*kvrpcpb.PrewriteResponse)
|
||||||
keyErrs := prewriteResp.GetErrors()
|
keyErrs := prewriteResp.GetErrors()
|
||||||
if len(keyErrs) == 0 {
|
if len(keyErrs) == 0 {
|
||||||
|
// Clear the RPC Error since the request is evaluated successfully.
|
||||||
|
sender.SetRPCError(nil)
|
||||||
|
|
||||||
if batch.isPrimary {
|
if batch.isPrimary {
|
||||||
// After writing the primary key, if the size of the transaction is larger than 32M,
|
// After writing the primary key, if the size of the transaction is larger than 32M,
|
||||||
// start the ttlManager. The ttlManager will be closed in tikvTxn.Commit().
|
// start the ttlManager. The ttlManager will be closed in tikvTxn.Commit().
|
||||||
|
|
@ -299,17 +299,12 @@ func (action actionPrewrite) handleSingleBatch(c *twoPhaseCommitter, bo *Backoff
|
||||||
// Check already exists error
|
// Check already exists error
|
||||||
if alreadyExist := keyErr.GetAlreadyExist(); alreadyExist != nil {
|
if alreadyExist := keyErr.GetAlreadyExist(); alreadyExist != nil {
|
||||||
e := &tikverr.ErrKeyExist{AlreadyExist: alreadyExist}
|
e := &tikverr.ErrKeyExist{AlreadyExist: alreadyExist}
|
||||||
err = c.extractKeyExistsErr(e)
|
return c.extractKeyExistsErr(e)
|
||||||
if err != nil {
|
|
||||||
atomic.StoreUint32(&c.prewriteFailed, 1)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract lock from key error
|
// Extract lock from key error
|
||||||
lock, err1 := extractLockFromKeyErr(keyErr)
|
lock, err1 := extractLockFromKeyErr(keyErr)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
atomic.StoreUint32(&c.prewriteFailed, 1)
|
|
||||||
return errors.Trace(err1)
|
return errors.Trace(err1)
|
||||||
}
|
}
|
||||||
logutil.BgLogger().Info("prewrite encounters lock",
|
logutil.BgLogger().Info("prewrite encounters lock",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue