Skip to content

Instantly share code, notes, and snippets.

@cstorey
Created April 12, 2013 10:39
Show Gist options
  • Save cstorey/5371165 to your computer and use it in GitHub Desktop.
Save cstorey/5371165 to your computer and use it in GitHub Desktop.
Playing around with id generation inspired by gosnow. And by "inspired by" I mean "a blatant copy of".
require 'zlib'
require 'socket'
require 'thread'
WORKERID_BITS = 10
MAX_WORKER_ID = -1 ^ (-1 << WORKERID_BITS)
SEQUENCE_BITS = 12
MAX_SEQUENCE = -1 ^ (-1 << SEQUENCE_BITS)
SINCE_MILLIS = Time.utc(2012, 1, 1).to_f * 1000
class SnowFlake
def initialize worker_id = self.default_worker_id
fail "Invalid worker id" if (worker_id < 0 || worker_id > MAX_WORKER_ID)
self.worker_id = worker_id
self.last_timestamp = 0
self.sequence = 0
self.lock = Mutex.new
end
def next
ts = timestamp
if ts == last_timestamp
self.sequence = (sequence+1) & MAX_SEQUENCE
if sequence == 0
ts = til_next_millis(ts)
end
else
self.sequence = 0
end
fail "Invalid timestamp, #{ts} preceeds #{self}" if ts < last_timestamp
self.last_timestamp = ts
as_int64
end
def as_int64
(last_timestamp << (WORKERID_BITS + SEQUENCE_BITS)) |
(worker_id << SEQUENCE_BITS) |
sequence
end
def timestamp
(Time.now.to_f * 1000).to_i
end
def til_next_millis ts
i = timestamp
while i < ts
sleep 0.0001
i = timestamp
end
i
end
def default_worker_id
id = Zlib.crc32 [Socket.gethostname, Process.pid.to_s].join(":")
MAX_WORKER_ID & id
end
attr_accessor :last_timestamp, :worker_id, :sequence, :lock
end
sf = SnowFlake.new
1000_000.times {
val = sf.next
c = [val >> 32, val & 0xFFFFFFFF].pack("NN")
b = [val].pack("w")
puts [c].pack("m").delete('='), [b].pack("m").delete('=')
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment