Starts named instances on boot from your own source of truth.
PushX.Instance instances live in memory only (see PushX.Instance —
"Lifecycle"), so after a node restart your application has to start them
again. Put the loader in your supervision tree after whatever it reads
from (typically your Repo) and give it a function that returns the
instances to start:
# application.ex
children = [
MyApp.Repo,
{PushX.Instance.Loader, instances: &MyApp.Push.tenant_instances/0},
MyAppWeb.Endpoint
]
# MyApp.Push
def tenant_instances do
for tenant <- Tenants.with_push_credentials() do
{:"tenant_#{tenant.id}_apns", :apns,
key_id: tenant.apns_key_id,
team_id: tenant.apns_team_id,
private_key: tenant.apns_private_key,
mode: :prod}
end
endThe loader runs synchronously during boot: start_link/1 calls
load/1, logs the outcome, and returns :ignore (it keeps no process
around), so by the time the next child starts the instances exist. Each
instance is started with PushX.Instance.start/3; one tenant's bad
credentials are logged and skipped rather than stopping the application —
pass on_error: :raise if you would rather fail the boot.
Options
:instances— (required) a list of{name, provider, config}tuples, or a 0-arity function /{module, function, args}that returns one. Called at load time, after earlier children (your Repo) are up.:on_error—:log(default) logs each failure and continues;:raiseraises after attempting all instances, with every failure in the message (the supervisor then fails to start — use this when a missing tenant must stop the deploy).
Later provisioning (a new tenant signs up) is just PushX.Instance.start/3
from your own code; the loader is for boot. To re-run it at any time call
load/1 — instances that are already running are reported as such, not
restarted.
Summary
Types
Functions
Starts every instance returned by :instances and reports what happened.
Instances that are already running count as :already_running (so
re-running the loader is safe); any other PushX.Instance.start/3 error
lands in :failed with its reason and is logged at error level.
@spec start_link(keyword()) :: :ignore
Runs load/1 synchronously and returns :ignore (no process is kept).
Raises if on_error: :raise and any instance failed to start.