Packages

Neo4j driver for Elixir, using the fast Bolt protocol

Current section

Files

Jump to
boltx lib bolt_sips routing routing_table.ex
Raw

lib/bolt_sips/routing/routing_table.ex

defmodule Bolt.Sips.Routing.RoutingTable do
@moduledoc ~S"""
representing the routing table elements
There are a couple of ways to get the routing table from the server, for recent Neo4j servers, and with the
latest version of Bolt.Sips, you could use this query:
Bolt.Sips.query!(Bolt.Sips.conn, "call dbms.cluster.routing.getRoutingTable($props)", %{props: %{}})
[
%{
"servers" => [
%{"addresses" => ["localhost:7687"], "role" => "WRITE"},
%{"addresses" => ["localhost:7689", "localhost:7688"], "role" => "READ"},
%{
"addresses" => ["localhost:7688", "localhost:7689", "localhost:7687"],
"role" => "ROUTE"
}
],
"ttl" => 300
}
]
"""
@type t :: %__MODULE__{
roles: %{(:read | :write | :route | :direct) => %{String.t() => non_neg_integer}},
updated_at: non_neg_integer,
ttl: non_neg_integer
}
defstruct roles: %{}, ttl: 300, updated_at: 0
alias Bolt.Sips.Utils
@write "WRITE"
@read "READ"
@route "ROUTE"
@spec parse(map) :: __MODULE__.t() | {:error, String.t()}
def parse(%{"servers" => servers, "ttl" => ttl}) do
with {:ok, roles} <- parse_servers(servers),
{:ok, ttl} <- parse_ttl(ttl) do
%__MODULE__{roles: roles, ttl: ttl, updated_at: Utils.now()}
end
end
def parse(map),
do: {:error, "not a valid routing table: " <> inspect(map)}
@spec parse_servers(list()) :: {:ok, map()}
defp parse_servers(servers) do
parsed_servers =
servers
|> Enum.reduce(%{}, fn %{"addresses" => addresses, "role" => role}, acc ->
with {:ok, atomized_role} <- to_atomic_role(role) do
roles =
addresses
|> Enum.reduce(acc, fn address, acc ->
Map.update(acc, atomized_role, %{address => 0}, &Map.put(&1, address, 0))
end)
roles
else
_ -> acc
end
end)
{:ok, parsed_servers}
end
defp to_atomic_role(role) when role in [@read, @write, @route] do
atomic_role =
case role do
@read -> :read
@write -> :write
@route -> :route
_ -> :direct
end
{:ok, atomic_role}
end
defp to_atomic_role(_), do: {:error, :alien_role}
def parse_ttl(ttl), do: {:ok, ensure_integer(ttl)}
@doc false
def ttl_expired?(updated_at, ttl) do
updated_at + ttl <= Utils.now()
end
defp ensure_integer(ttl) when is_nil(ttl), do: 0
defp ensure_integer(ttl) when is_binary(ttl), do: String.to_integer(ttl)
defp ensure_integer(ttl) when is_integer(ttl), do: ttl
defp ensure_integer(ttl), do: raise(ArgumentError, "invalid ttl: " <> inspect(ttl))
end