done that was fun
parent
9b811d89f0
commit
723b00ab89
|
|
@ -0,0 +1,100 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"hash/fnv"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Println(host_10b)
|
||||
log.Println(epoch)
|
||||
log.Println(epoch.UnixNano() / int64(time.Millisecond) / 10)
|
||||
log.Println(ts_41b())
|
||||
for i := 0; i < 7; i++ {
|
||||
time.Sleep(time.Duration(i) * time.Millisecond)
|
||||
log.Printf("%v = ts_41b/%v/seq_13b/%v/host_10b/%v", id(), ts_41b(), seq_13b(), host_10b)
|
||||
}
|
||||
}
|
||||
|
||||
func id() uint64 {
|
||||
ts := ts_41b() << (64 - 41)
|
||||
seq := seq_13b() << (64 - 41 - 13)
|
||||
host := host_10b
|
||||
return ts | seq | host
|
||||
}
|
||||
|
||||
func ts_41b() uint64 {
|
||||
durationSinceEpoch := time.Now().Sub(epoch)
|
||||
return uint64(durationSinceEpoch/time.Millisecond) / 10
|
||||
}
|
||||
|
||||
var seqLock = &sync.RWMutex{}
|
||||
var seqV = &atomic.Uint64{}
|
||||
var seqTS = &atomic.Int64{}
|
||||
|
||||
func seq_13b() uint64 {
|
||||
_seq_13b_rollover()
|
||||
got := func() uint64 {
|
||||
seqLock.RLock()
|
||||
defer seqLock.RUnlock()
|
||||
return seqV.Add(1)
|
||||
}()
|
||||
if got > (1 << 13) {
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
return seq_13b()
|
||||
}
|
||||
return got
|
||||
}
|
||||
|
||||
func _seq_13b_now() int64 {
|
||||
return time.Now().UnixNano() / int64(time.Millisecond) / 10
|
||||
}
|
||||
|
||||
func _seq_13b_rollover() {
|
||||
now := _seq_13b_now()
|
||||
if seqTS.Load() == now {
|
||||
return
|
||||
}
|
||||
func() {
|
||||
seqLock.Lock()
|
||||
defer seqLock.Unlock()
|
||||
if seqTS.Load() == now {
|
||||
return
|
||||
}
|
||||
seqTS.Store(now)
|
||||
seqV.Store(0)
|
||||
}()
|
||||
}
|
||||
|
||||
var epoch = func() time.Time {
|
||||
t := time.Unix(0, 0)
|
||||
if v := os.Getenv("SNOWFLAKE_UID_SERVICE_EPOCH"); v != "" {
|
||||
// RFC3339 = "2006-01-02T15:04:05Z07:00"
|
||||
t2, err := time.ParseInLocation(time.RFC3339, v, time.UTC)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
t = t2
|
||||
}
|
||||
return t // uint64(t.UnixNano() / int64(time.Millisecond) / 10)
|
||||
}()
|
||||
|
||||
var host_10b = func() uint64 {
|
||||
if v := os.Getenv("SNOWFLAKE_UID_SERVICE_HOST"); v != "" {
|
||||
v, err := strconv.ParseUint(v, 10, 10)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ui64 := uint64(v)
|
||||
return uint64(ui64 << 54 >> 54)
|
||||
}
|
||||
hash := fnv.New32()
|
||||
hash.Write([]byte(os.Getenv("HOSTNAME")))
|
||||
ui32 := hash.Sum32()
|
||||
return uint64((ui32 << 22) >> 22)
|
||||
}()
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestID(t *testing.T) {
|
||||
last := id()
|
||||
for i := 0; i < 1000; i++ {
|
||||
next := id()
|
||||
if next <= last {
|
||||
t.Error(i, last, next)
|
||||
}
|
||||
last = next
|
||||
}
|
||||
}
|
||||
|
||||
func TestTS(t *testing.T) {
|
||||
got := int64(ts_41b())
|
||||
if got != ((got << (64 - 41)) >> (64 - 41)) {
|
||||
t.Error("got > 41b")
|
||||
}
|
||||
gotTime := time.Unix(got/100, 10*(got%100))
|
||||
if time.Since(gotTime) > time.Second {
|
||||
t.Error(gotTime)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeq(t *testing.T) {
|
||||
last := seq_13b()
|
||||
for i := 0; i < 1000; i++ {
|
||||
next := seq_13b()
|
||||
if next <= last {
|
||||
t.Error(i, last, next)
|
||||
}
|
||||
last = next
|
||||
}
|
||||
|
||||
seqV.Store(1 << 14)
|
||||
got := seq_13b()
|
||||
if got > 2 {
|
||||
t.Error(got)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue