├── snowflake_test.go └── snowflake.go /snowflake_test.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // go test -bench="." -test.benchmem 8 | func BenchmarkSnowFlake(t *testing.B) { 9 | uuid, _ := NewUUID(2) 10 | var pre int64 = 0 11 | 12 | for i := 0; i < t.N; i++ { 13 | id, err := uuid.Next() 14 | if err != nil { 15 | t.Log("ERROR:", err) 16 | continue 17 | } 18 | 19 | if id < 0 { 20 | t.Fatalf("id: %d < 0", id) 21 | } 22 | 23 | if pre >= id { 24 | t.Fatalf("pre: %d >= id: :%d", pre, id) 25 | } 26 | 27 | pre = id 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /snowflake.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | const ( 10 | workerBits = 10 11 | sequenceBits = 12 12 | workerShift = 12 13 | timestampShift = 22 14 | 15 | maxWorker = -1 ^ -1< maxWorker || worker < 0 { 32 | return nil, fmt.Errorf("worker can't be greater than %d or less than 0", maxWorker) 33 | } 34 | 35 | uuid.worker = worker 36 | uuid.lastTimestamp = -1 37 | uuid.sequence = 0 38 | 39 | return uuid, nil 40 | } 41 | 42 | func (u *UUID) Next() (int64, error) { 43 | u.Lock() 44 | ts := nowMillis() 45 | if ts < u.lastTimestamp { 46 | u.Unlock() 47 | return 0, fmt.Errorf("clock is moving backwards") 48 | } 49 | 50 | if ts == u.lastTimestamp { 51 | u.sequence = (u.sequence + 1) & sequenceMask 52 | if u.sequence == 0 { 53 | ts = tilNextMillis(u.lastTimestamp) 54 | } 55 | } else { 56 | u.sequence = 0 57 | } 58 | 59 | u.lastTimestamp = ts 60 | id := ((ts - Epoch) << timestampShift) | (u.worker << workerShift) | u.sequence 61 | u.Unlock() 62 | 63 | return id, nil 64 | } 65 | 66 | func nowMillis() int64 { 67 | return time.Now().UnixNano() / 1000000 68 | } 69 | 70 | func tilNextMillis(last int64) int64 { 71 | ts := nowMillis() 72 | for ts <= last { 73 | ts = nowMillis() 74 | } 75 | return ts 76 | } 77 | --------------------------------------------------------------------------------