sonyflake.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // Package sonyflake implements Sonyflake, a distributed unique ID generator inspired by Twitter's Snowflake.
  2. //
  3. // A Sonyflake ID is composed of
  4. // 39 bits for time in units of 10 msec
  5. // 8 bits for a sequence number
  6. // 16 bits for a machine id
  7. package sonyflake
  8. import (
  9. "errors"
  10. "net"
  11. "sync"
  12. "time"
  13. )
  14. // These constants are the bit lengths of Sonyflake ID parts.
  15. const (
  16. BitLenTime = 39 // bit length of time
  17. BitLenSequence = 8 // bit length of sequence number
  18. BitLenMachineID = 63 - BitLenTime - BitLenSequence // bit length of machine id
  19. )
  20. // Settings configures Sonyflake:
  21. //
  22. // StartTime is the time since which the Sonyflake time is defined as the elapsed time.
  23. // If StartTime is 0, the start time of the Sonyflake is set to "2014-09-01 00:00:00 +0000 UTC".
  24. // If StartTime is ahead of the current time, Sonyflake is not created.
  25. //
  26. // MachineID returns the unique ID of the Sonyflake instance.
  27. // If MachineID returns an error, Sonyflake is not created.
  28. // If MachineID is nil, default MachineID is used.
  29. // Default MachineID returns the lower 16 bits of the private IP address.
  30. //
  31. // CheckMachineID validates the uniqueness of the machine ID.
  32. // If CheckMachineID returns false, Sonyflake is not created.
  33. // If CheckMachineID is nil, no validation is done.
  34. type Settings struct {
  35. StartTime time.Time
  36. MachineID func() (uint16, error)
  37. CheckMachineID func(uint16) bool
  38. }
  39. // Sonyflake is a distributed unique ID generator.
  40. type Sonyflake struct {
  41. mutex *sync.Mutex
  42. startTime int64
  43. elapsedTime int64
  44. sequence uint16
  45. machineID uint16
  46. }
  47. // NewSonyflake returns a new Sonyflake configured with the given Settings.
  48. // NewSonyflake returns nil in the following cases:
  49. // - Settings.StartTime is ahead of the current time.
  50. // - Settings.MachineID returns an error.
  51. // - Settings.CheckMachineID returns false.
  52. func NewSonyflake(st Settings) *Sonyflake {
  53. sf := new(Sonyflake)
  54. sf.mutex = new(sync.Mutex)
  55. sf.sequence = uint16(1<<BitLenSequence - 1)
  56. if st.StartTime.After(time.Now()) {
  57. return nil
  58. }
  59. if st.StartTime.IsZero() {
  60. sf.startTime = toSonyflakeTime(time.Date(2014, 9, 1, 0, 0, 0, 0, time.UTC))
  61. } else {
  62. sf.startTime = toSonyflakeTime(st.StartTime)
  63. }
  64. var err error
  65. if st.MachineID == nil {
  66. sf.machineID, err = lower16BitPrivateIP()
  67. } else {
  68. sf.machineID, err = st.MachineID()
  69. }
  70. if err != nil || (st.CheckMachineID != nil && !st.CheckMachineID(sf.machineID)) {
  71. return nil
  72. }
  73. return sf
  74. }
  75. // NextID generates a next unique ID.
  76. // After the Sonyflake time overflows, NextID returns an error.
  77. func (sf *Sonyflake) NextID() (uint64, error) {
  78. const maskSequence = uint16(1<<BitLenSequence - 1)
  79. sf.mutex.Lock()
  80. defer sf.mutex.Unlock()
  81. current := currentElapsedTime(sf.startTime)
  82. if sf.elapsedTime < current {
  83. sf.elapsedTime = current
  84. sf.sequence = 0
  85. } else { // sf.elapsedTime >= current
  86. sf.sequence = (sf.sequence + 1) & maskSequence
  87. if sf.sequence == 0 {
  88. sf.elapsedTime++
  89. overtime := sf.elapsedTime - current
  90. time.Sleep(sleepTime((overtime)))
  91. }
  92. }
  93. return sf.toID()
  94. }
  95. const sonyflakeTimeUnit = 1e7 // nsec, i.e. 10 msec
  96. func toSonyflakeTime(t time.Time) int64 {
  97. return t.UTC().UnixNano() / sonyflakeTimeUnit
  98. }
  99. func currentElapsedTime(startTime int64) int64 {
  100. return toSonyflakeTime(time.Now()) - startTime
  101. }
  102. func sleepTime(overtime int64) time.Duration {
  103. return time.Duration(overtime)*10*time.Millisecond -
  104. time.Duration(time.Now().UTC().UnixNano()%sonyflakeTimeUnit)*time.Nanosecond
  105. }
  106. func (sf *Sonyflake) toID() (uint64, error) {
  107. if sf.elapsedTime >= 1<<BitLenTime {
  108. return 0, errors.New("over the time limit")
  109. }
  110. return uint64(sf.elapsedTime)<<(BitLenSequence+BitLenMachineID) |
  111. uint64(sf.sequence)<<BitLenMachineID |
  112. uint64(sf.machineID), nil
  113. }
  114. func privateIPv4() (net.IP, error) {
  115. as, err := net.InterfaceAddrs()
  116. if err != nil {
  117. return nil, err
  118. }
  119. for _, a := range as {
  120. ipnet, ok := a.(*net.IPNet)
  121. if !ok || ipnet.IP.IsLoopback() {
  122. continue
  123. }
  124. ip := ipnet.IP.To4()
  125. if isPrivateIPv4(ip) {
  126. return ip, nil
  127. }
  128. }
  129. return nil, errors.New("no private ip address")
  130. }
  131. func isPrivateIPv4(ip net.IP) bool {
  132. return ip != nil &&
  133. (ip[0] == 10 || ip[0] == 172 && (ip[1] >= 16 && ip[1] < 32) || ip[0] == 192 && ip[1] == 168)
  134. }
  135. func lower16BitPrivateIP() (uint16, error) {
  136. ip, err := privateIPv4()
  137. if err != nil {
  138. return 0, err
  139. }
  140. return uint16(ip[2])<<8 + uint16(ip[3]), nil
  141. }
  142. // Decompose returns a set of Sonyflake ID parts.
  143. func Decompose(id uint64) map[string]uint64 {
  144. const maskSequence = uint64((1<<BitLenSequence - 1) << BitLenMachineID)
  145. const maskMachineID = uint64(1<<BitLenMachineID - 1)
  146. msb := id >> 63
  147. time := id >> (BitLenSequence + BitLenMachineID)
  148. sequence := id & maskSequence >> BitLenMachineID
  149. machineID := id & maskMachineID
  150. return map[string]uint64{
  151. "id": id,
  152. "msb": msb,
  153. "time": time,
  154. "sequence": sequence,
  155. "machine-id": machineID,
  156. }
  157. }