guid.go 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. package nsqd
  2. // the core algorithm here was borrowed from:
  3. // Blake Mizerany's `noeqd` https://github.com/bmizerany/noeqd
  4. // and indirectly:
  5. // Twitter's `snowflake` https://github.com/twitter/snowflake
  6. // only minor cleanup and changes to introduce a type, combine the concept
  7. // of workerID + datacenterId into a single identifier, and modify the
  8. // behavior when sequences rollover for our specific implementation needs
  9. import (
  10. "encoding/hex"
  11. "errors"
  12. "sync"
  13. "time"
  14. )
  15. const (
  16. nodeIDBits = uint64(10)
  17. sequenceBits = uint64(12)
  18. nodeIDShift = sequenceBits
  19. timestampShift = sequenceBits + nodeIDBits
  20. sequenceMask = int64(-1) ^ (int64(-1) << sequenceBits)
  21. // ( 2012-10-28 16:23:42 UTC ).UnixNano() >> 20
  22. twepoch = int64(1288834974288)
  23. )
  24. var ErrTimeBackwards = errors.New("time has gone backwards")
  25. var ErrSequenceExpired = errors.New("sequence expired")
  26. var ErrIDBackwards = errors.New("ID went backward")
  27. type guid int64
  28. type guidFactory struct {
  29. sync.Mutex
  30. nodeID int64
  31. sequence int64
  32. lastTimestamp int64
  33. lastID guid
  34. }
  35. func NewGUIDFactory(nodeID int64) *guidFactory {
  36. return &guidFactory{
  37. nodeID: nodeID,
  38. }
  39. }
  40. func (f *guidFactory) NewGUID() (guid, error) {
  41. f.Lock()
  42. // divide by 1048576, giving pseudo-milliseconds
  43. ts := time.Now().UnixNano() >> 20
  44. if ts < f.lastTimestamp {
  45. f.Unlock()
  46. return 0, ErrTimeBackwards
  47. }
  48. if f.lastTimestamp == ts {
  49. f.sequence = (f.sequence + 1) & sequenceMask
  50. if f.sequence == 0 {
  51. f.Unlock()
  52. return 0, ErrSequenceExpired
  53. }
  54. } else {
  55. f.sequence = 0
  56. }
  57. f.lastTimestamp = ts
  58. id := guid(((ts - twepoch) << timestampShift) |
  59. (f.nodeID << nodeIDShift) |
  60. f.sequence)
  61. if id <= f.lastID {
  62. f.Unlock()
  63. return 0, ErrIDBackwards
  64. }
  65. f.lastID = id
  66. f.Unlock()
  67. return id, nil
  68. }
  69. func (g guid) Hex() MessageID {
  70. var h MessageID
  71. var b [8]byte
  72. b[0] = byte(g >> 56)
  73. b[1] = byte(g >> 48)
  74. b[2] = byte(g >> 40)
  75. b[3] = byte(g >> 32)
  76. b[4] = byte(g >> 24)
  77. b[5] = byte(g >> 16)
  78. b[6] = byte(g >> 8)
  79. b[7] = byte(g)
  80. hex.Encode(h[:], b[:])
  81. return h
  82. }