2020-06-12 00:26:57 -04:00
|
|
|
package snowflake
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"hash/fnv"
|
|
|
|
"math"
|
|
|
|
"net"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
totalBits = 64
|
|
|
|
epochBits = 32
|
|
|
|
nodeIDBits = 10
|
|
|
|
sequenceBits = 12
|
|
|
|
|
|
|
|
// Custom Epoch (January 1, 2015 Midnight UTC = 2015-01-01T00:00:00Z)
|
2020-07-09 23:02:23 -04:00
|
|
|
customEpoch int64 = 1420070400000
|
2020-06-12 00:26:57 -04:00
|
|
|
)
|
|
|
|
|
2020-07-09 23:02:23 -04:00
|
|
|
var maxNodeID int64
|
|
|
|
var maxSequence int64
|
2020-06-12 00:26:57 -04:00
|
|
|
|
2020-07-09 23:02:23 -04:00
|
|
|
var nodeID int64
|
|
|
|
var lastTimestamp int64 = 0
|
|
|
|
var sequence int64
|
2020-06-12 00:26:57 -04:00
|
|
|
|
|
|
|
func init() {
|
2020-07-09 23:02:23 -04:00
|
|
|
maxNodeID = int64(math.Pow(2, nodeIDBits) - 1)
|
|
|
|
maxSequence = int64(math.Pow(2, sequenceBits) - 1)
|
2020-06-12 00:26:57 -04:00
|
|
|
nodeID = generateNodeID()
|
|
|
|
}
|
|
|
|
|
2020-07-09 23:02:23 -04:00
|
|
|
func generateNodeID() int64 {
|
|
|
|
var nodeID int64
|
2020-06-12 00:26:57 -04:00
|
|
|
if interfaces, err := net.Interfaces(); err == nil {
|
|
|
|
h := fnv.New32a()
|
|
|
|
for _, i := range interfaces {
|
|
|
|
h.Write(i.HardwareAddr)
|
|
|
|
}
|
2020-07-09 23:02:23 -04:00
|
|
|
nodeID = int64(h.Sum32())
|
2020-06-12 00:26:57 -04:00
|
|
|
} else {
|
|
|
|
panic("interfaces not available")
|
|
|
|
}
|
|
|
|
nodeID = nodeID & maxNodeID
|
|
|
|
return nodeID
|
|
|
|
}
|
|
|
|
|
|
|
|
var timestampMutex sync.Mutex
|
|
|
|
var sequenceMutex sync.Mutex
|
|
|
|
|
|
|
|
// Next returns the next logical snowflake
|
2020-07-09 23:02:23 -04:00
|
|
|
func Next() int64 {
|
2020-06-12 00:26:57 -04: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
|
2020-07-09 23:02:23 -04:00
|
|
|
// id := currentTimestamp << (totalBits - epochBits)
|
|
|
|
// id |= (nodeID << (totalBits - epochBits - nodeIDBits))
|
|
|
|
// id |= sequence
|
|
|
|
var id int64 = currentTimestamp << (nodeIDBits + sequenceBits)
|
|
|
|
id |= (nodeID << sequenceBits)
|
2020-06-12 00:26:57 -04:00
|
|
|
id |= sequence
|
2020-07-09 23:02:23 -04:00
|
|
|
|
2020-06-12 00:26:57 -04:00
|
|
|
fmt.Printf("%b\n", id)
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
|
2020-07-09 23:02:23 -04:00
|
|
|
func ts() int64 {
|
|
|
|
return int64(time.Now().UnixNano()/1000000) - customEpoch
|
2020-06-12 00:26:57 -04:00
|
|
|
}
|
|
|
|
|
2020-07-09 23:02:23 -04:00
|
|
|
func waitNextMillis(currentTimestamp int64) int64 {
|
2020-06-12 00:26:57 -04:00
|
|
|
for currentTimestamp == lastTimestamp {
|
|
|
|
currentTimestamp = ts()
|
|
|
|
}
|
|
|
|
return currentTimestamp
|
|
|
|
}
|