DEV: Speed up requests in development mode (#12890)

DEV: Speed up requests in development mode (#12890)

On every request, Rails checks to see whether any ruby code has been changed on the filesystem. The default FileUpdateChecker does this by iterating over every file on the autoload_paths and comparing its modified-time.

In Discourse, our autoload path of /app includes the /app/assets directory, and therefore thousands of non-ruby files (e.g. node_modules). This makes the Dir["/app"] call very slow (>100ms in my case). On my machine, every Rails-handled request spends around 150-200ms in the FileUpdateChecker. This commit introduces a couple of changes to completely eliminate this wasted time:

  • The /app/assets directory is excluded from the file watchers. For me, this cut the time spent in the file_watcher to around 50-100ms

  • Switches our development config to use the EventedFileUpdateChecker, which makes use of the listen gem to subscribe to os-specific notifications of changes. This completely removes the FileUpdateChecker from the critical path

On my machine, topic_list requests now return in around 80ms (previously >200ms). Live code reload still works as it did before

diff --git a/config/application.rb b/config/application.rb
index b8294b1..f29044b 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -120,6 +120,19 @@ module Discourse
     Rails.autoloaders.main.ignore(Dir["#{config.root}/app/models/reports"])
     Rails.autoloaders.main.ignore(Dir["#{config.root}/lib/freedom_patches"])
 
+    def watchable_args
+      files, dirs = super
+
+      # Skip the assets directory. It doesn't contain any .rb files, so watching it
+      # is just slowing things down and raising warnings about node_modules symlinks
+      app_file_extensions = dirs.delete("#{config.root}/app")
+      Dir["#{config.root}/app/*"].reject { |path| path.end_with? "/assets" }.each do |path|
+        dirs[path] = app_file_extensions
+      end
+
+      [files, dirs]
+    end
+
     # Only load the plugins named here, in the order given (default is alphabetical).
     # :all can be used as a placeholder for all plugins not explicitly named.
     # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 53025e0..8d9ffb6 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -7,6 +7,7 @@ Discourse::Application.configure do
   # every request.  This slows down response time but is perfect for development
   # since you don't have to restart the web server when you make code changes.
   config.cache_classes = false
+  config.file_watcher = ActiveSupport::EventedFileUpdateChecker
 
   # Log error messages when you accidentally call methods on nil.
   config.eager_load = false

GitHub sha: 59c239d8

This commit appears in #12890 which was approved by CvX. It was merged by davidtaylorhq.