Improving incremental compilation times

Hi there! I need to know what the best practices are when working with Ash resources and related files (calculations, preparations, etc), specifically around avoiding unnecessary dependencies and keeping compilation times low. Things to do, and what not to do. I originally posted about this on the Elixir Forum (https://elixirforum.com/t/reducing-incremental-compilation-times-in-phoenix-ash-project/72113). The feedback there helped me better analyze my codebase and realize that I may be doing something wrong within Ash resources — and related files — myself, which has increased recompilation times (up to 10s). My gut is telling me that what I'm doing here might be wrong:
# invitation.ex
# References several modules, which results in many dependencies
change after_action(fn _changeset, invitation, _context ->
invitation
|> Ash.load!([establishment: [:organization]], authorize?: false)
|> MyApp.Emails.InvitationEmail.new()
|> MyApp.Mailer.serialize()
|> then(fn serialized_email -> %{"email" => serialized_email} end)
|> MyApp.Workers.MailerWorker.new()
|> Oban.insert!()

{:ok, invitation}
end)
# invitation.ex
# References several modules, which results in many dependencies
change after_action(fn _changeset, invitation, _context ->
invitation
|> Ash.load!([establishment: [:organization]], authorize?: false)
|> MyApp.Emails.InvitationEmail.new()
|> MyApp.Mailer.serialize()
|> then(fn serialized_email -> %{"email" => serialized_email} end)
|> MyApp.Workers.MailerWorker.new()
|> Oban.insert!()

{:ok, invitation}
end)
defmodule MyApp.Establishments.Invitation.Calculations.Url do
use Ash.Resource.Calculation

@impl true
def load(_query, _opts, _context), do: [:id]

@impl true
def calculate(records, _opts, _context) do
Enum.map(records, fn record ->
# Calling this function caused this module (url.ex) to end up with 39 runtime dependencies
LamashkaWeb.Endpoint.url() <> "/accept-invitation/#{record.id}"
end)
end
end
defmodule MyApp.Establishments.Invitation.Calculations.Url do
use Ash.Resource.Calculation

@impl true
def load(_query, _opts, _context), do: [:id]

@impl true
def calculate(records, _opts, _context) do
Enum.map(records, fn record ->
# Calling this function caused this module (url.ex) to end up with 39 runtime dependencies
LamashkaWeb.Endpoint.url() <> "/accept-invitation/#{record.id}"
end)
end
end
I'm pretty new to this and had no idea about runtime/compilation dependencies until now, so before I continue with my project I’d better take some time to understand all of this to prevent it from continuing to happen, as it has already impacted my productivity. Thanks in advance for your help.
Solution:
```elixir defmodule Your.Resource.Changes.SendInvitationEmail do use Ash.Resource.Change def change(changeset, , ) ->...
Jump to solution
3 Replies
ZachDaniel
ZachDaniel2mo ago
Try putting that function in a change module instead of in-line
change __MODULE__.Changes.SendInvitationEmail
change __MODULE__.Changes.SendInvitationEmail
Solution
ZachDaniel
ZachDaniel2mo ago
defmodule Your.Resource.Changes.SendInvitationEmail do
use Ash.Resource.Change

def change(changeset, _, _) ->
Ash.Changeset.after_action(changeset, fn _changeset, invitation->
invitation
|> Ash.load!([establishment: [:organization]], authorize?: false)
|> MyApp.Emails.InvitationEmail.new()
|> MyApp.Mailer.serialize()
|> then(fn serialized_email -> %{"email" => serialized_email} end)
|> MyApp.Workers.MailerWorker.new()
|> Oban.insert!()

{:ok, invitation}
end)
end
end
defmodule Your.Resource.Changes.SendInvitationEmail do
use Ash.Resource.Change

def change(changeset, _, _) ->
Ash.Changeset.after_action(changeset, fn _changeset, invitation->
invitation
|> Ash.load!([establishment: [:organization]], authorize?: false)
|> MyApp.Emails.InvitationEmail.new()
|> MyApp.Mailer.serialize()
|> then(fn serialized_email -> %{"email" => serialized_email} end)
|> MyApp.Workers.MailerWorker.new()
|> Oban.insert!()

{:ok, invitation}
end)
end
end
Joan Gavelán
Joan GavelánOP2mo ago
Alright, so the idea is that I shouldn't work directly within Ash resources, but in separate modules. Ash resources still take a while to recompile, but I shouldn't care much since the work I'm actively developing is in a separate module (and these modules compile quickly on every change) Yes, this totally fixed my compilation times. I even had problems making changes to my controllers due to transitive dependencies to the modules I was calling within my Ash resources. Moving my logic to their own change modules totally fixed this. Thanks!

Did you find this page helpful?