// Copyright 2015 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 ( "sync" "github.com/juju/errors" "github.com/ngaut/log" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/meta" ) const ( step = 1000 ) // Allocator is an auto increment id generator. // Just keep id unique actually. type Allocator interface { // Alloc allocs the next autoID for table with tableID. // It gets a batch of autoIDs at a time. So it does not need to access storage for each call. Alloc(tableID int64) (int64, error) // Rebase rebases the autoID base for table with tableID and the new base value. // If allocIDs is true, it will allocate some IDs and save to the cache. // If allocIDs is false, it will not allocate IDs. Rebase(tableID, newBase int64, allocIDs bool) error } type allocator struct { mu sync.Mutex base int64 end int64 store kv.Storage dbID int64 } // Rebase implements autoid.Allocator Rebase interface. func (alloc *allocator) Rebase(tableID, newBase int64, allocIDs bool) error { if tableID == 0 { return errors.New("Invalid tableID") } alloc.mu.Lock() defer alloc.mu.Unlock() if newBase <= alloc.base { return nil } if newBase <= alloc.end { alloc.base = newBase return nil } return kv.RunInNewTxn(alloc.store, true, func(txn kv.Transaction) error { m := meta.NewMeta(txn) end, err := m.GetAutoTableID(alloc.dbID, tableID) if err != nil { return errors.Trace(err) } if newBase <= end { return nil } newStep := newBase - end + step if !allocIDs { newStep = newBase - end } end, err = m.GenAutoTableID(alloc.dbID, tableID, newStep) if err != nil { return errors.Trace(err) } alloc.end = end alloc.base = newBase if !allocIDs { alloc.base = alloc.end } return nil }) } // Alloc implements autoid.Allocator Alloc interface. func (alloc *allocator) Alloc(tableID int64) (int64, error) { if tableID == 0 { return 0, errors.New("Invalid tableID") } alloc.mu.Lock() defer alloc.mu.Unlock() if alloc.base == alloc.end { // step err := kv.RunInNewTxn(alloc.store, true, func(txn kv.Transaction) error { m := meta.NewMeta(txn) base, err1 := m.GetAutoTableID(alloc.dbID, tableID) if err1 != nil { return errors.Trace(err1) } end, err1 := m.GenAutoTableID(alloc.dbID, tableID, step) if err1 != nil { return errors.Trace(err1) } alloc.end = end if end == step { alloc.base = base } else { alloc.base = end - step } return nil }) if err != nil { return 0, errors.Trace(err) } } 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 } var ( memID int64 memIDLock sync.Mutex ) type memoryAllocator struct { mu sync.Mutex base int64 end int64 dbID int64 } // Rebase implements autoid.Allocator Rebase interface. func (alloc *memoryAllocator) Rebase(tableID, newBase int64, allocIDs bool) error { // TODO: implement it. return nil } // Alloc implements autoid.Allocator Alloc interface. func (alloc *memoryAllocator) Alloc(tableID int64) (int64, error) { if tableID == 0 { return 0, errors.New("Invalid tableID") } alloc.mu.Lock() defer alloc.mu.Unlock() if alloc.base == alloc.end { // step memIDLock.Lock() memID = memID + step alloc.end = memID alloc.base = alloc.end - step memIDLock.Unlock() } alloc.base++ return alloc.base, nil } // NewAllocator returns a new auto increment id generator on the store. func NewAllocator(store kv.Storage, dbID int64) Allocator { return &allocator{ store: store, dbID: dbID, } } // NewMemoryAllocator returns a new auto increment id generator in memory. func NewMemoryAllocator(dbID int64) Allocator { return &memoryAllocator{ dbID: dbID, } }