- close_connection
- dispatcher_error
- dispatcher_log
- exit_handler
- exit_now_handler
- gc_countdown
- install_signal_handler
- install_signal_handlers
- logger
- mark_features!
- new
- process!
- process!
- process_each_request
- process_request
- reload!
- reload_handler
- restart!
- restart_handler
- restore!
- run_gc!
- with_signal_handler
SIGNALS | = | { 'HUP' => :reload, 'INT' => :exit_now, 'TERM' => :exit_now, 'USR1' => :exit, 'USR2' => :restart |
GLOBAL_SIGNALS | = | SIGNALS.keys - %w(USR1) |
[RW] | gc_request_period | |
[RW] | log_file_path | |
[R] | when_ready |
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.
[ show source ]
# File railties/lib/fcgi_handler.rb, line 31 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 34: 35: # Yield for additional configuration. 36: yield self if block_given? 37: 38: # Safely install signal handlers. 39: install_signal_handlers 40: 41: @app = Dispatcher.new 42: 43: # Start error timestamp at 11 seconds ago. 44: @last_error_on = Time.now - 11 45: end
Initialize and run the FastCGI instance, passing arguments through to new.
[ show source ]
# File railties/lib/fcgi_handler.rb, line 22 22: def self.process!(*args, &block) 23: new(*args, &block).process! 24: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 47 47: def process!(provider = FCGI) 48: mark_features! 49: 50: dispatcher_log :info, 'starting' 51: process_each_request provider 52: dispatcher_log :info, 'stopping gracefully' 53: 54: rescue Exception => error 55: case error 56: when SystemExit 57: dispatcher_log :info, 'stopping after explicit exit' 58: when SignalException 59: dispatcher_error error, 'stopping after unhandled signal' 60: else 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' 65: retry 66: else 67: dispatcher_error error, 'stopping after unhandled exception within 10 seconds of the last' 68: end 69: end 70: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 236 236: def close_connection(request) 237: request.finish if request 238: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 126 126: def dispatcher_error(e, msg = "") 127: error_message = 128: "Dispatcher failed to catch: #{e} (#{e.class})\n" + 129: " #{e.backtrace.join("\n ")}\n#{msg}" 130: dispatcher_log(:error, error_message) 131: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 118 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" 124: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 163 163: def exit_handler(signal) 164: dispatcher_log :info, "asked to stop ASAP" 165: if @processing 166: @when_ready = :exit 167: else 168: throw :exit 169: end 170: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 158 158: def exit_now_handler(signal) 159: dispatcher_log :info, "asked to stop immediately" 160: exit 161: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 228 228: def gc_countdown 229: if gc_request_period 230: @gc_request_countdown ||= gc_request_period 231: @gc_request_countdown -= 1 232: run_gc! if @gc_request_countdown <= 0 233: end 234: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 137 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 140: 141: begin 142: trap(signal, handler) 143: rescue ArgumentError 144: dispatcher_log :warn, "Ignoring unsupported signal #{signal}." 145: end 146: else 147: dispatcher_log :warn, "Ignoring unsupported signal #{signal}." 148: end 149: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 133 133: def install_signal_handlers 134: GLOBAL_SIGNALS.each { |signal| install_signal_handler(signal) } 135: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 114 114: def logger 115: @logger ||= Logger.new(@log_file_path) 116: end
Make a note of $" so we can safely reload this instance.
[ show source ]
# File railties/lib/fcgi_handler.rb, line 213 213: def mark_features! 214: @features = $".clone 215: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 73 73: def process_each_request(provider) 74: request = nil 75: 76: catch :exit do 77: provider.each do |request| 78: process_request(request) 79: 80: case when_ready 81: when :reload 82: reload! 83: when :restart 84: close_connection(request) 85: restart! 86: when :exit 87: close_connection(request) 88: throw :exit 89: end 90: end 91: end 92: rescue SignalException => signal 93: raise unless signal.message == 'SIGUSR1' 94: close_connection(request) 95: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 97 97: def process_request(request) 98: @processing, @when_ready = true, nil 99: gc_countdown 100: 101: with_signal_handler 'USR1' do 102: begin 103: ::Rack::Handler::FastCGI.serve(request, @app) 104: rescue SignalException, SystemExit 105: raise 106: rescue Exception => error 107: dispatcher_error error, 'unhandled dispatch error' 108: end 109: end 110: ensure 111: @processing = false 112: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 205 205: def reload! 206: run_gc! if gc_request_period 207: restore! 208: @when_ready = nil 209: dispatcher_log :info, "reloaded" 210: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 172 172: def reload_handler(signal) 173: dispatcher_log :info, "asked to reload ASAP" 174: if @processing 175: @when_ready = :reload 176: else 177: reload! 178: end 179: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 190 190: def 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(' ') 194: 195: dispatcher_log :info, "restarted" 196: 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 201: 202: exec(command_line) 203: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 181 181: def restart_handler(signal) 182: dispatcher_log :info, "asked to restart ASAP" 183: if @processing 184: @when_ready = :restart 185: else 186: restart! 187: end 188: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 217 217: def restore! 218: $".replace @features 219: Dispatcher.reset_application! 220: ActionController::Routing::Routes.reload 221: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 223 223: def run_gc! 224: @gc_request_countdown = gc_request_period 225: GC.enable; GC.start; GC.disable 226: end
[ show source ]
# File railties/lib/fcgi_handler.rb, line 151 151: def with_signal_handler(signal) 152: install_signal_handler(signal) 153: yield 154: ensure 155: install_signal_handler(signal, 'DEFAULT') 156: end