Packages

CP key ownership for Elixir clusters: at most one node owns a given key, in every failure mode.

Current section

Files

Jump to
fief lib fief authority postgres migrations.ex
Raw

lib/fief/authority/postgres/migrations.ex

# Compiled only when the user has ecto_sql (Oban-style): fief itself has no
# hard dependency on Ecto — see the optional deps note in mix.exs.
if Code.ensure_loaded?(Ecto.Migration) do
defmodule Fief.Authority.Postgres.Migrations do
@moduledoc """
Table definitions for `Fief.Authority.Postgres` (implementation.md §3.1):
five tables, every one keyed by namespace — the instance name, stringified.
One set of migrations covers all instances sharing the database; adding an
instance is rows, never DDL.
Lives under the adapter it provisions — it exists solely to set up
`Fief.Authority.Postgres`'s tables.
Run from your own Ecto migration:
defmodule MyApp.Repo.Migrations.AddFief do
use Ecto.Migration
def up, do: Fief.Authority.Postgres.Migrations.up()
def down, do: Fief.Authority.Postgres.Migrations.down()
end
The DDL is idempotent (`IF NOT EXISTS`), so re-running `up/0` is safe.
"""
use Ecto.Migration
@tables ~w(fief_leases fief_members fief_table fief_meta fief_leader)
@doc "Create the five fief tables."
def up do
execute("""
CREATE TABLE IF NOT EXISTS fief_leases (
ns text NOT NULL,
node text NOT NULL,
expires_at timestamptz NOT NULL,
granted_at timestamptz NOT NULL,
PRIMARY KEY (ns, node)
)
""")
execute("""
CREATE TABLE IF NOT EXISTS fief_members (
ns text NOT NULL,
node text NOT NULL,
status text NOT NULL,
joined_at timestamptz NOT NULL,
PRIMARY KEY (ns, node)
)
""")
# prev_owner NULL = settled; epoch = the namespace epoch of the last
# successful CAS on this row (the M4 row-epoch decision).
execute("""
CREATE TABLE IF NOT EXISTS fief_table (
ns text NOT NULL,
vnode integer NOT NULL,
owner text NOT NULL,
prev_owner text,
epoch bigint NOT NULL,
PRIMARY KEY (ns, vnode)
)
""")
# The per-namespace epoch/term domain plus the immutable first-writer-wins
# config: `config` carries the full config term (ETF); partitions and
# impl_fingerprint are broken out for operator visibility.
execute("""
CREATE TABLE IF NOT EXISTS fief_meta (
ns text PRIMARY KEY,
epoch bigint NOT NULL DEFAULT 0,
max_term bigint NOT NULL DEFAULT 0,
partitions integer,
impl_fingerprint text,
config bytea
)
""")
execute("""
CREATE TABLE IF NOT EXISTS fief_leader (
ns text PRIMARY KEY,
holder text NOT NULL,
term bigint NOT NULL,
expires_at timestamptz NOT NULL
)
""")
end
@doc "Drop the five fief tables."
def down do
for table <- @tables, do: execute("DROP TABLE IF EXISTS #{table}")
end
end
end