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
43 # Start error timestamp at 11 seconds ago.
44 @last_error_on = Time
.now
- 11
47 def process
!(provider
= FCGI
)
50 dispatcher_log
:info, 'starting'
51 process_each_request provider
52 dispatcher_log
:info, 'stopping gracefully'
54 rescue Exception
=> error
57 dispatcher_log
:info, 'stopping after explicit exit'
59 dispatcher_error error
, 'stopping after unhandled signal'
61 # Retry if exceptions occur more than 10 seconds apart.
62 if Time
.now
- @last_error_on > 10
63 @last_error_on = Time
.now
64 dispatcher_error error
, 'retrying after unhandled exception'
67 dispatcher_error error
, 'stopping after unhandled exception within 10 seconds of the last'
73 def process_each_request(provider
)
77 provider
.each
do |request
|
78 process_request(request
)
84 close_connection(request
)
87 close_connection(request
)
92 rescue SignalException
=> signal
93 raise unless signal
.message
== 'SIGUSR1'
94 close_connection(request
)
97 def process_request(request
)
98 @processing, @when_ready = true, nil
101 with_signal_handler
'USR1' do
103 ::Rack::Handler::FastCGI.serve(request
, @app)
104 rescue SignalException
, SystemExit
106 rescue Exception
=> error
107 dispatcher_error error
, 'unhandled dispatch error'
115 @logger ||= Logger
.new(@log_file_path)
118 def dispatcher_log(level
, msg
)
119 time_str
= Time
.now
.strftime("%d/%b/%Y:%H:%M:%S")
120 logger
.send(level
, "[#{time_str} :: #{$$}] #{msg}")
121 rescue Exception
=> log_error
# Logger errors
122 STDERR << "Couldn't write to #{@log_file_path.inspect}: #{msg}\n"
123 STDERR << " #{log_error.class}: #{log_error.message}\n"
126 def dispatcher_error(e
, msg
= "")
128 "Dispatcher failed to catch: #{e} (#{e.class})\n" +
129 " #{e.backtrace.join("\n ")}\n#{msg}"
130 dispatcher_log(:error, error_message
)
133 def install_signal_handlers
134 GLOBAL_SIGNALS
.each
{ |signal
| install_signal_handler(signal
) }
137 def install_signal_handler(signal
, handler
= nil)
138 if SIGNALS
.include?(signal
) && self.class.method_defined
?(name
= "#{SIGNALS[signal]}_handler")
139 handler
||= method(name
).to_proc
142 trap(signal
, handler
)
144 dispatcher_log
:warn, "Ignoring unsupported signal #{signal}."
147 dispatcher_log
:warn, "Ignoring unsupported signal #{signal}."
151 def with_signal_handler(signal
)
152 install_signal_handler(signal
)
155 install_signal_handler(signal
, 'DEFAULT')
158 def exit_now_handler(signal
)
159 dispatcher_log
:info, "asked to stop immediately"
163 def exit_handler(signal
)
164 dispatcher_log
:info, "asked to stop ASAP"
172 def reload_handler(signal
)
173 dispatcher_log
:info, "asked to reload ASAP"
175 @when_ready = :reload
181 def restart_handler(signal
)
182 dispatcher_log
:info, "asked to restart ASAP"
184 @when_ready = :restart
191 config
= ::Config::CONFIG
192 ruby
= File
::join(config
['bindir'], config
['ruby_install_name']) + config
['EXEEXT']
193 command_line
= [ruby
, $0, ARGV].flatten
.join(' ')
195 dispatcher_log
:info, "restarted"
197 # close resources as they won't be closed by
198 # the OS when using exec
199 logger
.close
rescue nil
200 Rails
.logger
.close
rescue nil
206 run_gc
! if gc_request_period
209 dispatcher_log
:info, "reloaded"
212 # Make a note of $" so we can safely reload this instance.
219 Dispatcher
.reset_application
!
220 ActionController
::Routing::Routes.reload
224 @gc_request_countdown = gc_request_period
225 GC
.enable
; GC
.start
; GC
.disable
230 @gc_request_countdown ||= gc_request_period
231 @gc_request_countdown -= 1
232 run_gc
! if @gc_request_countdown <= 0
236 def close_connection(request
)
237 request
.finish
if request