diff --git a/uid.go b/uid.go index 274d3a4..db7c727 100644 --- a/uid.go +++ b/uid.go @@ -33,10 +33,24 @@ type IdCreatorHelper interface { CreateMixedId(prefix string, id int64, parts ...interface{}) string } +// Range Range define the range [start, end] of ids. +type Range struct { + Next int64 // next id + Status int // 0:invlid; 1:ok; 2:useout + Prefix string // prefix + Start int64 // begin id of this range(include) + End int64 // end id of this range(include) + Length int // lenght of this range +} type rangeQueue struct { deque.Deque[*Range] } +const ( + RANGE_STATUS_OK = 1 + RANGE_STATUS_USEOUT = 2 +) + // idCreator type idCreator struct { lock sync.Mutex @@ -59,10 +73,7 @@ func NewIdCreator(typ string, helper IdCreatorHelper) IdCreator { // Step 2. generate the first range. if prefix, err := idg.createDefaultPrefix(); err == nil { - if err := idg.createNewRange(prefix); err != nil { - log.Errorf("generate the first range failed: %s", err) - panic("NewIdCreator error") - } + idg.createNewRangeWithErrorPanic(prefix) } // Step 3. backgroud task for generate range @@ -81,7 +92,7 @@ func NewIdCreator(typ string, helper IdCreatorHelper) IdCreator { } if int(timeoutMs) > 0 { - idg.createNewRange(prefix) + idg.createNewRangeWithErrorPanic(prefix) time.Sleep(timeoutMs * time.Millisecond) } else if int(timeoutMs) < 0 { time.Sleep((-timeoutMs) * time.Millisecond) @@ -99,7 +110,8 @@ func (idg *idCreator) GetId(idparts ...interface{}) string { for { var id int64 = 0 var currange = idg.getCurrentRange(prefix) - log.Infof("success get current range:(%s, %d)", prefix, currange.Start) + log.Infof("success get current range:(%s, %d-%d)", + prefix, currange.Start, currange.End) // Step 1. Optimistic[no lock] to add id in current range. for { id = atomic.LoadInt64(&currange.Next) @@ -127,16 +139,6 @@ func (idg *idCreator) GetId(idparts ...interface{}) string { } } -// Range Range define the range [start, end] of ids. -type Range struct { - Next int64 // next id - Status int // 0:invlid; 1:ok; 2:used - Prefix string // prefix - Start int64 // begin id of this range(include) - End int64 // end id of this range(include) - Length int // lenght of this range -} - func (idg *idCreator) createDefaultPrefix() (prefix string, err error) { defer func() { if r := recover(); r != nil { @@ -149,25 +151,18 @@ func (idg *idCreator) createDefaultPrefix() (prefix string, err error) { return prefix, err } -func (idg *idCreator) removeOldRange(prefix string) error { - if lock, ok := idg.RangesLocks[prefix]; ok { - lock.Lock() - defer lock.Unlock() - - queue := idg.Ranges[prefix] - for { - r := queue.Front() - if r.Status == 1 { - break - } else if r.Status == 2 { - queue.PopFront() - } else { - panic("invalid status, maybe somethong bug") - } - } +func (idg *idCreator) createNewRangeWithErrorPanic(prefix string) { + if err := idg.createNewRange(prefix); err != nil { + panic("create new range error:" + err.Error()) } - return nil } + +func (idg *idCreator) removeOldRangeWithErrorPanic(prefix string) { + if err := idg.removeOldRange(prefix); err != nil { + panic("remove old range error:" + err.Error()) + } +} + func (idg *idCreator) createNewRange(prefix string) error { // Step 1. create range from db or something else. start, length, err := idg.Helper.CreateRange(prefix) @@ -186,47 +181,68 @@ func (idg *idCreator) createNewRange(prefix string) error { idg.Ranges[prefix] = &rangeQueue{Deque: *deque.New[*Range]()} idg.RangesLocks[prefix] = &sync.Mutex{} } - var lock = idg.RangesLocks[prefix] var queue = idg.Ranges[prefix] + var lock = idg.RangesLocks[prefix] lock.Lock() defer lock.Unlock() // Step 3. push to ranges queue. - queue.PushBack( - &Range{ - Next: start, - Status: 1, - Prefix: prefix, - Start: start, - End: start + int64(length) - 1, - Length: length, - }) + var r = &Range{ + Next: start, + Status: RANGE_STATUS_OK, + Prefix: prefix, + Start: start, + End: start + int64(length) - 1, + Length: length, + } + queue.PushBack(r) + log.Infof("success createNewRange (%s, %d-%d)", prefix, start, r.End) + return nil +} + +func (idg *idCreator) removeOldRange(prefix string) error { + if lock, ok := idg.RangesLocks[prefix]; ok { + lock.Lock() + defer lock.Unlock() + + queue := idg.Ranges[prefix] + for queue.Len() > 0 { + r := queue.Front() + if r.Status == RANGE_STATUS_OK { + break + } else if r.Status == RANGE_STATUS_USEOUT { + queue.PopFront() + log.Infof("success remove old range:(%s,%d-%d)", + prefix, r.Start, r.End) + } else { + panic("invalid status, maybe somethong bug") + } + } + } else { + log.Infof("rare case: rangequeue:(%s) have remove by others", prefix) + } return nil } func (idg *idCreator) getCurrentRange(prefix string) (r *Range) { - // Step 0. get ranges queue by prefix. var ( - trycount = 0 - queue *rangeQueue = nil - ok = false + ok = false + lock *sync.Mutex = nil + queue *rangeQueue = nil ) - for trycount = 0; !ok && trycount < 3; trycount++ { - if queue, ok = idg.Ranges[prefix]; !ok { - idg.createNewRange(prefix) - } - } - if !ok { - panic("maybe something bug, cannot found range queue after createNewRange") - } - queue, ok = idg.Ranges[prefix] - if !ok { + // Step 0. get ranges queue by prefix. + for trycount := 0; !ok && trycount < 3; trycount++ { + if queue, ok = idg.Ranges[prefix]; ok { + lock = idg.RangesLocks[prefix] + break + } + // try create new range idg.createNewRange(prefix) } - var lock, okk = idg.RangesLocks[prefix] - if !okk { - panic("unreachable code: lock is not exist") + if !ok { + panic("maybe something bug or too many pressures," + + "cannot found range queue after createNewRange retry 3 times") } // Step 1. try read from queue directly. @@ -238,7 +254,7 @@ func (idg *idCreator) getCurrentRange(prefix string) (r *Range) { } lock.Unlock() - // Step 2. try create a new range & try again. + // Step 2. retry create a new range & try again. idg.createNewRange(prefix) lock.Lock() defer lock.Unlock() @@ -253,31 +269,14 @@ func (idg *idCreator) turnToNextRange(r *Range) { // We donot lock the range, because there is no too much // side effect even if 'createNewRange' execute twice. switch r.Status { - case 1: - r.Status = 2 - idg.removeOldRange(r.Prefix) - idg.createNewRange(r.Prefix) - case 2: - //have turn by others, we do nothing. + case RANGE_STATUS_OK: + r.Status = RANGE_STATUS_USEOUT + idg.createNewRangeWithErrorPanic(r.Prefix) + idg.removeOldRangeWithErrorPanic(r.Prefix) + case RANGE_STATUS_USEOUT: + // have turn by others, we do nothing. log.Debugf("have turn by others, we do nothing.") default: panic("invalid status, maybe somethong bug") } } - -// demo demo range creator, return current timestamp % 86400 * lenght. -// **DONOT** use in product enviroment. -// **DONOT** use in product enviroment. -// **DONOT** use in product enviroment. -type demo struct{} - -func (d *demo) CreateRange(typ, prefix string) (start int64, length int, err error) { - var curdate string = time.Now().Format("20060102") - if prefix != curdate { - return 0, 0, fmt.Errorf("demo just support prefix is current date") - } - - start = int64((time.Now().Unix() % 86400) * 10000) - length = 10000 - return start, length, nil -}