Packages
localize
0.14.0
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.41.3
0.41.2
0.41.1
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.1
0.30.0
retired
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
0.1.0-alpha.1
Localization (parsing, formatting) of numbers, dates/time/calendar, units of measure, messages and lists. Includes localized collation.
Current section
Files
Jump to
Current section
Files
lib/localize/data_loader.ex
defmodule Localize.DataLoader do
@moduledoc """
A GenServer that serializes ETF data file loading to prevent
race conditions.
When multiple processes request the same data concurrently,
the first request triggers the load and subsequent requests
wait for it to complete. This avoids duplicate file reads and
redundant `:persistent_term.put/2` calls (each of which
triggers a global GC).
The server is started as part of the `Localize` supervision
tree. All ETF file loading goes through `load/2`.
"""
use GenServer
# ── Public API ────────────────────────────────────────────────
@doc """
Starts the data loader GenServer.
"""
def start_link(options \\ []) do
GenServer.start_link(__MODULE__, options, name: __MODULE__)
end
@doc """
Loads an ETF data file, serialized through the GenServer.
If the data is already cached in `:persistent_term`, returns
it immediately without contacting the server. Otherwise,
delegates to the GenServer which ensures only one load per
key occurs.
### Arguments
* `key` is the `:persistent_term` key (e.g.,
`{:localize, :supplemental, "aliases.etf"}`).
* `load_fn` is a zero-arity function that reads and decodes
the file, returning the data to be cached.
### Returns
* The loaded data.
"""
@spec load(term(), (-> term())) :: term()
def load(key, load_fn) do
case :persistent_term.get(key, :not_loaded) do
:not_loaded ->
if server_running?() do
GenServer.call(__MODULE__, {:load, key, load_fn}, :infinity)
else
# Compile time or before app start — load directly
data = load_fn.()
:persistent_term.put(key, data)
data
end
data ->
data
end
end
defp server_running? do
pid = Process.whereis(__MODULE__)
pid && Process.alive?(pid)
end
# ── GenServer callbacks ───────────────────────────────────────
@impl GenServer
def init(_options) do
{:ok, %{}}
end
@impl GenServer
def handle_call({:load, key, load_fn}, _from, state) do
data =
case :persistent_term.get(key, :not_loaded) do
:not_loaded ->
data = load_fn.()
:persistent_term.put(key, data)
data
data ->
data
end
{:reply, data, state}
end
end