-
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,7 +57,8 @@ type allocator struct { | |
end int64 | ||
store kv.Storage | ||
// dbID is current database's ID. | ||
dbID int64 | ||
dbID int64 | ||
isUnsigned bool | ||
} | ||
|
||
// GetStep is only used by tests | ||
|
@@ -91,7 +92,10 @@ func (alloc *allocator) NextGlobalAutoID(tableID int64) (int64, error) { | |
return errors.Trace(err1) | ||
}) | ||
metrics.AutoIDHistogram.WithLabelValues(metrics.GlobalAutoID, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) | ||
return autoID + 1, errors.Trace(err) | ||
if alloc.isUnsigned { | ||
return int64(uint64(autoID) + 1), err | ||
} | ||
return autoID + 1, err | ||
} | ||
|
||
// Rebase implements autoid.Allocator Rebase interface. | ||
|
@@ -104,40 +108,69 @@ func (alloc *allocator) Rebase(tableID, requiredBase int64, allocIDs bool) error | |
|
||
alloc.mu.Lock() | ||
defer alloc.mu.Unlock() | ||
if requiredBase <= alloc.base { | ||
// Satisfied by alloc.base, nothing to do. | ||
|
||
// Satisfied by alloc.base, nothing to do. | ||
if !alloc.isUnsigned && requiredBase <= alloc.base { | ||
return nil | ||
} else if alloc.isUnsigned && uint64(requiredBase) <= uint64(alloc.base) { | ||
return nil | ||
} | ||
if requiredBase <= alloc.end { | ||
// Satisfied by alloc.end, need to updata alloc.base. | ||
|
||
// Satisfied by alloc.end, need to update alloc.base. | ||
if !alloc.isUnsigned && requiredBase <= alloc.end { | ||
alloc.base = requiredBase | ||
return nil | ||
} else if alloc.isUnsigned && uint64(requiredBase) <= uint64(alloc.end) { | ||
alloc.base = requiredBase | ||
return nil | ||
} | ||
var newBase, newEnd int64 | ||
var uNewBase, uNewEnd uint64 | ||
startTime := time.Now() | ||
err := kv.RunInNewTxn(alloc.store, true, func(txn kv.Transaction) error { | ||
m := meta.NewMeta(txn) | ||
currentEnd, err1 := m.GetAutoTableID(alloc.dbID, tableID) | ||
if err1 != nil { | ||
return errors.Trace(err1) | ||
} | ||
uCurrentEnd, uRequiredBase := uint64(currentEnd), uint64(requiredBase) | ||
if allocIDs { | ||
newBase = mathutil.MaxInt64(currentEnd, requiredBase) | ||
newEnd = newBase + step | ||
if alloc.isUnsigned { | ||
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. You want to prevent overflow?
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. Yes, 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. Well, maybe error or warning... 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. This check is used to prevent the It may be reasonable to change alloc.end to math.Uint64 if it will overflow? 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. If alloc.end overflow, what will happen? 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. @tiancaiamao tidb> create table t(a bigint signed not null auto_increment, unique key(a));
Query OK, 0 rows affected (0.02 sec)
tidb> insert into t value(9223372036854775806);
Query OK, 1 row affected (0.00 sec)
tidb> insert into t value();
Query OK, 1 row affected (0.00 sec)
tidb> insert into t value();
Query OK, 1 row affected (0.00 sec)
tidb> insert into t value();
Query OK, 1 row affected (0.00 sec)
tidb> insert into t value();
Query OK, 1 row affected (0.00 sec)
tidb> select * from t;
+----------------------+
| a |
+----------------------+
| -9223372036854775808 |
| -9223372036854775806 |
| -9223372036854775804 |
| -9223372036854775802 |
| 9223372036854775806 |
+----------------------+
5 rows in set (0.00 sec) 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. Overwrite the old row id is dangerous for data consistency. 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. As a tradeoff, #8181 (review) may be better? 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. I think throw error is simple, easier, and more robust. |
||
uNewBase = mathutil.MaxUint64(uCurrentEnd, uRequiredBase) | ||
uNewEnd = uNewBase + uint64(step) | ||
} else { | ||
newBase = mathutil.MaxInt64(currentEnd, requiredBase) | ||
newEnd = newBase + step | ||
} | ||
} else { | ||
if currentEnd >= requiredBase { | ||
|
||
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 | ||
// Required base satisfied, we don't need to update KV. | ||
return nil | ||
} else if alloc.isUnsigned && uCurrentEnd >= uRequiredBase { | ||
uNewBase = uCurrentEnd | ||
uNewEnd = uCurrentEnd | ||
return nil | ||
} | ||
// If we don't want to allocate IDs, for example when creating a table with a given base value, | ||
// We need to make sure when other TiDB server allocates ID for the first time, requiredBase + 1 | ||
// will be allocated, so we need to increase the end to exactly the requiredBase. | ||
newBase = requiredBase | ||
newEnd = requiredBase | ||
if !alloc.isUnsigned { | ||
newBase = requiredBase | ||
newEnd = requiredBase | ||
} else { | ||
uNewBase = uRequiredBase | ||
uNewEnd = uRequiredBase | ||
} | ||
|
||
} | ||
if !alloc.isUnsigned { | ||
_, err1 = m.GenAutoTableID(alloc.dbID, tableID, newEnd-currentEnd) | ||
} else { | ||
_, err1 = m.GenAutoTableID(alloc.dbID, tableID, int64(uNewEnd-uCurrentEnd)) | ||
} | ||
_, err1 = m.GenAutoTableID(alloc.dbID, tableID, newEnd-currentEnd) | ||
return errors.Trace(err1) | ||
}) | ||
metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) | ||
|
@@ -237,10 +270,11 @@ func (alloc *memoryAllocator) Alloc(tableID int64) (int64, error) { | |
} | ||
|
||
// NewAllocator returns a new auto increment id generator on the store. | ||
func NewAllocator(store kv.Storage, dbID int64) Allocator { | ||
func NewAllocator(store kv.Storage, dbID int64, isUnsigned bool) Allocator { | ||
return &allocator{ | ||
store: store, | ||
dbID: dbID, | ||
store: store, | ||
dbID: dbID, | ||
isUnsigned: isUnsigned, | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,7 +57,7 @@ func (*testSuite) TestT(c *C) { | |
}) | ||
c.Assert(err, IsNil) | ||
|
||
alloc := autoid.NewAllocator(store, 1) | ||
alloc := autoid.NewAllocator(store, 1, false) | ||
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 add some tests for |
||
c.Assert(alloc, NotNil) | ||
|
||
globalAutoId, err := alloc.NextGlobalAutoID(1) | ||
|
@@ -97,25 +97,25 @@ func (*testSuite) TestT(c *C) { | |
c.Assert(err, IsNil) | ||
c.Assert(id, Equals, int64(3011)) | ||
|
||
alloc = autoid.NewAllocator(store, 1) | ||
alloc = autoid.NewAllocator(store, 1, false) | ||
c.Assert(alloc, NotNil) | ||
id, err = alloc.Alloc(1) | ||
c.Assert(err, IsNil) | ||
c.Assert(id, Equals, int64(autoid.GetStep()+1)) | ||
|
||
alloc = autoid.NewAllocator(store, 1) | ||
alloc = autoid.NewAllocator(store, 1, false) | ||
c.Assert(alloc, NotNil) | ||
err = alloc.Rebase(2, int64(1), false) | ||
c.Assert(err, IsNil) | ||
id, err = alloc.Alloc(2) | ||
c.Assert(err, IsNil) | ||
c.Assert(id, Equals, int64(2)) | ||
|
||
alloc = autoid.NewAllocator(store, 1) | ||
alloc = autoid.NewAllocator(store, 1, false) | ||
c.Assert(alloc, NotNil) | ||
err = alloc.Rebase(3, int64(3210), false) | ||
c.Assert(err, IsNil) | ||
alloc = autoid.NewAllocator(store, 1) | ||
alloc = autoid.NewAllocator(store, 1, false) | ||
c.Assert(alloc, NotNil) | ||
err = alloc.Rebase(3, int64(3000), false) | ||
c.Assert(err, IsNil) | ||
|
@@ -159,7 +159,7 @@ func (*testSuite) TestConcurrentAlloc(c *C) { | |
errCh := make(chan error, count) | ||
|
||
allocIDs := func() { | ||
alloc := autoid.NewAllocator(store, dbID) | ||
alloc := autoid.NewAllocator(store, dbID, false) | ||
for j := 0; j < int(autoid.GetStep())+5; j++ { | ||
id, err1 := alloc.Alloc(tblID) | ||
if err1 != nil { | ||
|
@@ -213,7 +213,7 @@ func (*testSuite) TestRollbackAlloc(c *C) { | |
injectConf := new(kv.InjectionConfig) | ||
injectConf.SetCommitError(errors.New("injected")) | ||
injectedStore := kv.NewInjectedStore(store, injectConf) | ||
alloc := autoid.NewAllocator(injectedStore, 1) | ||
alloc := autoid.NewAllocator(injectedStore, 1, false) | ||
_, err = alloc.Alloc(2) | ||
c.Assert(err, NotNil) | ||
c.Assert(alloc.Base(), Equals, int64(0)) | ||
|
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 it is better to extract these lines to a function.
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 mean line1024~1028?
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, I see some similar code in this PR.