Class | PhusionPassenger::Railz::ApplicationSpawner
In: | lib/phusion_passenger/railz/application_spawner.rb
Parent: | AbstractServer

This class is capable of spawning instances of a single Ruby on Rails application. It does so by preloading as much of the application‘s code as possible, then creating instances of the application using what is already preloaded. This makes it spawning application instances very fast, except for the first spawn.

Use multiple instances of ApplicationSpawner if you need to spawn multiple different Ruby on Rails applications.

Note: ApplicationSpawner may only be started asynchronously with AbstractServer#start. Starting it synchronously with AbstractServer#start_synchronously has not been tested.

### Methods

new spawn_application spawn_application! start

### Included Modules

Utils

### Classes and Modules

Class PhusionPassenger::Railz::ApplicationSpawner::Error

### Constants

ROOT_UID | = | 0 |  | The user ID of the root user.
ROOT_GID | = | 0 |  | The group ID of the root user.

### Attributes

app_root | [R] | The application root of this spawner.

### Public Class methods

new(app_root, options = {})

`app_root` is the root directory of this application, i.e. the directory that contains ‘app/’, ‘public/’, etc. If given an invalid directory, or a directory that doesn‘t appear to be a Rails application root directory, then an InvalidPath will be raised.

Additional options are:

- `lower_privilege` and `lowest_user`: If `lower_privilege` is true, then ApplicationSpawner will attempt to switch to the user who owns the application‘s `config/environment.rb`, and to the default group of that user.

  If that user doesn‘t exist on the system, or if that user is root, then ApplicationSpawner will attempt to switch to the username given by `lowest_user` (and to the default group of that user). If `lowest_user` doesn‘t exist either, or if switching user failed (because the current process does not have the privilege to do so), then ApplicationSpawner will continue without reporting an error.

- `environment`: Allows one to specify the RAILS_ENV environment to use.

All other options will be passed on to RequestHandler.

[Source]

        # File lib/phusion_passenger/railz/application_spawner.rb, line 83
    83:         def initialize(app_root, options = {})
    84:                 super()
    85:                 begin
    86:                         @app_root = normalize_path(app_root)
    87:                 rescue SystemCallError => e
    88:                         raise InvalidPath, e.message
    89:                 rescue InvalidPath
    90:                         raise
    91:                 end
    92:                 @options = sanitize_spawn_options(options)
    93:                 @lower_privilege = @options["lower_privilege"]
    94:                 @lowest_user     = @options["lowest_user"]
    95:                 @environment     = @options["environment"]
    96:                 self.max_idle_time = DEFAULT_APP_SPAWNER_MAX_IDLE_TIME
    97:                 assert_valid_app_root(@app_root)
    98:                 define_message_handler(:spawn_application, :handle_spawn_application)
    99:         end

### Public Instance methods

spawn_application()

Spawn an instance of the RoR application. When successful, an Application object will be returned, which represents the spawned RoR application.

Raises:

- AbstractServer::ServerNotStarted: The ApplicationSpawner server hasn‘t already been started.
- ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly.

[Source]

         # File lib/phusion_passenger/railz/application_spawner.rb, line 107
    107:         def spawn_application
    108:                 server.write("spawn_application")
    109:                 pid, socket_name, socket_type = server.read
    110:                 if pid.nil?
    111:                         raise IOError, "Connection closed"
    112:                 end
    113:                 owner_pipe = server.recv_io
    114:                 return Application.new(@app_root, pid, socket_name,
    115:                         socket_type, owner_pipe)
    116:         rescue SystemCallError, IOError, SocketError => e
    117:                 raise Error, "The application spawner server exited unexpectedly: #{e}"
    118:         end

spawn_application!()

Spawn an instance of the RoR application. When successful, an Application object will be returned, which represents the spawned RoR application.

Unlike spawn_application, this method may be called even when the ApplicationSpawner server isn‘t started. This allows one to spawn a RoR application without preloading any source files.

This method may only be called if no Rails framework has been loaded in the current Ruby VM.

Raises:

- AppInitError: The Ruby on Rails application raised an exception or called exit() during startup.
- SystemCallError, IOError, SocketError: Something went wrong.

[Source]

         # File lib/phusion_passenger/railz/application_spawner.rb, line 134
    134:         def spawn_application!
    135:                 a, b = UNIXSocket.pair
    136:                 pid = safe_fork('application', true) do
    137:                         begin
    138:                                 a.close
    139:
    140:                                 file_descriptors_to_leave_open = [0, 1, 2, b.fileno]
    141:                                 NativeSupport.close_all_file_descriptors(file_descriptors_to_leave_open)
    142:                                 close_all_io_objects_for_fds(file_descriptors_to_leave_open)
    143:
    144:                                 channel = MessageChannel.new(b)
    145:                                 success = report_app_init_status(channel) do
    146:                                         ENV['RAILS_ENV'] = @environment
    147:                                         Dir.chdir(@app_root)
    148:                                         if @lower_privilege
    149:                                                 lower_privilege('config/environment.rb', @lowest_user)
    150:                                         end
    151:
    152:                                         # require Rails' environment, using the same path as the original rails dispatcher,
    153:                                         # which normally does: require File.dirname(__FILE__) + "/../config/environment"
    154:                                         # thus avoiding the possibility of including the same file twice.
    155:                                         require 'public/../config/environment'
    156:
    157:                                         require 'dispatcher'
    158:                                 end
    159:                                 if success
    160:                                         start_request_handler(channel, false)
    161:                                 end
    162:                         rescue SignalException => e
    163:                                 if e.message != AbstractRequestHandler::HARD_TERMINATION_SIGNAL &&
    164:                                    e.message != AbstractRequestHandler::SOFT_TERMINATION_SIGNAL
    165:                                         raise
    166:                                 end
    167:                         end
    168:                 end
    169:                 b.close
    170:                 Process.waitpid(pid) rescue nil
    171:
    172:                 channel = MessageChannel.new(a)
    173:                 unmarshal_and_raise_errors(channel)
    174:
    175:                 # No exception was raised, so spawning succeeded.
    176:                 pid, socket_name, socket_type = channel.read
    177:                 if pid.nil?
    178:                         raise IOError, "Connection closed"
    179:                 end
    180:                 owner_pipe = channel.recv_io
    181:                 return Application.new(@app_root, pid, socket_name,
    182:                         socket_type, owner_pipe)
    183:         end

start()

Overrided from AbstractServer#start.

May raise these additional exceptions:

- AppInitError: The Ruby on Rails application raised an exception or called exit() during startup.
- ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly.

[Source]

         # File lib/phusion_passenger/railz/application_spawner.rb, line 191
    191:         def start
    192:                 super
    193:                 begin
    194:                         unmarshal_and_raise_errors(server)
    195:                 rescue IOError, SystemCallError, SocketError => e
    196:                         stop
    197:                         raise Error, "The application spawner server exited unexpectedly: #{e}"
    198:                 rescue
    199:                         stop
    200:                         raise
    201:                 end
    202:         end

[Validate]