Rails reload dynamic routes on multiple instances/servers

前端 未结 2 1091
花落未央
花落未央 2021-01-14 01:09

How can we force Rails to reload_routes on multiple servers/instances?

We have a multi-tenant platform in Google App-Engine running on 5+ instances and we want all

相关标签:
2条回答
  • 2021-01-14 01:47

    We finally found a solution that works pretty well and is also not affecting performance too much. We use the fact that Threads in production are keeping states across requests. So we decided to create a middleware that checks the latest timestamp of a routes change and in case the timestamp is not the same as the one saved in Thread.current we force a Frontend::Application.reload_routes!

    config/production.rb

    Frontend::Application.configure do
      ...
      config.middleware.use RoutesReloader
      ...
    end
    

    app/middleware/routes_reloader.rb

    class RoutesReloader
      SKIPPED_PATHS = ['/assets/', '/admin/']
    
      def initialize(app)
        @app = app
      end
    
      def call(env)
        if reload_required?(env)
          timestamp = Rails.cache.read(:routes_changed_timestamp)
    
          if Thread.current[:routes_changed_timestamp] != timestamp
            Frontend::Application.reload_routes!
    
            Thread.current[:routes_changed_timestamp] = timestamp
          end
        end
    
        @app.call(env)
      end
    
      private
    
      def reload_required?(env)
        SKIPPED_PATHS.none? { |word| env['PATH_INFO'].include?(word) }
      end
    end
    

    app/model/routes.rb

    class Routes < ActiveRecord::Base
    
      after_save :save_timestamp
    
      private
    
      def save_timestamp
        ts = Time.zone.now.to_i
        Rails.cache.write(:routes_changed_timestamp, ts, expires_in: 30.minutes)
      end
    end
    

    Benefits:

    • You can exclude the reload on certain paths like /assets/ and /admin/
    • Threads server multiple requests and the reload only happens once
    • You can implement this on any model you like

    Caveats:

    • New Threads will load routes twice
    • All Threads will reload routes if you clear Rails Cache (you could overcome this with a persistent solution; e.g. saving the timestamp into mysql and then into cache)

    But overall we didn't recognise any performance drops.

    We have been struggling with this now for years and the above solution is the first that really helped us reloading routes on multiple threads.

    0 讨论(0)
  • 2021-01-14 01:49

    Assuming you have no shared storage: You could write an action that reloads the route for that particular instance. When you trigger DynamicRoutes::reload, you would make a request to the other instances' reload action.

    If you do have shared storage, write a before_action that reloads the routes whenever a specific file has been "touched" and touch that file if you want to have all instances reload the routes.

    0 讨论(0)
提交回复
热议问题