mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-17 08:14:25 +08:00
uuid generator supports generating more than 1 uuid in one time
This commit is contained in:
@@ -45,3 +45,8 @@ type ServiceUsingUuid struct {
|
|||||||
func (s *ServiceUsingUuid) GenerateUuid(uuidType uuid.UuidType) int64 {
|
func (s *ServiceUsingUuid) GenerateUuid(uuidType uuid.UuidType) int64 {
|
||||||
return s.container.GenerateUuid(uuidType)
|
return s.container.GenerateUuid(uuidType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateUuids generates new uuids according to given uuid type and count
|
||||||
|
func (s *ServiceUsingUuid) GenerateUuids(uuidType uuid.UuidType, count uint8) []int64 {
|
||||||
|
return s.container.GenerateUuids(uuidType, count)
|
||||||
|
}
|
||||||
|
|||||||
@@ -48,40 +48,51 @@ func NewInternalUuidGenerator(config *settings.Config) (*InternalUuidGenerator,
|
|||||||
return generator, nil
|
return generator, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateUuid returns a new uuid
|
// GenerateUuid generates a new uuid
|
||||||
func (u *InternalUuidGenerator) GenerateUuid(idType UuidType) int64 {
|
func (u *InternalUuidGenerator) GenerateUuid(idType UuidType) int64 {
|
||||||
|
uuids := u.GenerateUuids(idType, 1)
|
||||||
|
return uuids[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateUuids generates new uuids
|
||||||
|
func (u *InternalUuidGenerator) GenerateUuids(idType UuidType, count uint8) []int64 {
|
||||||
// 63bits = unixTime(32bits) + uuidType(4bits) + uuidServerId(8bits) + sequentialNumber(19bits)
|
// 63bits = unixTime(32bits) + uuidType(4bits) + uuidServerId(8bits) + sequentialNumber(19bits)
|
||||||
|
|
||||||
|
uuids := make([]int64, count)
|
||||||
|
|
||||||
|
if count < 1 {
|
||||||
|
return uuids
|
||||||
|
}
|
||||||
|
|
||||||
var unixTime uint64
|
var unixTime uint64
|
||||||
var newSeqId uint64
|
var newFirstSeqId uint64
|
||||||
|
var newLastSeqId uint64
|
||||||
uuidType := uint8(idType)
|
uuidType := uint8(idType)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
unixTime = uint64(time.Now().Unix())
|
unixTime = uint64(time.Now().Unix())
|
||||||
newSeqId = atomic.AddUint64(&u.uuidSeqNumbers[uuidType], 1)
|
newLastSeqId = atomic.AddUint64(&u.uuidSeqNumbers[uuidType], uint64(count))
|
||||||
|
|
||||||
if newSeqId>>seqNumberIdBits == unixTime {
|
if newLastSeqId>>seqNumberIdBits == unixTime {
|
||||||
|
newFirstSeqId = newLastSeqId - uint64(count-1)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSeqId := newSeqId
|
currentSeqId := newLastSeqId
|
||||||
newSeqId = unixTime << seqNumberIdBits
|
newFirstSeqId = unixTime << seqNumberIdBits
|
||||||
|
newLastSeqId = newFirstSeqId + uint64(count-1)
|
||||||
|
|
||||||
if atomic.CompareAndSwapUint64(&u.uuidSeqNumbers[uuidType], currentSeqId, newSeqId) {
|
if atomic.CompareAndSwapUint64(&u.uuidSeqNumbers[uuidType], currentSeqId, newLastSeqId) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
seqId := newSeqId & seqNumberIdMask
|
for i := 0; i < int(count); i++ {
|
||||||
|
seqId := (newFirstSeqId + uint64(i)) & seqNumberIdMask
|
||||||
|
uuids[i] = u.assembleUuid(unixTime, uuidType, seqId)
|
||||||
|
}
|
||||||
|
|
||||||
unixTimePart := (int64(unixTime) & internalUuidUnixTimeMask) << (internalUuidTypeBits + internalUuidServerIdBits + internalUuidSeqIdBits)
|
return uuids
|
||||||
uuidTypePart := (int64(uuidType) & internalUuidTypeMask) << (internalUuidServerIdBits + internalUuidSeqIdBits)
|
|
||||||
uuidServerIdPart := (int64(u.uuidServerId) & internalUuidServerIdMask) << internalUuidSeqIdBits
|
|
||||||
seqIdPart := int64(seqId) & internalUuidSeqIdMask
|
|
||||||
|
|
||||||
uuid := unixTimePart | uuidTypePart | uuidServerIdPart | seqIdPart
|
|
||||||
|
|
||||||
return uuid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseUuidInfo returns a info struct which contains all information in uuid
|
// ParseUuidInfo returns a info struct which contains all information in uuid
|
||||||
@@ -104,3 +115,12 @@ func (u *InternalUuidGenerator) ParseUuidInfo(uuid int64) *InternalUuidInfo {
|
|||||||
SequentialId: seqId,
|
SequentialId: seqId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *InternalUuidGenerator) assembleUuid(unixTime uint64, uuidType uint8, seqId uint64) int64 {
|
||||||
|
unixTimePart := (int64(unixTime) & internalUuidUnixTimeMask) << (internalUuidTypeBits + internalUuidServerIdBits + internalUuidSeqIdBits)
|
||||||
|
uuidTypePart := (int64(uuidType) & internalUuidTypeMask) << (internalUuidServerIdBits + internalUuidSeqIdBits)
|
||||||
|
uuidServerIdPart := (int64(u.uuidServerId) & internalUuidServerIdMask) << internalUuidSeqIdBits
|
||||||
|
seqIdPart := int64(seqId) & internalUuidSeqIdMask
|
||||||
|
|
||||||
|
return unixTimePart | uuidTypePart | uuidServerIdPart | seqIdPart
|
||||||
|
}
|
||||||
|
|||||||
@@ -121,3 +121,126 @@ func TestGenerateUuid_1000000TimesConcurrent(t *testing.T) {
|
|||||||
|
|
||||||
waitGroup.Wait()
|
waitGroup.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGenerateUuids_Count0(t *testing.T) {
|
||||||
|
expectedUuidServerId := uint8(90)
|
||||||
|
expectedUuidType := UUID_TYPE_TRANSACTION
|
||||||
|
|
||||||
|
generator, _ := NewInternalUuidGenerator(&settings.Config{UuidServerId: expectedUuidServerId})
|
||||||
|
uuids := generator.GenerateUuids(expectedUuidType, 0)
|
||||||
|
|
||||||
|
assert.NotEqual(t, nil, uuids)
|
||||||
|
assert.Equal(t, 0, len(uuids))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateUuids_Count255(t *testing.T) {
|
||||||
|
expectedUnixTime := time.Now().Unix()
|
||||||
|
expectedUuidServerId := uint8(90)
|
||||||
|
expectedUuidType := UUID_TYPE_TRANSACTION
|
||||||
|
expectedUuidCount := uint8(255)
|
||||||
|
|
||||||
|
generator, _ := NewInternalUuidGenerator(&settings.Config{UuidServerId: expectedUuidServerId})
|
||||||
|
uuids := generator.GenerateUuids(expectedUuidType, expectedUuidCount)
|
||||||
|
|
||||||
|
for i := 0; i < int(expectedUuidCount); i++ {
|
||||||
|
uuidInfo := generator.ParseUuidInfo(uuids[i])
|
||||||
|
|
||||||
|
actualUnixTime := uuidInfo.UnixTime
|
||||||
|
assert.Equal(t, uint32(expectedUnixTime), actualUnixTime)
|
||||||
|
|
||||||
|
actualUuidServerId := uuidInfo.UuidServerId
|
||||||
|
assert.Equal(t, expectedUuidServerId, actualUuidServerId)
|
||||||
|
|
||||||
|
actualUuidType := uuidInfo.UuidType
|
||||||
|
assert.Equal(t, uint8(expectedUuidType), actualUuidType)
|
||||||
|
|
||||||
|
expectedSeqId := i
|
||||||
|
actualSeqId := uuidInfo.SequentialId
|
||||||
|
assert.Equal(t, uint32(expectedSeqId), actualSeqId)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, int(expectedUuidCount), len(uuids))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateUuids_30TimesIn3Seconds(t *testing.T) {
|
||||||
|
expectedUuidServerId := uint8(90)
|
||||||
|
expectedUuidType := UUID_TYPE_TRANSACTION
|
||||||
|
expectedUuidCount := uint8(255)
|
||||||
|
|
||||||
|
generator, _ := NewInternalUuidGenerator(&settings.Config{UuidServerId: expectedUuidServerId})
|
||||||
|
|
||||||
|
for cycle := 0; cycle < 30; cycle++ {
|
||||||
|
expectedUnixTime := time.Now().Unix()
|
||||||
|
uuids := generator.GenerateUuids(expectedUuidType, expectedUuidCount)
|
||||||
|
var firstSeqId uint32
|
||||||
|
|
||||||
|
for i := 0; i < int(expectedUuidCount); i++ {
|
||||||
|
uuidInfo := generator.ParseUuidInfo(uuids[i])
|
||||||
|
|
||||||
|
actualUnixTime := uuidInfo.UnixTime
|
||||||
|
assert.Equal(t, uint32(expectedUnixTime), actualUnixTime)
|
||||||
|
|
||||||
|
actualUuidServerId := uuidInfo.UuidServerId
|
||||||
|
assert.Equal(t, expectedUuidServerId, actualUuidServerId)
|
||||||
|
|
||||||
|
actualUuidType := uuidInfo.UuidType
|
||||||
|
assert.Equal(t, uint8(expectedUuidType), actualUuidType)
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
firstSeqId = uuidInfo.SequentialId
|
||||||
|
} else {
|
||||||
|
expectedSeqId := firstSeqId + uint32(i)
|
||||||
|
actualSeqId := uuidInfo.SequentialId
|
||||||
|
assert.Equal(t, expectedSeqId, actualSeqId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateUuids_1000000TimesConcurrent(t *testing.T) {
|
||||||
|
concurrentCount := 50
|
||||||
|
expectedUuidCount := uint8(100)
|
||||||
|
generator, _ := NewInternalUuidGenerator(&settings.Config{UuidServerId: 3})
|
||||||
|
var mutex sync.Mutex
|
||||||
|
var generatedIds sync.Map
|
||||||
|
var waitGroup sync.WaitGroup
|
||||||
|
|
||||||
|
for routineIndex := 0; routineIndex < concurrentCount; routineIndex++ {
|
||||||
|
go func() {
|
||||||
|
waitGroup.Add(1)
|
||||||
|
|
||||||
|
for cycle := 0; cycle < 400; cycle++ {
|
||||||
|
if cycle%100 == 0 { // each server can only generate 500,000 (50 * 10000) uuids in one second
|
||||||
|
time.Sleep(1000 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedUnixTime := time.Now().Unix()
|
||||||
|
uuids := generator.GenerateUuids(UUID_TYPE_USER, expectedUuidCount)
|
||||||
|
|
||||||
|
for i := 0; i < int(expectedUuidCount); i++ {
|
||||||
|
uuidInfo := generator.ParseUuidInfo(uuids[i])
|
||||||
|
|
||||||
|
if uint32(expectedUnixTime) != uuidInfo.UnixTime {
|
||||||
|
mutex.Lock()
|
||||||
|
assert.Equal(t, uint32(expectedUnixTime), uuidInfo.UnixTime)
|
||||||
|
mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if existedUnixTime, exists := generatedIds.Load(uuids[i]); exists {
|
||||||
|
mutex.Lock()
|
||||||
|
assert.Fail(t, fmt.Sprintf("uuid \"%d\" conflicts, seq id is %d, existed unixtime is %d, current unix time is %d", uuids[i], uuidInfo.SequentialId, existedUnixTime, uuidInfo.UnixTime))
|
||||||
|
mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedIds.Store(uuids[i], uuidInfo.UnixTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waitGroup.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
waitGroup.Wait()
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,3 +31,8 @@ func InitializeUuidGenerator(config *settings.Config) error {
|
|||||||
func (u *UuidContainer) GenerateUuid(uuidType UuidType) int64 {
|
func (u *UuidContainer) GenerateUuid(uuidType UuidType) int64 {
|
||||||
return u.Current.GenerateUuid(uuidType)
|
return u.Current.GenerateUuid(uuidType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateUuids returns new uuids by the current uuid generator
|
||||||
|
func (u *UuidContainer) GenerateUuids(uuidType UuidType, count uint8) []int64 {
|
||||||
|
return u.Current.GenerateUuids(uuidType, count)
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ package uuid
|
|||||||
// UuidGenerator is common uuid generator interface
|
// UuidGenerator is common uuid generator interface
|
||||||
type UuidGenerator interface {
|
type UuidGenerator interface {
|
||||||
GenerateUuid(uuidType UuidType) int64
|
GenerateUuid(uuidType UuidType) int64
|
||||||
|
GenerateUuids(uuidType UuidType, count uint8) []int64
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user