Packages
avrora
0.9.0
0.30.2
0.30.1
0.30.0
0.29.2
0.29.1
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.2
0.24.1
0.24.0
0.23.0
0.22.0
0.21.1
0.21.0
0.20.0
0.19.0
0.18.1
0.18.0
0.17.0
0.16.0
0.15.0
0.14.1
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.1
0.9.0
0.8.1
0.8.0
0.7.1
0.7.0
0.6.0
0.5.1
0.5.0
0.4.0
0.3.1
0.3.0
0.2.2
0.2.1
0.2.0
0.1.1-beta
0.1.0-beta
An Elixir library for working with Avro messages conveniently. It supports local schema files and Confluent® schema registry.
Current section
Files
Jump to
Current section
Files
lib/avrora/resolver.ex
defmodule Avrora.Resolver do
@moduledoc """
Resolve schema name or global ID to a schema, keeping cache up to date.
"""
require Logger
alias Avrora.Config
alias Avrora.Schema.Name
@doc """
Resolve schema, trying multiple methods. First tries integer id, then string name.
## Examples
...> {:ok, schema} = Avrora.Resolver.resolve_any([1, "io.confluent.Payment"])
...> schema.full_name
"io.confluent.Payment"
"""
@spec resolve_any(nonempty_list(integer() | String.t())) ::
{:ok, Avrora.Schema.t()} | {:error, term()}
def resolve_any(ids) do
ids = List.wrap(ids)
total = Enum.count(ids)
ids
|> Stream.map(&{&1, resolve(&1)})
|> Stream.with_index(1)
|> Enum.find_value(fn {{id, {status, result}}, index} ->
if status == :error, do: Logger.debug("fail to resolve schema by identifier `#{id}`")
if status == :ok || index == total, do: {status, result}
end)
end
@doc """
Resolve schema by integer ID, then update memory storage.
Stores schema in memory cache with key id.
## Examples
iex> {:ok, schema} = Avrora.Resolver.resolve(1)
iex> schema.full_name
"io.confluent.Payment"
"""
@spec resolve(integer()) :: {:ok, Avrora.Schema.t()} | {:error, term()}
def resolve(id) when is_integer(id) do
with {:ok, nil} <- memory_storage().get(id),
{:ok, avro} <- registry_storage().get(id) do
memory_storage().put(id, avro)
end
end
@doc """
Resolve schema by string name with optional version.
Version can be provided by adding `:` and version number, e.g. `io.confluent.Payment:5`.
If the Schema Registry is configured (`:registry_url`), it will first try
there, then local schemas folder (`:schemas_path`).
Stores schema in memory with key `name` and `name:version` and adds schema to registry if configured.
## Examples
...> {:ok, schema1} = Avrora.Resolver.resolve("io.confluent.Payment")
...> {:ok, schema2} = Avrora.Resolver.resolve("io.confluent.Payment:42")
...> schema1.version
42
...> schema2.version
42
...> schema1.full_name
"io.confluent.Payment"
...> schema.full_name
"io.confluent.Payment"
"""
@spec resolve(String.t()) :: {:ok, Avrora.Schema.t()} | {:error, term()}
def resolve(name) when is_binary(name) do
with {:ok, schema_name} <- Name.parse(name),
{:ok, nil} <- memory_storage().get(name) do
case registry_storage().get(name) do
{:ok, schema} ->
with {:ok, schema} <-
memory_storage().put("#{schema_name.name}:#{schema.version}", schema),
{:ok, schema} <- memory_storage().put(schema_name.name, schema),
{:ok, timestamp} <- memory_storage().expire(schema_name.name, names_ttl()) do
if timestamp == :infinity,
do: Logger.debug("schema `#{schema_name.name}` will be always resolved from memory")
memory_storage().put(schema.id, schema)
end
{:error, :unknown_subject} ->
with {:ok, schema} <- file_storage().get(schema_name.name),
{:ok, schema} <- registry_storage().put(schema_name.name, schema.json),
{:ok, schema} <- memory_storage().put(schema_name.name, schema),
{:ok, timestamp} <- memory_storage().expire(schema_name.name, names_ttl()) do
if timestamp == :infinity,
do: Logger.debug("schema `#{schema_name.name}` will be always resolved from memory")
memory_storage().put(schema.id, schema)
end
{:error, :unconfigured_registry_url} ->
with {:ok, schema} <- file_storage().get(name),
do: memory_storage().put(schema_name.name, schema)
{:error, reason} ->
{:error, reason}
end
end
end
defp file_storage, do: Config.file_storage()
defp memory_storage, do: Config.memory_storage()
defp registry_storage, do: Config.registry_storage()
defp names_ttl, do: Config.names_cache_ttl()
end