14 GLOBAL_SIGNALS
= SIGNALS
.keys
- %w(USR1
)
16 attr_reader
:when_ready
18 attr_accessor
:log_file_path
19 attr_accessor
:gc_request_period
21 # Initialize and run the FastCGI instance, passing arguments through to new.
22 def self.process
!(*args
, &block
)
23 new(*args
, &block
).process
!
26 # Initialize the FastCGI instance with the path to a crash log
27 # detailing unhandled exceptions (default RAILS_ROOT/log/fastcgi.crash.log)
28 # and the number of requests to process between garbage collection runs
29 # (default nil for normal GC behavior.) Optionally, pass a block which
30 # takes this instance as an argument for further configuration.
31 def initialize(log_file_path
= nil, gc_request_period
= nil)
32 self.log_file_path
= log_file_path
|| "#{RAILS_ROOT}/log/fastcgi.crash.log"
33 self.gc_request_period
= gc_request_period
35 # Yield for additional configuration.
36 yield self if block_given
?
38 # Safely install signal handlers.
39 install_signal_handlers
41 # Start error timestamp at 11 seconds ago.
42 @last_error_on = Time
.now
- 11
45 def process
!(provider
= FCGI
)
48 dispatcher_log
:info, 'starting'
49 process_each_request provider
50 dispatcher_log
:info, 'stopping gracefully'
52 rescue Exception
=> error
55 dispatcher_log
:info, 'stopping after explicit exit'
57 dispatcher_error error
, 'stopping after unhandled signal'
59 # Retry if exceptions occur more than 10 seconds apart.
60 if Time
.now
- @last_error_on > 10
61 @last_error_on = Time
.now
62 dispatcher_error error
, 'retrying after unhandled exception'
65 dispatcher_error error
, 'stopping after unhandled exception within 10 seconds of the last'
71 def process_each_request(provider
)
75 provider
.each_cgi
do |cgi
|
90 rescue SignalException
=> signal
91 raise unless signal
.message
== 'SIGUSR1'
95 def process_request(cgi
)
96 @processing, @when_ready = true, nil
99 with_signal_handler
'USR1' do
101 Dispatcher
.dispatch(cgi
)
102 rescue SignalException
, SystemExit
104 rescue Exception
=> error
105 dispatcher_error error
, 'unhandled dispatch error'
113 @logger ||= Logger
.new(@log_file_path)
116 def dispatcher_log(level
, msg
)
117 time_str
= Time
.now
.strftime("%d/%b/%Y:%H:%M:%S")
118 logger
.send(level
, "[#{time_str} :: #{$$}] #{msg}")
119 rescue Exception
=> log_error
# Logger errors
120 STDERR << "Couldn't write to #{@log_file_path.inspect}: #{msg}\n"
121 STDERR << " #{log_error.class}: #{log_error.message}\n"
124 def dispatcher_error(e
, msg
= "")
126 "Dispatcher failed to catch: #{e} (#{e.class})\n" +
127 " #{e.backtrace.join("\n ")}\n#{msg}"
128 dispatcher_log(:error, error_message
)
131 def install_signal_handlers
132 GLOBAL_SIGNALS
.each
{ |signal
| install_signal_handler(signal
) }
135 def install_signal_handler(signal
, handler
= nil)
136 if SIGNALS
.include?(signal
) && self.class.method_defined
?(name
= "#{SIGNALS[signal]}_handler")
137 handler
||= method(name
).to_proc
140 trap(signal
, handler
)
142 dispatcher_log
:warn, "Ignoring unsupported signal #{signal}."
145 dispatcher_log
:warn, "Ignoring unsupported signal #{signal}."
149 def with_signal_handler(signal
)
150 install_signal_handler(signal
)
153 install_signal_handler(signal
, 'DEFAULT')
156 def exit_now_handler(signal
)
157 dispatcher_log
:info, "asked to stop immediately"
161 def exit_handler(signal
)
162 dispatcher_log
:info, "asked to stop ASAP"
170 def reload_handler(signal
)
171 dispatcher_log
:info, "asked to reload ASAP"
173 @when_ready = :reload
179 def restart_handler(signal
)
180 dispatcher_log
:info, "asked to restart ASAP"
182 @when_ready = :restart
189 config
= ::Config::CONFIG
190 ruby
= File
::join(config
['bindir'], config
['ruby_install_name']) + config
['EXEEXT']
191 command_line
= [ruby
, $0, ARGV].flatten
.join(' ')
193 dispatcher_log
:info, "restarted"
195 # close resources as they won't be closed by
196 # the OS when using exec
197 logger
.close
rescue nil
198 Rails
.logger
.close
rescue nil
204 run_gc
! if gc_request_period
207 dispatcher_log
:info, "reloaded"
210 # Make a note of $" so we can safely reload this instance.
217 Dispatcher
.reset_application
!
218 ActionController
::Routing::Routes.reload
222 @gc_request_countdown = gc_request_period
223 GC
.enable
; GC
.start
; GC
.disable
228 @gc_request_countdown ||= gc_request_period
229 @gc_request_countdown -= 1
230 run_gc
! if @gc_request_countdown <= 0
234 def close_connection(cgi
)
235 cgi
.instance_variable_get("@request").finish
if cgi