aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--railties/CHANGELOG5
-rwxr-xr-xrailties/dispatches/dispatch.fcgi54
2 files changed, 53 insertions, 6 deletions
diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index 5664206ed0..f98c57459d 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,5 +1,10 @@
*SVN*
+
+* Allow graceful exits for dispatch.fcgi processes by sending a SIGUSR1. If the process is currently handling a request, the request will be allowed to complete and then will terminate itself. If a request is not being handled, the process is terminated immediately (via #exit). This basically works like restart graceful on Apache. [Jamis Buck]
+
+* Made dispatch.fcgi more robust by catching fluke errors and retrying unless its a permanent condition. [Jamis Buck]
+
* Added console --profile for profiling an IRB session #1154 [bitsweat]
* Changed console_sandbox into console --sandbox #1154 [bitsweat]
diff --git a/railties/dispatches/dispatch.fcgi b/railties/dispatches/dispatch.fcgi
index 683a61f4c9..34a99a7105 100755
--- a/railties/dispatches/dispatch.fcgi
+++ b/railties/dispatches/dispatch.fcgi
@@ -1,27 +1,69 @@
#!/usr/local/bin/ruby
+def dispatcher_log(level, path,msg)
+ Logger.new(path).send(level, msg)
+rescue Object => log_error
+ STDERR << "Couldn't write to #{path}: #{msg}"
+end
+
def dispatcher_error(path,e,msg="")
error_message =
"[#{Time.now}] Dispatcher failed to catch: #{e} (#{e.class})\n #{e.backtrace.join("\n ")}\n#{msg}"
- Logger.new(path).fatal(error_message)
-rescue Object => log_error
- STDERR << "Couldn't write to #{path} (#{e} [#{e.class}])\n" << error_message
+ dispatcher_log(:error, path, error_message)
end
+last_error_on = nil
begin
require File.dirname(__FILE__) + "/../config/environment"
require 'dispatcher'
require 'fcgi'
log_file_path = "#{RAILS_ROOT}/log/fastcgi.crash.log"
+ dispatcher_log(:info, log_file_path, "fcgi #{$$} starting")
+
+ # Allow graceful exits by sending the process SIGUSR1. If the process is
+ # currently handling a request, the request will be allowed to complete and
+ # then will terminate itself. If a request is not being handled, the
+ # process is terminated immediately (via #exit).
+
+ $please_exit_at_your_earliest_convenience = false
+ $i_am_currently_processing_a_request = false
+ trap("USR1") do
+ if $i_am_currently_processing_a_request
+ dispatcher_log(:info, log_file_path, "asking #{$$} to terminate ASAP")
+ $please_exit_at_your_earliest_convenience = true
+ else
+ dispatcher_log(:info, log_file_path, "telling #{$$} to terminate NOW")
+ exit
+ end
+ end
+
+ # Process each request as it comes in, as a pseudo-CGI.
FCGI.each_cgi do |cgi|
begin
+ $i_am_currently_processing_a_request = true
Dispatcher.dispatch(cgi)
- rescue Object => rails_error
- dispatcher_error(log_file_path, rails_error)
+ rescue Object => e
+ dispatcher_error(log_file_path, e)
+ ensure
+ $stdout.flush
+ $i_am_currently_processing_a_request = false
+ break if $please_exit_at_your_earliest_convenience
end
end
+
+ dispatcher_log(:info, log_file_path, "fcgi #{$$} terminated gracefully")
+rescue SystemExit => exit_error
+ dispatcher_log(:info, log_file_path, "fcgi #{$$} terminated by explicit exit")
rescue Object => fcgi_error
- dispatcher_error(log_file_path, fcgi_error, "FCGI process #{$$} killed by this error\n")
+ # 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) && (last_error_on.nil? || last_error_on - Time.now > 10)
+ last_error_on = Time.now
+ dispatcher_error(log_file_path, fcgi_error, "FCGI process #{$$} almost killed by this error\n")
+ retry
+ else
+ dispatcher_error(log_file_path, fcgi_error, "FCGI process #{$$} killed by this error\n")
+ end
end \ No newline at end of file