Created
September 19, 2023 19:15
-
-
Save victorhazbun/41209fc114551f31eeb4d17e429d947a to your computer and use it in GitHub Desktop.
URL shortener
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class IntToBase | |
def self.convert(int, base) | |
digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
result = "" | |
while int > 0 | |
result += digits[int % base] | |
int /= base | |
end | |
result.reverse | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'snow_flake' | |
require 'int_to_base' | |
service_epoch = Time.new(2023, 9, 19, 0, 0, 0).strftime('%s%L').to_i | |
datacenter_id = 3 | |
node_id = 2 | |
snow_flake = SnowFlake.new(service_epoch, datacenter_id, node_id) | |
next_id = snow_flake.next_id | |
base62 = IntToBase.convert(next_id, 62) | |
pp "Next ID: #{next_id}" | |
pp "Parsed ID: #{snow_flake.parse(next_id)}" | |
pp "Base62 ID: #{base62}" | |
pp "Short URL: https://short.com/#{base62}" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Pure ruby independent ID generator like the Snowflake, ChiliFlake. | |
# @see https://github.com/twitter/snowflake | |
# @see https://github.com/ma2shita/chiliflake | |
class SnowFlake | |
TIMESTAMP_BITS = 41 | |
NODE_ID_BITS = 5 | |
DATACENTER_ID_BITS = 5 | |
SEQUENCE_BITS = 12 | |
MAX_NODE_ID = (1 << NODE_ID_BITS) # 1024 | |
MAX_DATACENTER_ID = (1 << DATACENTER_ID_BITS) # 1024 | |
MAX_SEQUENCE = (1 << SEQUENCE_BITS) # 4096 | |
def initialize(target_epoch, datacenter_id, node_id, sequence = 0) | |
raise OverflowError, "invalid node_id (#{node_id} >= #{MAX_NODE_ID})" if node_id >= MAX_NODE_ID | |
raise OverflowError, "invalid datacenter_id (#{datacenter_id} >= #{MAX_DATACENTER_ID})" if datacenter_id >= MAX_DATACENTER_ID | |
raise OverflowError, "invalid sequence (#{sequence} >= #{MAX_SEQUENCE})" if sequence >= MAX_SEQUENCE | |
@target_epoch = target_epoch | |
@node_id = node_id % MAX_NODE_ID | |
@datacenter_id = datacenter_id % MAX_DATACENTER_ID | |
@sequence = sequence % MAX_SEQUENCE | |
@last_time = current_time | |
end | |
def next_id | |
time = current_time | |
raise InvalidSystemClockError, "(#{time} < #{@last_time})" if time < @last_time | |
if time == @last_time | |
@sequence = (@sequence + 1) % MAX_SEQUENCE | |
# NOTE: Distributed in node_id so if more than MAX_SEQUENCE, or wait until the next time. | |
# time = till_next_time if @sequence == 0 | |
else | |
@sequence = 0 | |
end | |
@last_time = time | |
compose(@last_time, @datacenter_id, @node_id, @sequence) | |
end | |
def parse(flake_id) | |
SnowFlake.parse(flake_id, @target_epoch) | |
end | |
def self.parse(flake_id, target_epoch) | |
hash = {} | |
hash[:epoch_time] = flake_id >> (SEQUENCE_BITS + NODE_ID_BITS + DATACENTER_ID_BITS) | |
hash[:time] = Time.at((hash[:epoch_time] + target_epoch) / 1000.0) | |
hash[:datacenter_id] = (flake_id >> (SEQUENCE_BITS + DATACENTER_ID_BITS)).to_s(2)[-DATACENTER_ID_BITS, DATACENTER_ID_BITS].to_i(2) | |
hash[:node_id] = (flake_id >> SEQUENCE_BITS).to_s(2)[-NODE_ID_BITS, NODE_ID_BITS].to_i(2) | |
hash[:sequence] = flake_id.to_s(2)[-SEQUENCE_BITS, SEQUENCE_BITS].to_i(2) | |
hash | |
end | |
private | |
def compose(last_time, datacenter_id, node_id, sequence) | |
((last_time - @target_epoch) << (SEQUENCE_BITS + NODE_ID_BITS + DATACENTER_ID_BITS)) + | |
(datacenter_id << (SEQUENCE_BITS + DATACENTER_ID_BITS)) + | |
(node_id << SEQUENCE_BITS) + | |
sequence | |
end | |
def current_time | |
Time.now.strftime('%s%L').to_i | |
end | |
end | |
class SnowFlake::OverflowError < StandardError; end | |
class SnowFlake::InvalidSystemClockError < StandardError; end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment