Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Elixir package maintenance #25

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
8 changes: 4 additions & 4 deletions .github/workflows/pr-ex-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
elixir-version: "1.16.1"
elixir-version: "1.17.1"
otp-version: "26"
- name: Run credo
run: |
Expand All @@ -27,11 +27,11 @@ jobs:

test:
runs-on: ubuntu-latest
name: OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}}
name: Elixir ${{matrix.elixir}} (OTP ${{matrix.otp}})
strategy:
matrix:
otp: ["25"]
elixir: ["1.16.1", "1.15.7", "1.14.0"]
otp: ["25", "26"]
elixir: ["1.17.1", 1.16.1", "1.15.7"]
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
Expand Down
2 changes: 1 addition & 1 deletion impl/ex/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The package can be installed by adding `pika` to your list of dependencies in `m
```elixir
def deps do
[
{:pika, "~> 0.1"}
{:pika, "~> 0.3"}
]
end
```
Expand Down
16 changes: 8 additions & 8 deletions impl/ex/lib/pika.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ defmodule Pika do

@doc false
defp _gen(prefix, snowflake, true) do
bytes = :rand.bytes(16)
bytes = :crypto.strong_rand_bytes(16)

tail =
"_s_#{Base.encode32(bytes, padding: false, case: :lower)}_#{snowflake}"
Expand Down Expand Up @@ -80,14 +80,14 @@ defmodule Pika do
"""
def deconstruct(id) do
prefixes = Application.get_env(:pika, :prefixes)

fragments = id |> String.split("_")
[prefix, tail] = fragments

[prefix, tail] = id |> String.split("_")
[prefix_record] = Enum.filter(prefixes, fn m -> m.prefix == prefix end)
decoded_tail = Base.decode64!(tail, padding: false)
tail_fragments = decoded_tail |> String.split("_")
snowflake = tail_fragments |> Enum.at(length(tail_fragments) - 1) |> String.to_integer()

snowflake =
Base.decode64!(tail, padding: false)
|> String.split("_")
|> List.last()
|> String.to_integer()

decoded_snowflake = Snowflake.decode(snowflake)

Expand Down
87 changes: 50 additions & 37 deletions impl/ex/lib/snowflake.ex
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
defmodule Pika.DecodedSnowflake do
@moduledoc """
Represents a decoded `Snowflake`
"""
defstruct [:timestamp, :node_id, :seq, :epoch]
end

defmodule Pika.Snowflake do
import Bitwise
alias Pika.Utils
Expand Down Expand Up @@ -40,18 +47,47 @@ defmodule Pika.Snowflake do
def start_link([]), do: start_link()

def start_link(epoch) when is_integer(epoch) do
GenServer.start_link(__MODULE__, {Utils.compute_node_id(), epoch, 0, 0}, name: __MODULE__)
GenServer.start_link(__MODULE__, epoch, name: __MODULE__)
end

def start_link do
# State: {node_id, epoch, seq, last_sequence_exhaustion}
GenServer.start_link(__MODULE__, {Utils.compute_node_id(), 1_640_995_200_000, 0, 0},
name: __MODULE__
)
GenServer.start_link(__MODULE__, nil, name: __MODULE__)
end

def init(state) do
{:ok, state}
def init(epoch) do
{:ok, generate_initial_state(epoch)}
end

def handle_call(
{:decode, snowflake},
_from,
state = {_node_id, epoch, _seq, _last_seq_exhaustion}
) do
timestamp = (snowflake >>> 22) + epoch
node_id = snowflake >>> 12 &&& 0b11_1111_1111
seq = snowflake &&& 0b1111_1111_1111

{:reply,
%Pika.DecodedSnowflake{timestamp: timestamp, epoch: epoch, node_id: node_id, seq: seq},
state}
end

def handle_call({:generate, timestamp}, _from, {node_id, epoch, seq, last_seq_exhaustion}) do
if seq >= 4095 and timestamp == last_seq_exhaustion do
block(timestamp)
end

snowflake = (timestamp - epoch) <<< 22 ||| node_id <<< 12 ||| seq

seq =
if seq >= 4095 do
0
else
seq + 1
end

{:reply, snowflake,
{node_id, epoch, seq, maybe_sequence_exhausted(seq, timestamp, last_seq_exhaustion)}}
end

@doc """
Expand Down Expand Up @@ -83,38 +119,15 @@ defmodule Pika.Snowflake do
GenServer.call(__MODULE__, {:decode, snowflake})
end

def handle_call(
{:decode, snowflake},
_from,
state = {_node_id, epoch, _seq, _last_seq_exhaustion}
) do
timestamp = (snowflake >>> 22) + epoch
node_id = snowflake >>> 12 &&& 0b11_1111_1111
seq = snowflake &&& 0b1111_1111_1111

{:reply, %{timestamp: timestamp, epoch: epoch, node_id: node_id, seq: seq}, state}
end

def handle_call({:generate, timestamp}, _from, {node_id, epoch, seq, last_seq_exhaustion}) do
if seq >= 4095 and timestamp == last_seq_exhaustion do
block(timestamp)
end

snowflake = (timestamp - epoch) <<< 22 ||| node_id <<< 12 ||| seq
@doc false
defp maybe_sequence_exhausted(4095, timestamp, _last_seq_exhaustion), do: timestamp

seq =
if seq >= 4095 do
0
else
seq + 1
end
defp maybe_sequence_exhausted(_sequence, _timestamp, last_seq_exhaustion),
do: last_seq_exhaustion

if timestamp === last_seq_exhaustion do
{:reply, snowflake, {node_id, epoch, seq, timestamp}}
else
{:reply, snowflake, {node_id, epoch, seq, now_ts()}}
end
end
@doc false
defp generate_initial_state(nil), do: generate_initial_state(1_640_995_200_000)
defp generate_initial_state(epoch), do: {Utils.compute_node_id(), epoch, 0, 0}

@doc false
defp block(timestamp) do
Expand Down
15 changes: 8 additions & 7 deletions impl/ex/lib/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ defmodule Pika.Utils do
def get_mac_address do
{:ok, addresses} = :inet.getifaddrs()

{_if_name, if_mac} = Enum.reduce(addresses, [], fn ({if_name, if_data}, acc) ->
case Keyword.get(if_data, :hwaddr) |> validate_address do
{:ok, address} -> [{to_string(if_name), address} | acc]
_ -> acc
end
end)
|> List.first()
{_if_name, if_mac} =
Enum.reduce(addresses, [], fn {if_name, if_data}, acc ->
case Keyword.get(if_data, :hwaddr) |> validate_address do
{:ok, address} -> [{to_string(if_name), address} | acc]
_ -> acc
end
end)
|> List.first()

if_mac
|> Enum.map_join(":", fn i -> Integer.to_string(i, 16) |> String.pad_leading(2, "0") end)
Expand Down
2 changes: 1 addition & 1 deletion impl/ex/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule Pika.MixProject do
def project do
[
app: :pika,
version: "0.1.2",
version: "0.3.0",
elixir: "~> 1.14",
start_permanent: Mix.env() == :prod,
package: package(),
Expand Down
16 changes: 8 additions & 8 deletions impl/ex/mix.lock
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
%{
"benchee": {:hex, :benchee, "1.3.0", "f64e3b64ad3563fa9838146ddefb2d2f94cf5b473bdfd63f5ca4d0657bf96694", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "34f4294068c11b2bd2ebf2c59aac9c7da26ffa0068afdf3419f1b176e16c5f81"},
"benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"credo": {:hex, :credo, "1.7.4", "68ca5cf89071511c12fd9919eb84e388d231121988f6932756596195ccf7fd35", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9cf776d062c78bbe0f0de1ecaee183f18f2c3ec591326107989b054b7dddefc2"},
"credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"ex_doc": {:hex, :ex_doc, "0.31.1", "8a2355ac42b1cc7b2379da9e40243f2670143721dd50748bf6c3b1184dae2089", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3178c3a407c557d8343479e1ff117a96fd31bafe52a039079593fb0524ef61b0"},
"earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"},
"ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"},
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.4", "29563475afa9b8a2add1b7a9c8fb68d06ca7737648f28398e04461f008b69521", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f4ed47ecda66de70dd817698a703f8816daa91272e7e45812469498614ae8b29"},
"jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"},
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
}