module Uniqid::ClassMethods

@params worker_id @params server_id

Constants

BAK_ID_LEN

2 bits reserved, (0-3)

LOCAL_ID_LEN

7 bits for serial number in milliseconds, (0-127)

MAX_BAK_NUM
MAX_LOCAL_NUM
MAX_SERVER_NUM

The maximum number of servers supported, result is 63

MAX_TIMESTAMP
MAX_WORKER_NUM

The maximum number of workers supported, -1:complement0b111111111

SERVER_ID_LEN

6 bits for server ID, (0-63)

TIMESTAMP_LEN

40 bits for time in milliseconds

TIMESTAMP_START

Start timestamp, 2018-01-01 00:00:00

TOTAL_LEN

64 bits

WORKER_ID_LEN

9 bits for worker ID, (0-511)

Public Instance Methods

generate(worker_value, server_value, timestamp = nil) click to toggle source

Generate ID

# File lib/uniqid.rb, line 88
def generate(worker_value, server_value, timestamp = nil)
  server_value = server_value(server_value)
  worker_value = worker_value(worker_value)

  # The reserved position is temporarily random, shifted by 7 bits to the left
  bak_value = (rand(MAX_BAK_NUM) << LOCAL_ID_LEN)

  local_timestamp =
    if timestamp
      (timestamp * 1000).to_i
    else
      (Time.now.to_f * 1000).to_i
    end

  # If the last generation time is the same as the current time, the sequence within milliseconds
  if local_timestamp == @last_timestamp

    # The sequence is self-increasing and only has 7 bits,
    # so it is ANDed with MAX_LOCAL_NUM and removes the high bits
    sequence = (@sequence + 1) & MAX_LOCAL_NUM

    # Check for overflow: whether the sequence exceeds 127 per millisecond,
    # when 127, it is equal to 0 after AND with MAX_LOCAL_NUM
    if sequence.zero?
      # Wait until the next millisecond
      local_timestamp = next_timestamp(@last_timestamp)
    end

  else
    # If it is different from the last generation time, reset the sequence
    # In order to ensure that the mantissa is more random, set a random number in the last digit
    @sequence = rand(1 << LOCAL_ID_LEN)
    sequence = @sequence
  end

  @last_timestamp = local_timestamp

  # Save the difference of timestamp(current timestamp - start timestamp)
  local_timestamp -= TIMESTAMP_START

  (local_timestamp << (TOTAL_LEN - TIMESTAMP_LEN)) | server_value | worker_value | bak_value | sequence
end
get_timestamp(id) click to toggle source

Reverse check timestamp

# File lib/uniqid.rb, line 132
def get_timestamp(id)
  timestamp = (id >> (TOTAL_LEN - TIMESTAMP_LEN)) / 1000.0
  Time.at(timestamp + TIMESTAMP_START / 1000.0)
end
next_timestamp(last_timestamp) click to toggle source

Prevent the generation time is smaller than the previous time(due to issues such as NTP callback), and keep the incremental trend.

# File lib/uniqid.rb, line 56
def next_timestamp(last_timestamp)
  timestamp = (Time.now.to_f * 1000).to_i
  timestamp = (Time.now.to_f * 1000).to_i while timestamp <= last_timestamp
  timestamp
end
server_value(value) click to toggle source
# File lib/uniqid.rb, line 70
def server_value(value)
  left_num = BAK_ID_LEN + LOCAL_ID_LEN

  value = 0 if value.to_i > MAX_SERVER_NUM || value.to_i.negative?

  unless value.present?
    # In development mode, take the random number of the largest supported number
    if Rails.env == 'development'
      rand(MAX_SERVER_NUM) << left_num
    else
      value = 0
    end
  end

  value.to_i << left_num
end
worker_value(value) click to toggle source

@return worker_id 64 bits

# File lib/uniqid.rb, line 63
def worker_value(value)
  # Handling parameter exception
  value = 0 if value.to_i > MAX_WORKER_NUM || value.to_i.negative?

  value.to_i << SERVER_ID_LEN + BAK_ID_LEN + LOCAL_ID_LEN # 15 bits left
end