diff options
author | Jamis Buck <jamis@37signals.com> | 2005-06-29 11:07:20 +0000 |
---|---|---|
committer | Jamis Buck <jamis@37signals.com> | 2005-06-29 11:07:20 +0000 |
commit | 3cc47a4297d9c43e88972555e853e2d5359d804f (patch) | |
tree | e020dbfe531b90316377664ef6bfeca8f3976f73 /railties/lib/fcgi_handler.rb | |
parent | 8335fc610c7f2f5bc1d4b0e01370013bfccdba81 (diff) | |
download | rails-3cc47a4297d9c43e88972555e853e2d5359d804f.tar.gz rails-3cc47a4297d9c43e88972555e853e2d5359d804f.tar.bz2 rails-3cc47a4297d9c43e88972555e853e2d5359d804f.zip |
Use SIGHUP to dynamically reload an fcgi process without restarting it. Refactored dispatch.fcgi so that the RailsFCGIHandler is in the lib dir.
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1565 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'railties/lib/fcgi_handler.rb')
-rw-r--r-- | railties/lib/fcgi_handler.rb | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/railties/lib/fcgi_handler.rb b/railties/lib/fcgi_handler.rb new file mode 100644 index 0000000000..f615ff6d34 --- /dev/null +++ b/railties/lib/fcgi_handler.rb @@ -0,0 +1,112 @@ +require 'fcgi' +require 'logger' +require 'dispatcher' + +class RailsFCGIHandler + attr_reader :when_ready + attr_reader :processing + + def self.process! + new.process! + end + + def initialize(log_file_path = "#{RAILS_ROOT}/log/fastcgi.crash.log") + @when_ready = nil + @processing = false + + trap("HUP", method(:restart_handler).to_proc) + trap("USR1", method(:trap_handler).to_proc) + + # initialize to 11 seconds ago to minimize special cases + @last_error_on = Time.now - 11 + + @log_file_path = log_file_path + dispatcher_log(:info, "starting") + end + + def process! + mark! + + FCGI.each_cgi do |cgi| + if when_ready == :restart + restore! + @when_ready = nil + dispatcher_log(:info, "restarted") + end + + process_request(cgi) + break if when_ready == :exit + end + + dispatcher_log(:info, "terminated gracefully") + + rescue SystemExit => exit_error + dispatcher_log(:info, "terminated by explicit exit") + + rescue Object => fcgi_error + # retry on errors that would otherwise have terminated the FCGI process, + # but only if they occur more than 10 seconds apart. + if !(SignalException === fcgi_error) && Time.now - @last_error_on > 10 + @last_error_on = Time.now + dispatcher_error(fcgi_error, "almost killed by this error") + retry + else + dispatcher_error(fcgi_error, "killed by this error") + end + end + + private + def logger + @logger ||= Logger.new(@log_file_path) + end + + def dispatcher_log(level, msg) + time_str = Time.now.strftime("%d/%b/%Y:%H:%M:%S") + logger.send(level, "[#{time_str} :: #{$$}] #{msg}") + rescue Object => log_error + STDERR << "Couldn't write to #{@log_file_path.inspect}: #{msg}\n" + STDERR << " #{log_error.class}: #{log_error.message}\n" + end + + def dispatcher_error(e,msg="") + error_message = + "Dispatcher failed to catch: #{e} (#{e.class})\n" + + " #{e.backtrace.join("\n ")}\n#{msg}" + dispatcher_log(:error, error_message) + end + + def trap_handler(signal) + if processing + dispatcher_log :info, "asked to terminate ASAP" + @when_ready = :exit + else + dispatcher_log :info, "told to terminate NOW" + exit + end + end + + def restart_handler(signal) + @when_ready = :restart + dispatcher_log :info, "asked to restart ASAP" + end + + def process_request(cgi) + @processing = true + Dispatcher.dispatch(cgi) + rescue Object => e + raise if SignalException === e + dispatcher_error(e) + ensure + @processing = false + end + + def mark! + @features = $".clone + end + + def restore! + $".replace @features + Dispatcher.reset_application! + ActionController::Routing::Routes.reload + end +end |