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.
This commit is contained in:
Reed Allman
2018-03-21 16:01:18 -07:00
committed by Tolga Ceylan
parent 86fda80240
commit b9ff601098
2 changed files with 46 additions and 10 deletions

View File

@@ -11,7 +11,7 @@ type Id [16]byte
var ( var (
machineID uint64 machineID uint64
counter uint64 counter uint32
) )
// SetMachineId may only be called by one thread before any id generation // 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 // Ids are sortable within (not between, thanks to clocks) each machine, with
// a modified base32 encoding exposed for convenience in API usage. // a modified base32 encoding exposed for convenience in API usage.
func New() Id { func New() Id {
var id Id
t := time.Now() t := time.Now()
// NOTE compiler optimizes out division by constant for us // NOTE compiler optimizes out division by constant for us
ms := uint64(t.Unix())*1000 + uint64(t.Nanosecond()/int(time.Millisecond)) 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[0] = byte(ms >> 40)
id[1] = byte(ms >> 32) id[1] = byte(ms >> 32)
@@ -58,14 +62,13 @@ func New() Id {
id[4] = byte(ms >> 8) id[4] = byte(ms >> 8)
id[5] = byte(ms) id[5] = byte(ms)
id[6] = byte(machineID >> 12) id[6] = byte(machineID >> 40)
id[7] = byte(machineID >> 4) 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[12] = byte(count >> 24)
id[13] = byte(count >> 16) id[13] = byte(count >> 16)
id[14] = byte(count >> 8) id[14] = byte(count >> 8)

View File

@@ -1,7 +1,12 @@
package id package id
import ( import (
"encoding/binary"
"fmt"
"math"
"net"
"testing" "testing"
"time"
) )
func BenchmarkGen(b *testing.B) { func BenchmarkGen(b *testing.B) {
@@ -28,3 +33,31 @@ func BenchmarkUnmarshalText(b *testing.B) {
_ = id _ = 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)
}
}