Advertisement
Guest User

Untitled

a guest
May 22nd, 2019
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.05 KB | None | 0 0
  1. defmodule GlobalId do
  2. @moduledoc """
  3. GlobalId module contains an implementation of a guaranteed globally unique id system.
  4. """
  5.  
  6. @node_count 1024
  7. @node_id rem(System.unique_integer([:positive]), @node_count)
  8. @counter_coefficient 100_000
  9. @node_id_coefficient 10_000
  10. @timestamp_divisor @counter_coefficient * @node_id_coefficient
  11. @doc """
  12. Please implement the following function.
  13. 64 bit non negative integer output
  14. """
  15.  
  16. # for testing, make sure the node id matches in all cases - counter never goes over 100000
  17. # make sure no two ids are the same
  18. @spec get_id(non_neg_integer) :: non_neg_integer
  19. def get_id(last_id) do
  20. current_time = timestamp_in_seconds()
  21. counter = parse_counter(last_id, current_time)
  22. timestamp_and_node_id = (current_time * @node_id_coefficient) + node_id()
  23. (timestamp_and_node_id * @counter_coefficient) + counter
  24. end
  25.  
  26. defp parse_counter(last_id, current_time) do
  27. if parse_timestamp(last_id) == current_time do
  28. rem(last_id, @counter_coefficient) + 1
  29. else
  30. 0
  31. end
  32. end
  33.  
  34. defp parse_timestamp(last_id), do: div(last_id, @timestamp_divisor)
  35.  
  36. defp timestamp_in_seconds, do: div(timestamp(), 1000)
  37. #
  38. # You are given the following helper functions
  39. # Presume they are implemented - there is no need to implement them.
  40. #
  41.  
  42. @doc """
  43. Returns your node id as an integer.
  44. It will be greater than or equal to 0 and less than or equal to 1024.
  45. It is guaranteed to be globally unique.
  46. """
  47. @spec node_id() :: non_neg_integer
  48. def node_id, do: @node_id
  49.  
  50. @doc """
  51. Returns timestamp since the epoch in milliseconds.
  52. """
  53. @spec timestamp() :: non_neg_integer
  54. def timestamp, do: DateTime.utc_now() |> DateTime.to_unix(:millisecond)
  55. end
  56.  
  57. defmodule GlobalIdTest do
  58. @test_runs 100_000
  59.  
  60. # using all 64 bits assuming the integer is unsigned
  61. @max_id :math.pow(2,64) |> round
  62.  
  63. def run_tests do
  64. ids = generate_ids()
  65. verify_node(ids)
  66. verify_unique(ids)
  67. verify_bit_size(ids)
  68. :ok
  69. end
  70.  
  71. defp generate_ids do
  72. test_count = 0
  73. seed_id = 1
  74. ids_acc = []
  75. generate_id(@test_runs, test_count, seed_id, ids_acc)
  76. end
  77.  
  78. defp generate_id(number_of_ids, test_count, _previous_id, ids_acc) when number_of_ids == test_count do
  79. ids_acc
  80. end
  81.  
  82. defp generate_id(number_of_ids, test_count, previous_id, ids_acc) do
  83. new_id = GlobalId.get_id(previous_id)
  84. generate_id(number_of_ids, test_count + 1, new_id, [new_id | ids_acc])
  85. end
  86.  
  87. defp verify_node(ids) do
  88. node_ids = Enum.map(ids, &get_node_id(&1))
  89. if length(Enum.uniq(node_ids)) != 1 do
  90. raise "Test failure: all global IDs must contain the same node ID"
  91. end
  92. end
  93.  
  94. defp get_node_id(id) do
  95. timestamp_and_node_id = div(id, 100_000)
  96. rem(timestamp_and_node_id, 10_000)
  97. end
  98.  
  99. defp verify_unique(ids) do
  100. if ids != Enum.uniq(ids) do
  101. raise "Test failure: Each ID should be unique"
  102. end
  103. end
  104.  
  105. defp verify_bit_size(ids) do
  106. large_ids = Enum.filter(ids, fn(id) -> id >= @max_id end)
  107. if length(large_ids) != 0 do
  108. raise "Test failure: ID is larger than allowable size"
  109. end
  110. end
  111. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement