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

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

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.
  • environment_variables: Environment variables which should be passed to the spawned application. This is NULL-seperated string of key-value pairs, encoded in base64. The last byte in the unencoded data must be a NULL.
  • base_uri: The base URI on which this application is deployed. It equals "/" string if the application is deployed on the root URI. It must not equal the empty string.
  • print_exceptions: Whether exceptions that have occurred during application initialization should be printed to STDERR. The default is true.

All other options will be passed on to RequestHandler.

[Source]

     # File lib/phusion_passenger/railz/application_spawner.rb, line 102
102:         def initialize(app_root, options = {})
103:                 super()
104:                 @app_root = app_root
105:                 @canonicalized_app_root = canonicalize_path(app_root)
106:                 @options = sanitize_spawn_options(options)
107:                 @lower_privilege = @options["lower_privilege"]
108:                 @lowest_user     = @options["lowest_user"]
109:                 @environment     = @options["environment"]
110:                 @encoded_environment_variables = @options["environment_variables"]
111:                 @base_uri = @options["base_uri"] if @options["base_uri"] && @options["base_uri"] != "/"
112:                 @print_exceptions = @options["print_exceptions"]
113:                 self.max_idle_time = DEFAULT_APP_SPAWNER_MAX_IDLE_TIME
114:                 assert_valid_app_root(@app_root)
115:                 define_message_handler(:spawn_application, :handle_spawn_application)
116:         end

Public Instance methods

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

Raises:

[Source]

     # File lib/phusion_passenger/railz/application_spawner.rb, line 124
124:         def spawn_application
125:                 server.write("spawn_application")
126:                 pid, socket_name, socket_type = server.read
127:                 if pid.nil?
128:                         raise IOError, "Connection closed"
129:                 end
130:                 owner_pipe = server.recv_io
131:                 return Application.new(@app_root, pid, socket_name,
132:                         socket_type, owner_pipe)
133:         rescue SystemCallError, IOError, SocketError => e
134:                 raise Error, "The application spawner server exited unexpectedly: #{e}"
135:         end

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 151
151:         def spawn_application!
152:                 a, b = UNIXSocket.pair
153:                 pid = safe_fork('application', true) do
154:                         begin
155:                                 a.close
156:                                 
157:                                 file_descriptors_to_leave_open = [0, 1, 2, b.fileno]
158:                                 NativeSupport.close_all_file_descriptors(file_descriptors_to_leave_open)
159:                                 close_all_io_objects_for_fds(file_descriptors_to_leave_open)
160:                                 
161:                                 channel = MessageChannel.new(b)
162:                                 success = report_app_init_status(channel) do
163:                                         ENV['RAILS_ENV'] = @environment
164:                                         ENV['RAILS_RELATIVE_URL_ROOT'] = @base_uri
165:                                         Dir.chdir(@app_root)
166:                                         if @encoded_environment_variables
167:                                                 set_passed_environment_variables
168:                                         end
169:                                         if @lower_privilege
170:                                                 lower_privilege('config/environment.rb', @lowest_user)
171:                                         end
172:                                         
173:                                         require File.expand_path('config/environment')
174:                                         require 'dispatcher'
175:                                 end
176:                                 if success
177:                                         start_request_handler(channel, false)
178:                                 end
179:                         rescue SignalException => e
180:                                 if e.message != AbstractRequestHandler::HARD_TERMINATION_SIGNAL &&
181:                                    e.message != AbstractRequestHandler::SOFT_TERMINATION_SIGNAL
182:                                         raise
183:                                 end
184:                         end
185:                 end
186:                 b.close
187:                 Process.waitpid(pid) rescue nil
188:                 
189:                 channel = MessageChannel.new(a)
190:                 unmarshal_and_raise_errors(channel, @print_exceptions)
191:                 
192:                 # No exception was raised, so spawning succeeded.
193:                 pid, socket_name, socket_type = channel.read
194:                 if pid.nil?
195:                         raise IOError, "Connection closed"
196:                 end
197:                 owner_pipe = channel.recv_io
198:                 return Application.new(@app_root, pid, socket_name,
199:                         socket_type, owner_pipe)
200:         end

Overrided from AbstractServer#start.

May raise these additional exceptions:

[Source]

     # File lib/phusion_passenger/railz/application_spawner.rb, line 208
208:         def start
209:                 super
210:                 begin
211:                         unmarshal_and_raise_errors(server, @print_exceptions)
212:                 rescue IOError, SystemCallError, SocketError => e
213:                         stop
214:                         raise Error, "The application spawner server exited unexpectedly: #{e}"
215:                 rescue
216:                         stop
217:                         raise
218:                 end
219:         end

[Validate]