0

In rails 5 development mode, I keep getting this Net::ReadTimeout(Net::ReadTimeout) error in my custom session middleware that does a http request to my local mock custom session engine. Also the error only triggered after I made a code change and rails renders an error page. This is extremely annoying and really slows down the development process since we all have to refresh twice to see the result of code change.

After tracing down the code in those middlewares, It appears that my custom session middleware kick off the http request before the reloader complete reloading.

I wondered if we can halt/stop the rack middleware request from keep going down the rack until the reloading is done.

Rails version: 5.1

Ruby version: 2.4.1

I put the following log messages before and after the reloader is done reloading

application.rb

ActiveSupport::Reloader.to_run do
  puts 'Reloading'
end

ActiveSupport::Reloader.to_complete do
  puts 'DONE Reloading'
end

custom_session_siddleware.rb

def call(env)
  ...
  puts 'Session Processing'
  http = Net::HTTP.new(uri, port)
  ...
  @app.call(env)
end

The out put after I make a code change and refresh

Reloading
Session Processing
DONE Reloading

Here are all my middlewares

use Rack::Sendfile
use ActionDispatch::Static
use ActionDispatch::Executor
use ActiveSupport::Cache::Strategy::LocalCache::Middleware
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use RequestStore::Middleware
use ActionDispatch::RemoteIp
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use MyCustomSession::CustomSessionMiddleware
AirWick219
  • 894
  • 8
  • 27
  • 1
    Is `uri` pointing back at the running application? – matthewd Jun 27 '18 at 12:19
  • Your output shows all three steps occurring... how is that related to the timeout exception? – matthewd Jun 27 '18 at 12:20
  • So we have an engine that mocks the custom session service and it's only mounted for development, so the CustomSessionMiddleware would hit the mock in development mode and would hit the real service for production mode – AirWick219 Jun 27 '18 at 12:49
  • The output is showing the sequence of events when there is a code change. 1. The reloader unload all the classes, 2, my custom session middleware call's an http request (which it times out), 3. The reloader is done reloading. – AirWick219 Jun 27 '18 at 12:54
  • My theory is that. The time out happens because the custom session middleware is calling a route that in the middle of unload or reloading. And the sequence of events should be 1. Reloading. 2, DONE Reloading 3, Session Processing. I am not sure why the reloader middleware would let the request to keep going down the rack middlewares before is done reloading. – AirWick219 Jun 27 '18 at 12:59
  • `to_complete` is run [after a work run that has reloaded](https://github.com/rails/rails/blob/375a4143cf5caeb6159b338be824903edfd62836/activesupport/lib/active_support/reloader.rb#L16) -- that is, after the request that triggered the reload, not just after the reload. Try `before_`/`after_class_unload` for better visibility. – matthewd Jun 27 '18 at 13:19
  • After adding the `before_/after_class_unload` the sequence is 1. Before class unload, 2. After class unload, 3, Reloading, 4. Session Processing, 5 DONE Reloading – AirWick219 Jun 27 '18 at 14:18
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/173896/discussion-between-airwick219-and-matthewd). – AirWick219 Jun 27 '18 at 15:22
  • Added this issue in rails to keep track of this. https://github.com/rails/rails/issues/33252 – AirWick219 Jun 29 '18 at 13:23
  • @matthewd Can you check out the this issue above or this https://stackoverflow.com/questions/51205016/rails-5-reloader-move-on-to-the-next-middleware-before-complete-reloading . I think that are related. – AirWick219 Jul 09 '18 at 05:55
  • @matthewd I think I found a work around for this but run into a clean up issue that i think you can help https://stackoverflow.com/questions/51849817/rail-5-swap-actiondispatchreloader-with-a-custom-reloader – AirWick219 Aug 14 '18 at 21:18

1 Answers1

0

If your custom middleware is making a request back to the running Rack application, it's going to be very easy for you to have a bad time. But it can be made to work.


By the time your initial request is inside the Reloader, the actual reload has already occurred -- but that's only the unload.

Following the unload, any application (or engine) classes will need to be autoloaded again the next time they're accessed... and I think that's where you're running into an issue: the inner request needs to load something, but it can't do so while the outer request is active.

To fix this, the outer request needs to inform the system that it's in a safe place for another thread/request to load new code. Specifically, you need to wrap the Net::HTTP request (only -- not the @app.call(env)) with permit_concurrent_loads.

matthewd
  • 4,332
  • 15
  • 21
  • The documentation is rather confusing and I have tried wrapping everything above the `@app.call(env)` with `ActiveSupport::Dependencies.interlock.permit_concurrent_loads do` in the call method. – AirWick219 Jun 27 '18 at 14:29
  • or maybe I have to wrap that http request with something like `Concurrent::Future.execute do` ?? – AirWick219 Jun 27 '18 at 14:44