From 5650bc90940bd850a2f44bd3b58549d6b336dcd0 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 2 Jul 2005 04:52:14 +0000 Subject: r1588@asus: jeremy | 2005-07-02 03:14:45 -0700 Optional periodic garbage collection for dispatch.fcgi. Graceful exit on TERM also (a la Apache1). Ignore signals the platform does not support, such as USR1 on Windows. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1592 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- railties/lib/fcgi_handler.rb | 79 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 13 deletions(-) (limited to 'railties/lib') diff --git a/railties/lib/fcgi_handler.rb b/railties/lib/fcgi_handler.rb index f615ff6d34..f489627985 100644 --- a/railties/lib/fcgi_handler.rb +++ b/railties/lib/fcgi_handler.rb @@ -3,41 +3,77 @@ require 'logger' require 'dispatcher' class RailsFCGIHandler + SIGNALS = { + 'HUP' => :reload, + 'TERM' => :graceful_exit, + 'USR1' => :graceful_exit + } + attr_reader :when_ready attr_reader :processing - def self.process! - new.process! + attr_accessor :log_file_path + attr_accessor :gc_request_period + + + # Initialize and run the FastCGI instance, passing arguments through to new. + def self.process!(*args, &block) + new(*args, &block).process! end - def initialize(log_file_path = "#{RAILS_ROOT}/log/fastcgi.crash.log") + # Initialize the FastCGI instance with the path to a crash log + # detailing unhandled exceptions (default RAILS_ROOT/log/fastcgi.crash.log) + # and the number of requests to process between garbage collection runs + # (default nil for normal GC behavior.) Optionally, pass a block which + # takes this instance as an argument for further configuration. + def initialize(log_file_path = nil, gc_request_period = nil) @when_ready = nil @processing = false - trap("HUP", method(:restart_handler).to_proc) - trap("USR1", method(:trap_handler).to_proc) + self.log_file_path = log_file_path || "#{RAILS_ROOT}/log/fastcgi.crash.log" + self.gc_request_period = gc_request_period + + # Yield for additional configuration. + yield self if block_given? - # initialize to 11 seconds ago to minimize special cases + # Safely install signal handlers. + install_signal_handlers + + # Start error timestamp at 11 seconds ago. @last_error_on = Time.now - 11 - @log_file_path = log_file_path dispatcher_log(:info, "starting") end def process! + # Make a note of $" so we can safely reload this instance. mark! + # Begin countdown to garbage collection. + run_gc! if gc_request_period + FCGI.each_cgi do |cgi| - if when_ready == :restart + # Safely reload this instance if requested. + if when_ready == :reload + run_gc! if gc_request_period restore! @when_ready = nil - dispatcher_log(:info, "restarted") + dispatcher_log(:info, "reloaded") end process_request(cgi) + + # Break if graceful exit requested. break if when_ready == :exit + + # Garbage collection countdown. + if gc_request_period + @gc_request_countdown -= 1 + run_gc! if @gc_request_countdown <= 0 + end end + GC.enable dispatcher_log(:info, "terminated gracefully") rescue SystemExit => exit_error @@ -75,7 +111,19 @@ class RailsFCGIHandler dispatcher_log(:error, error_message) end - def trap_handler(signal) + def install_signal_handlers + SIGNALS.each do |signal, handler_name| + install_signal_handler signal, method("#{handler_name}_handler").to_proc + end + end + + def install_signal_handler(signal, handler) + trap signal, handler + rescue ArgumentError + dispatcher_log :warn, "Ignoring unsupported signal #{signal}." + end + + def graceful_exit_handler(signal) if processing dispatcher_log :info, "asked to terminate ASAP" @when_ready = :exit @@ -85,9 +133,9 @@ class RailsFCGIHandler end end - def restart_handler(signal) - @when_ready = :restart - dispatcher_log :info, "asked to restart ASAP" + def reload_handler(signal) + @when_ready = :reload + dispatcher_log :info, "asked to reload ASAP" end def process_request(cgi) @@ -109,4 +157,9 @@ class RailsFCGIHandler Dispatcher.reset_application! ActionController::Routing::Routes.reload end + + def run_gc! + @gc_request_countdown = gc_request_period + GC.enable; GC.start; GC.disable + end end -- cgit v1.2.3