Hey there, is it normal that whenever I modify a controller, it recompiles my router, all my controllers and my views?
I tried already ran mix xref
but I don’t really know how to investigate further.
Here is my mix xref
output for one of my controllers: https://gist.github.com/cblavier/217681e36c546fe7d44d2f0d245860b5
1 Like
Hmm, one big thing of note is that it looks like the Views/Templates use Router helpers, but directly instead of namespaced. Do you use things like some_path(...)
instead of Routes.some_path(...)
? That would create a hard dependency and force recompilation. Phoenix like 1.3 or so changed from direct accessing to namespaced accessing for that reason, though it does mean you lose compile-time checking on them.
5 Likes
Thank you, it helps! I was able to decrease the count of recompiled files from 208 to 72.
Now, I can decrease it to about 10 files if I disable all custom module plugs I’m using in my controllers. Any idea why?
I tried playing with config :phoenix, :plug_init_mode, :runtime
but it doesn’t change anything? Does it also apply to controller plugs or only router plugs?
I suggest watching this video. It’ll give you an idea of what causes recompilation of modules. :plug_init_mode
is just a way to lessen recompilations for plug, but you might have other causes of compile time dependencies as well.
2 Likes
Already watched it! I think I tracked down all my “compile” dependencies.
But I can’t understand why, when using a same plug from a few controllers, when I touch one of the controllers, all the other controllers get recompiles (even if my plug code is empty!)
I finally solved my problem
Touching a view now recompiles only 1 file, and touching a controller recompiles 2 files (the controller and the endpoint) instead of 208 … (went from 30sec to 2sec)
What did I do?
- watched this video twice
- ran a gazillion times mix
xref graph --source lib/../my_controller.ex
and xref graph --sink lib/../router.ex
- also ran
fswatch _build -r | cat | grep .beam
to actually see what files are being recompiled
- aliased
RouteHelpers
instead of importing them
- removed any hard reference to
LayoutView
or Router
in any of my plugs. I had to workaround this problem by using Module.concat["MyApp.Router.Helpers"]
in my navigation plugs
- had a weird dependency issue related to Coherence library when
coherence_routes
were inlined in my app main scope instead of its own dedicated scope
- the last one still seems weird to me, but I also had to rewrite any
defdelegate
calls from views to other views into a classic function call
Hope it will help someone, and if you have any comment about my post, feel free!
8 Likes
defdelegate
does ensure the function actually exists on the other end, which makes it create a compile time dependency.
3 Likes
Still trying to understand why I didn’t noticed this mess earlier, I figured out that I had no such problem on this project a month ago (ie. touching a controller was only re-compiling a single file)
I finally figured out that I upgraded my phoenix version. And this specific commit is the one that made the issue apparent.
Is it intended? Should I open a ticket on phoenix?
That commit is actually removing the compile time dependencies of plugs if :init_mode
is configured to :runtime
(in dev). It’s there to prevent the issues of cascading recompiles. If :init_mode
is :compile
it should act exactly as before the change.
I actually had no plug_init_mode
option at this time.
But I just tried to set it to both compile
or runtime
, it doesn’t change anything
(I even hard changed the init_mode
value directly in Endpoint
code to discard any configuration mistake)
That’s to be expected as it was only recently added to plug exactly because many people had the same problems as you had. When set to runtime it makes the init/1
function of plugs be executed at runtime instead of compile time, so any compile time dependencies based on those init calls should be gone.