// Package sonyflake implements Sonyflake, a distributed unique ID generator inspired by Twitter's Snowflake. // // A Sonyflake ID is composed of // // 39 bits for time in units of 10 msec // 8 bits for a sequence number // 16 bits for a machine id package sonyflake import ( "errors" "net" "sync" "time" "github.com/sony/sonyflake/types" ) // These constants are the bit lengths of Sonyflake ID parts. const ( BitLenTime = 39 // bit length of time BitLenSequence = 8 // bit length of sequence number BitLenMachineID = 63 - BitLenTime - BitLenSequence // bit length of machine id ) // Settings configures Sonyflake: // // StartTime is the time since which the Sonyflake time is defined as the elapsed time. // If StartTime is 0, the start time of the Sonyflake is set to "2014-09-01 00:00:00 +0000 UTC". // If StartTime is ahead of the current time, Sonyflake is not created. // // MachineID returns the unique ID of the Sonyflake instance. // If MachineID returns an error, Sonyflake is not created. // If MachineID is nil, default MachineID is used. // Default MachineID returns the lower 16 bits of the private IP address. // // CheckMachineID validates the uniqueness of the machine ID. // If CheckMachineID returns false, Sonyflake is not created. // If CheckMachineID is nil, no validation is done. type Settings struct { StartTime time.Time MachineID func() (uint16, error) CheckMachineID func(uint16) bool } // Sonyflake is a distributed unique ID generator. type Sonyflake struct { mutex *sync.Mutex startTime int64 elapsedTime int64 sequence uint16 machineID uint16 } var ( ErrStartTimeAhead = errors.New("start time is ahead of now") ErrNoPrivateAddress = errors.New("no private ip address") ErrOverTimeLimit = errors.New("over the time limit") ErrInvalidMachineID = errors.New("invalid machine id") ) var defaultInterfaceAddrs = net.InterfaceAddrs // New returns a new Sonyflake configured with the given Settings. // New returns an error in the following cases: // - Settings.StartTime is ahead of the current time. // - Settings.MachineID returns an error. // - Settings.CheckMachineID returns false. func New(st Settings) (*Sonyflake, error) { if st.StartTime.After(time.Now()) { return nil, ErrStartTimeAhead } sf := new(Sonyflake) sf.mutex = new(sync.Mutex) sf.sequence = uint16(1<= current sf.sequence = (sf.sequence + 1) & maskSequence if sf.sequence == 0 { sf.elapsedTime++ overtime := sf.elapsedTime - current time.Sleep(sleepTime((overtime))) } } return sf.toID() } const sonyflakeTimeUnit = 1e7 // nsec, i.e. 10 msec func toSonyflakeTime(t time.Time) int64 { return t.UTC().UnixNano() / sonyflakeTimeUnit } func currentElapsedTime(startTime int64) int64 { return toSonyflakeTime(time.Now()) - startTime } func sleepTime(overtime int64) time.Duration { return time.Duration(overtime*sonyflakeTimeUnit) - time.Duration(time.Now().UTC().UnixNano()%sonyflakeTimeUnit) } func (sf *Sonyflake) toID() (uint64, error) { if sf.elapsedTime >= 1<= 16 && ip[1] < 32) || ip[0] == 192 && ip[1] == 168) } func lower16BitPrivateIP(interfaceAddrs types.InterfaceAddrs) (uint16, error) { ip, err := privateIPv4(interfaceAddrs) if err != nil { return 0, err } return uint16(ip[2])<<8 + uint16(ip[3]), nil } // ElapsedTime returns the elapsed time when the given Sonyflake ID was generated. func ElapsedTime(id uint64) time.Duration { return time.Duration(elapsedTime(id) * sonyflakeTimeUnit) } func elapsedTime(id uint64) uint64 { return id >> (BitLenSequence + BitLenMachineID) } // SequenceNumber returns the sequence number of a Sonyflake ID. func SequenceNumber(id uint64) uint64 { const maskSequence = uint64((1<> BitLenMachineID } // MachineID returns the machine ID of a Sonyflake ID. func MachineID(id uint64) uint64 { const maskMachineID = uint64(1<> 63 time := elapsedTime(id) sequence := SequenceNumber(id) machineID := MachineID(id) return map[string]uint64{ "id": id, "msb": msb, "time": time, "sequence": sequence, "machine-id": machineID, } }