Regarding compile time I’m not sure, since Ecto (& the Postgrex adaptor) do some compile time work (and it would add a bunch of complexities…) - but if doing it on application init then I think something like this would work.
defmodule YourApp.Application do
use Application
def start(_type, _args) do
children = [
Db_Main.Repo
# ... other things you're starting
]
opts = [strategy: :one_for_one, name: YourApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
Every module you’re starting here (usually genservers or other supervisors) have an init/2
callback. Erlang when starting supervisors/genservers/etc blocks on the init/2
of each child module being started, knowing that, you can add a gen_server after your repo is started and before other parts of the application are, that would be responsible for querying the DB, retrieving the relevant info and storing it in an ETS table that has public read (default) access. To ensure that the remaining app would only be loaded after this, you would place this loading inside that genserver’s init.
It would then look like:
children = [
Db_Main.Repo
Supervisor.child_spec({YourApp.ConstraintsLoader, []}, type: :worker)
# ... other
]
So something like this should work, but you should handle possible failures, etc…
def YourApp.ConstraintsLoader do
use GenServer
require Logger
def start_link(_) do
Logger.info("Starting Constraints Loader")
GenServer.start_link(__MODULE__, nil, name: :constraints_loader)
end
def init(nil) do
# if you're just going to read from the table subsequently and from many processes/requests perhaps `read_concurrency: true`, can be given to the ets options as well
:ets.new(:constraints_table, [:named_table])
YourRepo.query("SELECT conname FROM pg_catalog.pg_constraint c WHERE c.confrelid = '#{my_table}'::regclass AND c.contype = 'f'")
|> Enum.each(fn(result_row) ->
# figure out what you need to insert and how you'll identify it, the first key on the tuple is the identifier and it can be any term
:ets.insert(:constraints_table, {"unique_identifier_element", result_row.column_1, result_row.column_2})
end)
{:ok, :initialized}
end
end
After this you should be able to query the :ets from any part of your app, with something like:
case :ets.lookup(:constraints_table, "unique_identifier_element") do
[] -> # no element with that identifier was in the table
[{identifier, column1, column2}] -> # an element with that key was found
end
You can also retrieve the full table as a list with :ets.tab2list(:constraints_table)