aboutsummaryrefslogtreecommitdiffstats
path: root/railties/dispatches/dispatch.fcgi
blob: 34a99a7105dd5d9809de84f3e11e7019e7e42153 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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}"
  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 => 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
  # 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