From 37e778b6b44ae087ce052ff380b1e3aaee31473d Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Mon, 9 Dec 2019 16:35:31 -0800 Subject: [PATCH] Cache routes.url_helpers built modules Previously, every time this was called it would return a new module. Every time a new controller is built, it had one of these module included onto it (see AbstractController::Railties::RoutesHelper). Because each time this was a new module we would end up with a different copy being included on each controller. This would also include a different copy on a controller than on its superclass (ex. ApplicationController). Furthermore, each generated module also extended the url helper modules. +--------------+ +-------------+ | | | | | path_helpers | | url_helpers | (named routes added here) | | | | +------+----+--+ +----+--+-----+ ^ ^ ^ ^ | | | | | +---------------+ | | | | | | | +-----------+ | | | | | +------+--+---+ +--+----+-----+ | | | | | (singleton) +------+ url_helpers | (duplicated for each controller) | | | | +-------------+ +-----+-------+ ^ | | +---------+-------+ | | | UsersController | | | +-----------------+ The result of this is that when named routes were added to the two top-level modules, defining those methods became extremely slow because Ruby has to invalidate the class method caches on all of the generated modules as well as each controller. Furthermore because there were multiple paths up the inheritance chain (ex. one through UsersController's generated module and one through ApplicationController's genereated module) it would end up invaliding the classes multiple times (ideally Ruby would be smarter about this, but it's much easier to solve in Rails). Caching this module means that all controllers should include the same module and defining these named routes should be 3-4x faster. This method can generate two different modules, based on whether supports_path is truthy, so those are cached separately. --- actionpack/lib/action_dispatch/routing/route_set.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 7b4a795f91e..9b1162998f5 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -477,6 +477,14 @@ module ActionDispatch end def url_helpers(supports_path = true) + if supports_path + @url_helpers_with_paths ||= generate_url_helpers(true) + else + @url_helpers_without_paths ||= generate_url_helpers(false) + end + end + + def generate_url_helpers(supports_path) routes = self Module.new do