done that was fun

master
Bel LaPointe 2023-03-13 08:35:12 -06:00
parent 9b811d89f0
commit 723b00ab89
2 changed files with 145 additions and 0 deletions

100
main.go Normal file
View File

@ -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)
}()

45
main_test.go Normal file
View File

@ -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)
}
}