pkg/idutil: reduce conflict rate from 1% to 0.005%

Perviously, we only use 8bits from member identification
in id generation. The conflict rate is A(256,3)/256^3, which
is around 1%. Now we use 16bites to reduce the rate to 0.005%.

We can attach the full member id into id generation if needed...
This commit is contained in:
Xiang Li 2016-02-04 13:06:27 -08:00
parent 0cdf1c45cf
commit e44e753e66
3 changed files with 9 additions and 9 deletions

View File

@ -358,7 +358,7 @@ func NewServer(cfg *ServerConfig) (*EtcdServer, error) {
lstats: lstats, lstats: lstats,
SyncTicker: time.Tick(500 * time.Millisecond), SyncTicker: time.Tick(500 * time.Millisecond),
peerRt: prt, peerRt: prt,
reqIDGen: idutil.NewGenerator(uint8(id), time.Now()), reqIDGen: idutil.NewGenerator(uint16(id), time.Now()),
forceVersionC: make(chan struct{}), forceVersionC: make(chan struct{}),
msgSnapC: make(chan raftpb.Message, maxInFlightMsgSnap), msgSnapC: make(chan raftpb.Message, maxInFlightMsgSnap),
} }

View File

@ -24,7 +24,7 @@ import (
const ( const (
tsLen = 5 * 8 tsLen = 5 * 8
cntLen = 2 * 8 cntLen = 8
suffixLen = tsLen + cntLen suffixLen = tsLen + cntLen
) )
@ -32,7 +32,7 @@ const (
// High order byte is memberID, next 5 bytes are from timestamp, // High order byte is memberID, next 5 bytes are from timestamp,
// and low order 2 bytes are 0s. // and low order 2 bytes are 0s.
// | prefix | suffix | // | prefix | suffix |
// | 1 byte | 5 bytes | 2 bytes | // | 2 bytes | 5 bytes | 1 byte |
// | memberID | timestamp | cnt | // | memberID | timestamp | cnt |
// //
// The timestamp 5 bytes is different when the machine is restart // The timestamp 5 bytes is different when the machine is restart
@ -42,16 +42,16 @@ const (
// The count field may overflow to timestamp field, which is intentional. // 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 // 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 << // id generated after restart is unique because etcd throughput is <<
// 65536req/ms. // 256req/ms(250k reqs/second).
type Generator struct { type Generator struct {
mu sync.Mutex mu sync.Mutex
// high order byte // high order 2 bytes
prefix uint64 prefix uint64
// low order 7 bytes // low order 6 bytes
suffix uint64 suffix uint64
} }
func NewGenerator(memberID uint8, now time.Time) *Generator { func NewGenerator(memberID uint16, now time.Time) *Generator {
prefix := uint64(memberID) << suffixLen prefix := uint64(memberID) << suffixLen
unixMilli := uint64(now.UnixNano()) / uint64(time.Millisecond/time.Nanosecond) unixMilli := uint64(now.UnixNano()) / uint64(time.Millisecond/time.Nanosecond)
suffix := lowbit(unixMilli, tsLen) << cntLen suffix := lowbit(unixMilli, tsLen) << cntLen

View File

@ -22,7 +22,7 @@ import (
func TestNewGenerator(t *testing.T) { func TestNewGenerator(t *testing.T) {
g := NewGenerator(0x12, time.Unix(0, 0).Add(0x3456*time.Millisecond)) g := NewGenerator(0x12, time.Unix(0, 0).Add(0x3456*time.Millisecond))
id := g.Next() id := g.Next()
wid := uint64(0x1200000034560001) wid := uint64(0x12000000345601)
if id != wid { if id != wid {
t.Errorf("id = %x, want %x", id, wid) t.Errorf("id = %x, want %x", id, wid)
} }
@ -45,7 +45,7 @@ func TestNewGeneratorUnique(t *testing.T) {
func TestNext(t *testing.T) { func TestNext(t *testing.T) {
g := NewGenerator(0x12, time.Unix(0, 0).Add(0x3456*time.Millisecond)) g := NewGenerator(0x12, time.Unix(0, 0).Add(0x3456*time.Millisecond))
wid := uint64(0x1200000034560001) wid := uint64(0x12000000345601)
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
id := g.Next() id := g.Next()
if id != wid+uint64(i) { if id != wid+uint64(i) {