x/snowflake/snowflake.go

98 lines
1.8 KiB
Go
Raw Normal View History

2020-06-11 21:26:57 -07:00
package snowflake
import (
"hash/fnv"
"math"
"net"
"sync"
"time"
)
const (
totalBits = 64
epochBits = 32
nodeIDBits = 10
sequenceBits = 12
2023-04-05 21:20:45 -07:00
// Custom Epoch (January 1, 2015 Midnight UTC = 2015-01-01T00:00:00Z) .
customEpoch int64 = 1420070400000
2020-06-11 21:26:57 -07:00
)
2023-04-05 21:20:45 -07:00
var (
maxNodeID int64
maxSequence int64
timestampMutex sync.Mutex
sequenceMutex sync.Mutex
nodeID int64
lastTimestamp int64 = 0
sequence int64
)
2020-06-11 21:26:57 -07:00
2023-04-05 21:20:45 -07:00
const two = 2
2020-06-11 21:26:57 -07:00
func init() {
2023-04-05 21:20:45 -07:00
maxNodeID = int64(math.Pow(two, nodeIDBits) - 1)
maxSequence = int64(math.Pow(two, sequenceBits) - 1)
2020-06-11 21:26:57 -07:00
nodeID = generateNodeID()
}
func generateNodeID() int64 {
var nodeID int64
2023-04-05 21:20:45 -07:00
2020-06-11 21:26:57 -07:00
if interfaces, err := net.Interfaces(); err == nil {
h := fnv.New32a()
for _, i := range interfaces {
h.Write(i.HardwareAddr)
}
2023-04-05 21:20:45 -07:00
nodeID = int64(h.Sum32())
2020-06-11 21:26:57 -07:00
} else {
panic("interfaces not available")
}
2023-04-05 21:20:45 -07:00
2020-06-11 21:26:57 -07:00
nodeID = nodeID & maxNodeID
2023-04-05 21:20:45 -07:00
2020-06-11 21:26:57 -07:00
return nodeID
}
2023-04-05 21:20:45 -07:00
// Next returns the next logical snowflake.
func Next() int64 {
2020-06-11 21:26:57 -07:00
timestampMutex.Lock()
currentTimestamp := ts()
timestampMutex.Unlock()
sequenceMutex.Lock()
if currentTimestamp == lastTimestamp {
sequence = (sequence + 1) & maxSequence
if sequence == 0 {
// Sequence Exhausted, wait till next millisecond.
currentTimestamp = waitNextMillis(currentTimestamp)
}
} else {
sequence = 0
}
sequenceMutex.Unlock()
lastTimestamp = currentTimestamp
// id := currentTimestamp << (totalBits - epochBits)
// id |= (nodeID << (totalBits - epochBits - nodeIDBits))
// id |= sequence
var id int64 = currentTimestamp << (nodeIDBits + sequenceBits)
id |= (nodeID << sequenceBits)
2020-06-11 21:26:57 -07:00
id |= sequence
2020-06-11 21:26:57 -07:00
return id
}
func ts() int64 {
return int64(time.Now().UnixNano()/1000000) - customEpoch
2020-06-11 21:26:57 -07:00
}
func waitNextMillis(currentTimestamp int64) int64 {
2020-06-11 21:26:57 -07:00
for currentTimestamp == lastTimestamp {
currentTimestamp = ts()
}
2023-04-05 21:20:45 -07:00
2020-06-11 21:26:57 -07:00
return currentTimestamp
}