From ff6a4f7e8f57fc43d8224867b518c36939f32dc3 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Fri, 1 Nov 2019 15:13:27 +0800 Subject: [PATCH] kv: add error code for package kv (#12866) --- ddl/db_test.go | 2 +- ddl/rollingback.go | 2 +- executor/batch_checker.go | 6 +++--- kv/error.go | 44 ++++++++++++++++----------------------- kv/error_test.go | 42 +++++++++++++++++++++++++++++++++++++ kv/memdb_buffer.go | 2 +- kv/union_store.go | 2 +- store/tikv/2pc.go | 2 +- store/tikv/snapshot.go | 2 +- table/tables/tables.go | 2 +- 10 files changed, 70 insertions(+), 36 deletions(-) create mode 100644 kv/error_test.go diff --git a/ddl/db_test.go b/ddl/db_test.go index 94c657caafc7f..778e3234c8002 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -263,7 +263,7 @@ LOOP: select { case err := <-done: c.Assert(err, NotNil) - c.Assert(err.Error(), Equals, "[kv:1062]Duplicate for key c3_index", Commentf("err:%v", err)) + c.Assert(err.Error(), Equals, "[kv:1062]Duplicate entry '' for key 'c3_index'", Commentf("err:%v", err)) break LOOP case <-ticker.C: if times >= 10 { diff --git a/ddl/rollingback.go b/ddl/rollingback.go index a61b4f8a50191..5c4ddcdd33877 100644 --- a/ddl/rollingback.go +++ b/ddl/rollingback.go @@ -40,7 +40,7 @@ func convertAddIdxJob2RollbackJob(t *meta.Meta, job *model.Job, tblInfo *model.T } if kv.ErrKeyExists.Equal(err) { - return ver, kv.ErrKeyExists.GenWithStack("Duplicate for key %s", indexInfo.Name.O) + return ver, kv.ErrKeyExists.GenWithStackByArgs("", indexInfo.Name.O) } return ver, errors.Trace(err) diff --git a/executor/batch_checker.go b/executor/batch_checker.go index a083df06381cd..02dc0d88292b6 100644 --- a/executor/batch_checker.go +++ b/executor/batch_checker.go @@ -15,6 +15,7 @@ package executor import ( "context" + "strconv" "github.com/pingcap/parser/model" "github.com/pingcap/tidb/expression" @@ -118,7 +119,7 @@ func getKeysNeedCheckOneRow(ctx sessionctx.Context, t table.Table, row []types.D key: t.RecordKey(handle), value: newRowValue, }, - dupErr: kv.ErrKeyExists.FastGen("Duplicate entry '%d' for key 'PRIMARY'", handle), + dupErr: kv.ErrKeyExists.FastGenByArgs(strconv.FormatInt(handle, 10), "PRIMARY"), } } @@ -150,8 +151,7 @@ func getKeysNeedCheckOneRow(ctx sessionctx.Context, t table.Table, row []types.D newKV: keyValue{ key: key, }, - dupErr: kv.ErrKeyExists.FastGen("Duplicate entry '%s' for key '%s'", - colValStr, v.Meta().Name), + dupErr: kv.ErrKeyExists.FastGenByArgs(colValStr, v.Meta().Name), }) } result = append(result, toBeCheckedRow{ diff --git a/kv/error.go b/kv/error.go index 9c0d9721d4fe8..35a9ae8ccc2ac 100644 --- a/kv/error.go +++ b/kv/error.go @@ -18,44 +18,30 @@ import ( "github.com/pingcap/parser/terror" ) -// KV error codes. -const ( - codeClosed terror.ErrCode = 1 - codeNotExist = 2 - codeTxnRetryable = 6 - codeCantSetNilValue = 7 - codeInvalidTxn = 8 - codeNotImplemented = 10 - codeTxnTooLarge = 11 - codeEntryTooLarge = 12 - codeKeyExists = 1062 -) - // TxnRetryableMark is used to uniform the commit error messages which could retry the transaction. // *WARNING*: changing this string will affect the backward compatibility. const TxnRetryableMark = "[try again later]" var ( - // ErrClosed is used when close an already closed txn. - ErrClosed = terror.ClassKV.New(codeClosed, "Error: Transaction already closed") // ErrNotExist is used when try to get an entry with an unexist key from KV store. - ErrNotExist = terror.ClassKV.New(codeNotExist, "Error: key not exist") + ErrNotExist = terror.ClassKV.New(mysql.ErrNotExist, mysql.MySQLErrName[mysql.ErrNotExist]) // ErrTxnRetryable is used when KV store occurs retryable error which SQL layer can safely retry the transaction. // When using TiKV as the storage node, the error is returned ONLY when lock not found (txnLockNotFound) in Commit, // subject to change it in the future. - ErrTxnRetryable = terror.ClassKV.New(codeTxnRetryable, "Error: KV error safe to retry %s "+TxnRetryableMark) + ErrTxnRetryable = terror.ClassKV.New(mysql.ErrTxnRetryable, + mysql.MySQLErrName[mysql.ErrTxnRetryable]+TxnRetryableMark) // ErrCannotSetNilValue is the error when sets an empty value. - ErrCannotSetNilValue = terror.ClassKV.New(codeCantSetNilValue, "can not set nil value") + ErrCannotSetNilValue = terror.ClassKV.New(mysql.ErrCannotSetNilValue, mysql.MySQLErrName[mysql.ErrCannotSetNilValue]) // ErrInvalidTxn is the error when commits or rollbacks in an invalid transaction. - ErrInvalidTxn = terror.ClassKV.New(codeInvalidTxn, "invalid transaction") + ErrInvalidTxn = terror.ClassKV.New(mysql.ErrInvalidTxn, mysql.MySQLErrName[mysql.ErrInvalidTxn]) // ErrTxnTooLarge is the error when transaction is too large, lock time reached the maximum value. - ErrTxnTooLarge = terror.ClassKV.New(codeTxnTooLarge, "transaction is too large") + ErrTxnTooLarge = terror.ClassKV.New(mysql.ErrTxnTooLarge, mysql.MySQLErrName[mysql.ErrTxnTooLarge]) // ErrEntryTooLarge is the error when a key value entry is too large. - ErrEntryTooLarge = terror.ClassKV.New(codeEntryTooLarge, "entry too large, the max entry size is %d, the size of data is %d") + ErrEntryTooLarge = terror.ClassKV.New(mysql.ErrEntryTooLarge, mysql.MySQLErrName[mysql.ErrEntryTooLarge]) // ErrKeyExists returns when key is already exist. - ErrKeyExists = terror.ClassKV.New(codeKeyExists, "key already exist") + ErrKeyExists = terror.ClassKV.New(mysql.ErrDupEntry, mysql.MySQLErrName[mysql.ErrDupEntry]) // ErrNotImplemented returns when a function is not implemented yet. - ErrNotImplemented = terror.ClassKV.New(codeNotImplemented, "not implemented") + ErrNotImplemented = terror.ClassKV.New(mysql.ErrNotImplemented, mysql.MySQLErrName[mysql.ErrNotImplemented]) // ErrWriteConflict is the error when the commit meets an write conflict error. ErrWriteConflict = terror.ClassKV.New(mysql.ErrWriteConflict, mysql.MySQLErrName[mysql.ErrWriteConflict]+" "+TxnRetryableMark) @@ -66,11 +52,17 @@ var ( func init() { kvMySQLErrCodes := map[terror.ErrCode]uint16{ - codeKeyExists: mysql.ErrDupEntry, - codeEntryTooLarge: mysql.ErrTooBigRowsize, - codeTxnTooLarge: mysql.ErrTxnTooLarge, + mysql.ErrNotExist: mysql.ErrNotExist, + mysql.ErrDupEntry: mysql.ErrDupEntry, + mysql.ErrTooBigRowsize: mysql.ErrTooBigRowsize, + mysql.ErrTxnTooLarge: mysql.ErrTxnTooLarge, + mysql.ErrTxnRetryable: mysql.ErrTxnRetryable, mysql.ErrWriteConflict: mysql.ErrWriteConflict, mysql.ErrWriteConflictInTiDB: mysql.ErrWriteConflictInTiDB, + mysql.ErrCannotSetNilValue: mysql.ErrCannotSetNilValue, + mysql.ErrInvalidTxn: mysql.ErrInvalidTxn, + mysql.ErrEntryTooLarge: mysql.ErrEntryTooLarge, + mysql.ErrNotImplemented: mysql.ErrNotImplemented, } terror.ErrClassToMySQLCodes[terror.ClassKV] = kvMySQLErrCodes } diff --git a/kv/error_test.go b/kv/error_test.go new file mode 100644 index 0000000000000..00900c0c789f2 --- /dev/null +++ b/kv/error_test.go @@ -0,0 +1,42 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package kv + +import ( + . "github.com/pingcap/check" + "github.com/pingcap/parser/mysql" + "github.com/pingcap/parser/terror" +) + +type testErrorSuite struct{} + +var _ = Suite(testErrorSuite{}) + +func (s testErrorSuite) TestError(c *C) { + kvErrs := []*terror.Error{ + ErrNotExist, + ErrTxnRetryable, + ErrCannotSetNilValue, + ErrInvalidTxn, + ErrTxnTooLarge, + ErrEntryTooLarge, + ErrKeyExists, + ErrNotImplemented, + ErrWriteConflict, + ErrWriteConflictInTiDB, + } + for _, err := range kvErrs { + c.Assert(err.ToSQLError().Code != mysql.ErrUnknown, IsTrue) + } +} diff --git a/kv/memdb_buffer.go b/kv/memdb_buffer.go index b3cabb0598de4..4a84878151d5e 100644 --- a/kv/memdb_buffer.go +++ b/kv/memdb_buffer.go @@ -103,7 +103,7 @@ func (m *memDbBuffer) Set(k Key, v []byte) error { m.db.Put(k, v) if m.Size() > int(m.bufferSizeLimit) { - return ErrTxnTooLarge.GenWithStack("transaction too large, size:%d", m.Size()) + return ErrTxnTooLarge.GenWithStackByArgs(m.Size()) } return nil } diff --git a/kv/union_store.go b/kv/union_store.go index 7db529e69b756..e2570a47999b7 100644 --- a/kv/union_store.go +++ b/kv/union_store.go @@ -77,7 +77,7 @@ func (e *existErrInfo) GetValue() string { // Err generates the error for existErrInfo func (e *existErrInfo) Err() error { - return ErrKeyExists.FastGen("Duplicate entry '%s' for key '%s'", e.value, e.idxName) + return ErrKeyExists.FastGenByArgs(e.value, e.idxName) } // unionStore is an in-memory Store which contains a buffer for write and a diff --git a/store/tikv/2pc.go b/store/tikv/2pc.go index dc8ccba1171d5..441447ff5b1dc 100644 --- a/store/tikv/2pc.go +++ b/store/tikv/2pc.go @@ -300,7 +300,7 @@ func (c *twoPhaseCommitter) initKeysAndMutations() error { } if size > int(kv.TxnTotalSizeLimit) { - return kv.ErrTxnTooLarge + return kv.ErrTxnTooLarge.GenWithStackByArgs(size) } const logEntryCount = 10000 const logSize = 4 * 1024 * 1024 // 4MB diff --git a/store/tikv/snapshot.go b/store/tikv/snapshot.go index 8ff05e24cbc2f..9423a33781733 100644 --- a/store/tikv/snapshot.go +++ b/store/tikv/snapshot.go @@ -397,7 +397,7 @@ func extractKeyErr(keyErr *pb.KeyError) error { } if keyErr.Retryable != "" { notFoundDetail := prettyLockNotFoundKey(keyErr.GetRetryable()) - return kv.ErrTxnRetryable.FastGenByArgs("tikv restarts txn: " + keyErr.GetRetryable() + " " + notFoundDetail) + return kv.ErrTxnRetryable.FastGenByArgs(keyErr.GetRetryable() + " " + notFoundDetail) } if keyErr.Abort != "" { err := errors.Errorf("tikv aborts txn: %s", keyErr.GetAbort()) diff --git a/table/tables/tables.go b/table/tables/tables.go index bc36c892fb1e1..ccef291a03862 100644 --- a/table/tables/tables.go +++ b/table/tables/tables.go @@ -843,7 +843,7 @@ func (t *tableCommon) buildIndexForRow(ctx sessionctx.Context, rm kv.RetrieverMu return err } - return kv.ErrKeyExists.FastGen("Duplicate entry '%s' for key '%s'", entryKey, idx.Meta().Name) + return kv.ErrKeyExists.FastGenByArgs(entryKey, idx.Meta().Name) } return err }