Packages

phoenix_kit

2.49.0
2.52.2 2.52.1 2.52.0 2.51.0 2.50.0 2.49.1 2.49.0 2.48.0 2.47.0 2.46.0 2.45.0 2.44.0 2.43.1 2.43.0 2.42.1 2.42.0 2.41.6 2.41.4 2.41.3 2.41.2 2.41.1 2.41.0 2.40.1 2.40.0 2.39.0 2.38.1 2.38.0 2.37.5 2.37.4 2.37.3 2.37.2 2.37.1 2.37.0 2.36.1 2.36.0 2.35.0 2.34.0 2.33.0 2.32.1 2.32.0 2.31.1 2.31.0 2.30.0 2.29.1 2.29.0 2.28.2 2.28.1 2.28.0 2.27.2 2.27.1 2.27.0 2.26.1 2.26.0 2.25.0 2.24.0 2.23.3 2.23.2 2.23.1 2.23.0 2.22.24 2.22.23 2.22.22 2.22.21 2.22.20 2.22.19 2.22.18 2.22.17 2.22.16 2.22.15 2.22.14 2.22.13 2.22.12 2.22.11 2.22.10 2.22.9 2.22.8 2.22.7 2.22.6 2.22.5 2.22.4 2.22.3 2.22.2 2.22.1 2.22.0 2.21.5 2.21.4 2.21.3 2.21.2 2.21.1 2.21.0 2.20.0 2.19.0 2.18.1 2.18.0 2.17.0 2.16.0 2.15.1 2.15.0 2.14.2 2.14.1 2.14.0 2.13.19 2.13.18 2.13.17 2.13.16 2.13.15 2.13.13 2.13.12 2.13.11 2.13.10 2.13.9 2.13.8 2.13.7 2.13.6 2.13.5 2.13.4 2.13.3 2.13.2 2.13.1 2.13.0 2.12.1 2.12.0 2.11.0 2.10.0 2.9.0 2.8.1 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.0 2.0.1 2.0.0 1.7.236 1.7.235 1.7.234 1.7.233 1.7.232 1.7.231 1.7.230 1.7.229 1.7.228 1.7.227 1.7.226 1.7.225 1.7.224 1.7.223 1.7.222 1.7.221 1.7.220 1.7.219 1.7.218 1.7.217 1.7.216 1.7.215 1.7.214 1.7.213 1.7.212 1.7.211 1.7.210 1.7.209 1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib modules storage library_state.ex
Raw

lib/modules/storage/library_state.ex

defmodule PhoenixKit.Modules.Storage.LibraryState do
@moduledoc """
What is going on with each library's storage, derived — never stored — from two
things: how many of its files are out of date
(`PhoenixKit.Modules.Storage.Reconciler.counts_by_library/0`, one grouped query)
and its `storage.reconcile` run
(`PhoenixKit.Modules.Storage.Jobs.Reconcile`, `dev_docs/plans/2026-10-03-job-runs.md`
§6.3, R8).
| state | when |
|---|---|
| `:paused` | its reconcile run is paused (or pausing) |
| `:syncing` | a reconcile run is queued or running — "N files left" |
| `:attention` | no run is active, and either its last run failed with files left, or the reconciler tried files and could not finish them (they wait out their retry window) |
| `:waiting` | files are out of date and **no run** is active: the queue is down, or nothing has triggered yet — the case a global count hides |
| `:up_to_date` | nothing is out of date |
"Up to date" means the files carry the revisions of their library's profile and
variant set. It does **not** prove every recorded object is still on its bucket;
that is what a later `storage.verify` is for (§6.4).
The map returned for a library:
%{state: :syncing, out_of_date: 12, eligible: 12, failing: 0, run: %Run{}}
`run` is the active run when there is one, else the last one, else `nil`.
"""
import Ecto.Query
alias PhoenixKit.Jobs.Run
alias PhoenixKit.Modules.Storage.Reconciler
@kind "storage.reconcile"
@type state :: :up_to_date | :syncing | :paused | :attention | :waiting
@type t :: %{
state: state(),
out_of_date: non_neg_integer(),
eligible: non_neg_integer(),
failing: non_neg_integer(),
run: Run.t() | nil
}
@doc "The kind whose runs this state is about."
@spec kind() :: String.t()
def kind, do: @kind
@doc """
The state of each of `library_uuids`, as a map keyed by the uuid string: three
queries however many libraries (the counts, the active runs, the last runs).
"""
@spec for_libraries([String.t()]) :: %{String.t() => t()}
def for_libraries(library_uuids) do
uuids = Enum.map(library_uuids, &to_string/1)
counts = Reconciler.counts_by_library(cast(uuids))
active = active_runs(uuids)
last = last_runs(uuids)
Map.new(uuids, fn uuid ->
{uuid, build(Map.get(counts, uuid), Map.get(active, uuid), Map.get(last, uuid))}
end)
end
@doc "The state of one library."
@spec for_library(String.t()) :: t()
def for_library(library_uuid) do
uuid = to_string(library_uuid)
Map.fetch!(for_libraries([uuid]), uuid)
end
@doc false
@spec build(map() | nil, Run.t() | nil, Run.t() | nil) :: t()
def build(counts, active, last) do
%{out_of_date: out, eligible: eligible, failing: failing} =
counts || %{out_of_date: 0, eligible: 0, failing: 0}
%{
state: state(out, failing, active, last),
out_of_date: out,
eligible: eligible,
failing: failing,
run: active || last
}
end
defp state(_out, _failing, %Run{state: state}, _last) when state in ~w(paused pausing),
do: :paused
defp state(_out, _failing, %Run{}, _last), do: :syncing
defp state(out, failing, nil, last) do
cond do
failing > 0 -> :attention
out > 0 and match?(%Run{state: "failed"}, last) -> :attention
out > 0 -> :waiting
true -> :up_to_date
end
end
# The unfinished reconcile run of each library (there is at most one: the active
# index allows no more).
defp active_runs(uuids) do
from(r in Run,
where: r.kind == @kind and r.state in ^Run.active_states() and r.scope_type == "library",
where: r.scope_uuid in ^cast(uuids)
)
|> repo().all()
|> Map.new(&{to_string(&1.scope_uuid), &1})
end
# The most recent reconcile run of each library, finished or not.
defp last_runs(uuids) do
from(r in Run,
where: r.kind == @kind and r.scope_type == "library" and r.scope_uuid in ^cast(uuids),
distinct: r.scope_uuid,
order_by: [asc: r.scope_uuid, desc: r.inserted_at, desc: r.uuid]
)
|> repo().all()
|> Map.new(&{to_string(&1.scope_uuid), &1})
end
defp cast(uuids), do: for(uuid <- uuids, {:ok, cast} <- [Ecto.UUID.cast(uuid)], do: cast)
defp repo, do: PhoenixKit.RepoHelper.repo()
end