Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- defmodule GlobalId do
- @moduledoc """
- GlobalId module contains an implementation of a guaranteed globally unique id system.
- """
- @node_count 1024
- @node_id rem(System.unique_integer([:positive]), @node_count)
- @counter_coefficient 100_000
- @node_id_coefficient 10_000
- @timestamp_divisor @counter_coefficient * @node_id_coefficient
- @doc """
- Please implement the following function.
- 64 bit non negative integer output
- """
- # for testing, make sure the node id matches in all cases - counter never goes over 100000
- # make sure no two ids are the same
- @spec get_id(non_neg_integer) :: non_neg_integer
- def get_id(last_id) do
- current_time = timestamp_in_seconds()
- counter = parse_counter(last_id, current_time)
- timestamp_and_node_id = (current_time * @node_id_coefficient) + node_id()
- (timestamp_and_node_id * @counter_coefficient) + counter
- end
- defp parse_counter(last_id, current_time) do
- if parse_timestamp(last_id) == current_time do
- rem(last_id, @counter_coefficient) + 1
- else
- 0
- end
- end
- defp parse_timestamp(last_id), do: div(last_id, @timestamp_divisor)
- defp timestamp_in_seconds, do: div(timestamp(), 1000)
- #
- # You are given the following helper functions
- # Presume they are implemented - there is no need to implement them.
- #
- @doc """
- Returns your node id as an integer.
- It will be greater than or equal to 0 and less than or equal to 1024.
- It is guaranteed to be globally unique.
- """
- @spec node_id() :: non_neg_integer
- def node_id, do: @node_id
- @doc """
- Returns timestamp since the epoch in milliseconds.
- """
- @spec timestamp() :: non_neg_integer
- def timestamp, do: DateTime.utc_now() |> DateTime.to_unix(:millisecond)
- end
- defmodule GlobalIdTest do
- @test_runs 100_000
- # using all 64 bits assuming the integer is unsigned
- @max_id :math.pow(2,64) |> round
- def run_tests do
- ids = generate_ids()
- verify_node(ids)
- verify_unique(ids)
- verify_bit_size(ids)
- :ok
- end
- defp generate_ids do
- test_count = 0
- seed_id = 1
- ids_acc = []
- generate_id(@test_runs, test_count, seed_id, ids_acc)
- end
- defp generate_id(number_of_ids, test_count, _previous_id, ids_acc) when number_of_ids == test_count do
- ids_acc
- end
- defp generate_id(number_of_ids, test_count, previous_id, ids_acc) do
- new_id = GlobalId.get_id(previous_id)
- generate_id(number_of_ids, test_count + 1, new_id, [new_id | ids_acc])
- end
- defp verify_node(ids) do
- node_ids = Enum.map(ids, &get_node_id(&1))
- if length(Enum.uniq(node_ids)) != 1 do
- raise "Test failure: all global IDs must contain the same node ID"
- end
- end
- defp get_node_id(id) do
- timestamp_and_node_id = div(id, 100_000)
- rem(timestamp_and_node_id, 10_000)
- end
- defp verify_unique(ids) do
- if ids != Enum.uniq(ids) do
- raise "Test failure: Each ID should be unique"
- end
- end
- defp verify_bit_size(ids) do
- large_ids = Enum.filter(ids, fn(id) -> id >= @max_id end)
- if length(large_ids) != 0 do
- raise "Test failure: ID is larger than allowable size"
- end
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement