-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
executor, session: refine insert unsigned bigint autoIncreID #8181
Changes from 2 commits
a7df24d
9d21807
2db694e
1eb9325
53fd0a6
758fd31
a68a08d
c1e1ff6
9104f74
fc03231
82b12d5
2e8b425
ce0e447
874ff0b
d0616ac
6792306
46d20a4
548ea41
61bfec6
6c4419c
3f1fcb4
71ea318
18c62fe
d688346
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -137,13 +137,12 @@ func (alloc *allocator) Rebase(tableID, requiredBase int64, allocIDs bool) error | |
if allocIDs { | ||
if alloc.isUnsigned { | ||
uNewBase = mathutil.MaxUint64(uCurrentEnd, uRequiredBase) | ||
uNewEnd = uNewBase + uint64(step) | ||
uNewEnd = mathutil.MinUint64(math.MaxUint64-uint64(step), uNewBase) + uint64(step) | ||
} else { | ||
newBase = mathutil.MaxInt64(currentEnd, requiredBase) | ||
newEnd = newBase + step | ||
newEnd = mathutil.MinInt64(math.MaxInt64-step, newBase) + step | ||
} | ||
} else { | ||
|
||
if !alloc.isUnsigned && currentEnd >= requiredBase { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. !alloc.isUnsigned will always be true here? |
||
newBase = currentEnd | ||
newEnd = currentEnd | ||
|
@@ -164,7 +163,6 @@ func (alloc *allocator) Rebase(tableID, requiredBase int64, allocIDs bool) error | |
uNewBase = uRequiredBase | ||
uNewEnd = uRequiredBase | ||
} | ||
|
||
} | ||
if !alloc.isUnsigned { | ||
_, err1 = m.GenAutoTableID(alloc.dbID, tableID, newEnd-currentEnd) | ||
|
@@ -177,7 +175,11 @@ func (alloc *allocator) Rebase(tableID, requiredBase int64, allocIDs bool) error | |
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
alloc.base, alloc.end = newBase, newEnd | ||
if !alloc.isUnsigned { | ||
alloc.base, alloc.end = newBase, newEnd | ||
} else { | ||
alloc.base, alloc.end = int64(uNewBase), int64(uNewEnd) | ||
} | ||
return nil | ||
} | ||
|
||
|
@@ -198,21 +200,38 @@ func (alloc *allocator) Alloc(tableID int64) (int64, error) { | |
if err1 != nil { | ||
return errors.Trace(err1) | ||
} | ||
newEnd, err1 = m.GenAutoTableID(alloc.dbID, tableID, step) | ||
tmpStep := step | ||
if alloc.isUnsigned { | ||
tmpStep = int64(mathutil.MinUint64(math.MaxUint64-uint64(newBase), uint64(step))) | ||
} else { | ||
tmpStep = mathutil.MinInt64(math.MaxInt64-newBase, step) | ||
} | ||
newEnd, err1 = m.GenAutoTableID(alloc.dbID, tableID, tmpStep) | ||
if err1 != nil { | ||
return errors.Trace(err1) | ||
} | ||
return nil | ||
}) | ||
metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDAlloc, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) | ||
if err != nil { | ||
return 0, errors.Trace(err) | ||
return 0, err | ||
} | ||
alloc.base, alloc.end = newBase, newEnd | ||
} | ||
log.Warning(uint64(alloc.base), uint64(alloc.end)) | ||
crazycs520 marked this conversation as resolved.
Show resolved
Hide resolved
crazycs520 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
alloc.base++ | ||
log.Debugf("[kv] Alloc id %d, table ID:%d, from %p, database ID:%d", alloc.base, tableID, alloc, alloc.dbID) | ||
if alloc.isUnsigned { | ||
if uint64(alloc.base) == math.MaxUint64 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. !alloc.isUnsigned need this kind of check( if alloc.base == math.MaxInt64 ) too. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we put this check to line204? Then we needn't do check each time |
||
return 0, ErrAutoincReadFailed | ||
} | ||
alloc.base = int64(uint64(alloc.base) + 1) | ||
log.Debugf("[kv] Alloc id %d, table ID:%d, from %p, database ID:%d", uint64(alloc.base), tableID, alloc, alloc.dbID) | ||
} else { | ||
if alloc.base < alloc.end { | ||
alloc.base++ | ||
} | ||
log.Debugf("[kv] Alloc id %d, table ID:%d, from %p, database ID:%d", alloc.base, tableID, alloc, alloc.dbID) | ||
} | ||
return alloc.base, nil | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// Copyright 2018 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 autoid | ||
|
||
import ( | ||
"github.com/pingcap/parser/mysql" | ||
"github.com/pingcap/parser/terror" | ||
) | ||
|
||
// Error instances. | ||
var ( | ||
ErrAutoincReadFailed = terror.ClassAutoid.New(mysql.ErrAutoincReadFailed, mysql.MySQLErrName[mysql.ErrAutoincReadFailed]) | ||
) | ||
|
||
func init() { | ||
// Map error codes to mysql error codes. | ||
tableMySQLErrCodes := map[terror.ErrCode]uint16{ | ||
mysql.ErrAutoincReadFailed: mysql.ErrAutoincReadFailed, | ||
} | ||
terror.ErrClassToMySQLCodes[terror.ClassAutoid] = tableMySQLErrCodes | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ import ( | |
"github.com/pingcap/tidb/domain" | ||
"github.com/pingcap/tidb/executor" | ||
"github.com/pingcap/tidb/kv" | ||
"github.com/pingcap/tidb/meta/autoid" | ||
plannercore "github.com/pingcap/tidb/planner/core" | ||
"github.com/pingcap/tidb/privilege/privileges" | ||
"github.com/pingcap/tidb/session" | ||
|
@@ -705,12 +706,28 @@ func (s *testSessionSuite) TestAutoIncrementID(c *C) { | |
tk.MustQuery("select last_insert_id(20)").Check(testkit.Rows(fmt.Sprint(20))) | ||
tk.MustQuery("select last_insert_id()").Check(testkit.Rows(fmt.Sprint(20))) | ||
|
||
// Corner cases for unsigned bigint auto_increment Columns. | ||
tk.MustExec("drop table if exists autoid") | ||
tk.MustExec("create table autoid(`auto_inc_id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,UNIQUE KEY `auto_inc_id` (`auto_inc_id`))") | ||
tk.MustExec("insert into autoid values(9223372036854775808);") | ||
tk.MustExec("insert into autoid values();") | ||
tk.MustExec("insert into autoid values();") | ||
tk.MustQuery("select * from autoid").Check(testkit.Rows("9223372036854775808", "9223372036854775810", "9223372036854775812")) | ||
tk.MustExec("insert into autoid values(18446744073709551614);") | ||
_, err := tk.Exec("insert into autoid values()") | ||
c.Assert(terror.ErrorEqual(err, autoid.ErrAutoincReadFailed), IsTrue) | ||
// FixMe: MySQL works fine with the this sql. | ||
_, err = tk.Exec("insert into autoid values(18446744073709551615)") | ||
c.Assert(terror.ErrorEqual(err, autoid.ErrAutoincReadFailed), IsTrue) | ||
|
||
// Corner cases for signed bigint auto_increment Columns. | ||
tk.MustExec("drop table if exists autoid") | ||
tk.MustExec("create table autoid(`auto_inc_id` bigint(20) NOT NULL AUTO_INCREMENT,UNIQUE KEY `auto_inc_id` (`auto_inc_id`))") | ||
tk.MustExec("insert into autoid values(9223372036854775806);") | ||
tk.MustExec("insert into autoid values();") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the test has a problem: mysql root@127.0.0.1:test> create table autoid(`auto_inc_id` bigint(20) NOT NULL AUTO_INCREMENT,UNIQUE KEY `auto_inc_id` (`auto_inc_id`));
Query OK, 0 rows affected
mysql root@127.0.0.1:test> insert into autoid values(9223372036854775806);
Query OK, 1 row affected
mysql root@127.0.0.1:test> select auto_inc_id,_tidb_rowid from autoid
+---------------------+---------------------+
| auto_inc_id | _tidb_rowid |
+---------------------+---------------------+
| 9223372036854775806 | 9223372036854775807 |
+---------------------+---------------------+
1 row in set
mysql root@127.0.0.1:test> insert into autoid values(); # auto_id = rowid= 9223372036854775807, cause below admin check error.
Query OK, 1 row affected
mysql root@127.0.0.1:test> select auto_inc_id,_tidb_rowid from autoid
+---------------------+---------------------+
| auto_inc_id | _tidb_rowid |
+---------------------+---------------------+
| 9223372036854775807 | 9223372036854775807 |
+---------------------+---------------------+
mysql root@127.0.0.1:test> admin check table autoid;
(8003, u'autoid err:[admin:1]index:&admin.RecordData{Handle:9223372036854775807, Values:[]types.Datum{types.Datum{k:0x1, collation:0x0, decimal:0x0, length:0x0, i:9223372036854775806, b:[]uint8(nil), x:interface {}(nil)}}} != record:&admin.RecordData{Handle:9223372036854775807, Values:[]types.Datum{types.Datum{k:0x1, collation:0x0, decimal:0x0, length:0x0, i:9223372036854775807, b:[]uint8(nil), x:interface {}(nil)}}}')
mysql root@127.0.0.1:test> select count(*) from autoid;
+----------+
| count(*) |
+----------+
| 1 |
+----------+
1 row in set
mysql root@127.0.0.1:test> select count(*) from autoid use index(auto_inc_id);
+----------+
| count(*) |
+----------+
| 2 |
+----------+
1 row in set There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please address this comment @XuHuaiyu. |
||
tk.MustQuery("select * from autoid").Check(testkit.Rows("9223372036854775806", "9223372036854775807")) | ||
_, err = tk.Exec("insert into autoid values();") | ||
c.Assert(terror.ErrorEqual(err, kv.ErrKeyExists), IsTrue) | ||
} | ||
|
||
func (s *testSessionSuite) TestAutoIncrementWithRetry(c *C) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You want to prevent overflow?
Should we
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes,
but why we panic here? @tiancaiamao
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, maybe error or warning...
I mean, if the ID overflow, it should not be changed to math.Uint64
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is used to prevent the
alloc.end
overflow.alloc.base is checked here.
It may be reasonable to change alloc.end to math.Uint64 if it will overflow?
@tiancaiamao
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If alloc.end overflow, what will happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tiancaiamao
The result of TiDB master:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overwrite the old row id is dangerous for data consistency.
Is it acceptable to change the behavior to throw error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a tradeoff, #8181 (review) may be better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think throw error is simple, easier, and more robust.