Current section
Files
Jump to
Current section
Files
lib/snowflake/generator.ex
defmodule Snowflake.Generator do
@moduledoc false
use GenServer
@machine_id_overflow 1024
@seq_overflow 4096
def start_link(epoch, machine_id) when machine_id < @machine_id_overflow do
state = {epoch, ts(epoch), machine_id, 0}
GenServer.start_link(__MODULE__, state, name: __MODULE__)
end
def handle_call(:next_id, from, {epoch, prev_ts, machine_id, seq} = state) do
case next_ts_and_seq(epoch, prev_ts, seq) do
{:error, :seq_overflow} ->
:timer.sleep(1)
handle_call(:next_id, from, state)
{:error, :backwards_clock} ->
{:reply, {:error, :backwards_clock}, state}
{:ok, new_ts, new_seq} ->
new_state = {epoch, new_ts, machine_id, new_seq}
{:reply, {:ok, create_id(new_ts, machine_id, new_seq)}, new_state}
end
end
defp next_ts_and_seq(epoch, prev_ts, seq) do
case ts(epoch) do
^prev_ts ->
case seq + 1 do
@seq_overflow -> {:error, :seq_overflow}
next_seq -> {:ok, prev_ts, next_seq}
end
new_ts ->
cond do
new_ts < prev_ts -> {:error, :backwards_clock}
true -> {:ok, new_ts, 0}
end
end
end
defp create_id(ts, machine_id, seq) do
<< new_id :: unsigned-integer-size(64)>> = <<
ts :: unsigned-integer-size(42),
machine_id :: unsigned-integer-size(10),
seq :: unsigned-integer-size(12) >>
new_id
end
defp ts(epoch) do
System.os_time(:milliseconds) - epoch
end
end