From b9ff6010980f09ab62998e5ff5b7fa8bf9c9884c Mon Sep 17 00:00:00 2001 From: Reed Allman Date: Wed, 21 Mar 2018 16:01:18 -0700 Subject: [PATCH] fix machine id to 48 bits, add test (#877) another bone head fail here. hopefully can leave this alone meow... newID does not get inlined, but doesn't allocate to trigger any stack expansion either. net perf hit on my laptop is 5ns, and we get a test out of it. it will push a new stack, so it's not negligible overhead and we could avoid it. we could keep the logic in both places just to have a test for it separate instead of re-using the function in the hot path. up to us. --- api/id/id.go | 23 +++++++++++++---------- api/id/id_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/api/id/id.go b/api/id/id.go index 9f6d3a4b9..dc71ec3e6 100644 --- a/api/id/id.go +++ b/api/id/id.go @@ -11,7 +11,7 @@ type Id [16]byte var ( machineID uint64 - counter uint64 + counter uint32 ) // SetMachineId may only be called by one thread before any id generation @@ -45,11 +45,15 @@ func SetMachineIdHost(addr net.IP, port uint16) { // Ids are sortable within (not between, thanks to clocks) each machine, with // a modified base32 encoding exposed for convenience in API usage. func New() Id { - var id Id t := time.Now() // NOTE compiler optimizes out division by constant for us ms := uint64(t.Unix())*1000 + uint64(t.Nanosecond()/int(time.Millisecond)) - count := atomic.AddUint64(&counter, 1) + count := atomic.AddUint32(&counter, 1) + return newID(ms, machineID, count) +} + +func newID(ms, machineID uint64, count uint32) Id { + var id Id id[0] = byte(ms >> 40) id[1] = byte(ms >> 32) @@ -58,14 +62,13 @@ func New() Id { id[4] = byte(ms >> 8) id[5] = byte(ms) - id[6] = byte(machineID >> 12) - id[7] = byte(machineID >> 4) + id[6] = byte(machineID >> 40) + id[7] = byte(machineID >> 32) + id[8] = byte(machineID >> 24) + id[9] = byte(machineID >> 16) + id[10] = byte(machineID >> 8) + id[11] = byte(machineID) - id[8] = byte(machineID<<4) | byte((count<<4)>>60) - - id[9] = byte(count >> 48) - id[10] = byte(count >> 40) - id[11] = byte(count >> 32) id[12] = byte(count >> 24) id[13] = byte(count >> 16) id[14] = byte(count >> 8) diff --git a/api/id/id_test.go b/api/id/id_test.go index a21f21cec..136b7ceb9 100644 --- a/api/id/id_test.go +++ b/api/id/id_test.go @@ -1,7 +1,12 @@ package id import ( + "encoding/binary" + "fmt" + "math" + "net" "testing" + "time" ) func BenchmarkGen(b *testing.B) { @@ -28,3 +33,31 @@ func BenchmarkUnmarshalText(b *testing.B) { _ = id } } + +func TestIdRaw(t *testing.T) { + SetMachineIdHost(net.IP{127, 0, 0, 1}, 8080) + + ts := time.Now() + ms := uint64(ts.Unix())*1000 + uint64(ts.Nanosecond()/int(time.Millisecond)) + count := uint32(math.MaxUint32) + id := newID(ms, machineID, count) + fmt.Println(len(id), id) + + var buf [8]byte + copy(buf[2:], id[:6]) + idTime := binary.BigEndian.Uint64(buf[:]) + if ms != idTime { + t.Fatal("id time doesn't not match time given", ms, idTime) + } + + copy(buf[2:], id[6:12]) + idMid := binary.BigEndian.Uint64(buf[:]) + if idMid != machineID { + t.Fatal("machine id mismatch", idMid, machineID) + } + + idCount := binary.BigEndian.Uint32(id[12:16]) + if idCount != count { + t.Fatal("count mismatch", idCount, count) + } +}