etcd/pkg/idutil/id.go
2015-01-13 11:54:51 -08:00

76 lines
2.0 KiB
Go

/*
Copyright 2014 CoreOS, 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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package idutil
import (
"math"
"sync"
"time"
)
const (
tsLen = 5 * 8
cntLen = 2 * 8
suffixLen = tsLen + cntLen
)
// The initial id is in this format:
// High order byte is memberID, next 5 bytes are from timestamp,
// and low order 2 bytes are 0s.
// | prefix | suffix |
// | 1 byte | 5 bytes | 2 bytes |
// | memberID | timestamp | cnt |
//
// The timestamp 5 bytes is different when the machine is restart
// after 1 ms and before 35 years.
//
// It increases suffix to generate the next id.
// The count field may overflow to timestamp field, which is intentional.
// It helps to extend the event window to 2^56. This doesn't break that
// id generated after restart is unique because etcd throughput is <<
// 65536req/ms.
type Generator struct {
mu sync.Mutex
// high order byte
prefix uint64
// low order 7 bytes
suffix uint64
}
func NewGenerator(memberID uint8, now time.Time) *Generator {
prefix := uint64(memberID) << suffixLen
unixMilli := uint64(now.UnixNano()) / uint64(time.Millisecond/time.Nanosecond)
suffix := lowbit(unixMilli, tsLen) << cntLen
return &Generator{
prefix: prefix,
suffix: suffix,
}
}
// Next generates a id that is unique.
func (g *Generator) Next() uint64 {
g.mu.Lock()
defer g.mu.Unlock()
g.suffix++
id := g.prefix | lowbit(g.suffix, suffixLen)
return id
}
func lowbit(x uint64, n uint) uint64 {
return x & (math.MaxUint64 >> (64 - n))
}