Included Modules

Class Index [+]

Quicksearch

PhusionPassenger::SpawnManager

The spawn manager is capable of spawning Ruby on Rails or Rack application instances. It acts like a simple fascade for the rest of the spawn manager system.

Note: SpawnManager may only be started synchronously with AbstractServer#start_synchronously. Starting asynchronously has not been tested. Don’t forget to call cleanup after the server’s main loop has finished.

Ruby on Rails optimizations

Spawning a Ruby on Rails application is usually slow. But SpawnManager will preload and cache Ruby on Rails frameworks, as well as application code, so subsequent spawns will be very fast.

Internally, SpawnManager uses Railz::FrameworkSpawner to preload and cache Ruby on Rails frameworks. Railz::FrameworkSpawner, in turn, uses Railz::ApplicationSpawner to preload and cache application code.

In case you’re wondering why the namespace is “Railz” and not “Rails”: it’s to work around an obscure bug in ActiveSupport’s Dispatcher.

Public Class Methods

new() click to toggle source
    # File lib/phusion_passenger/spawn_manager.rb, line 61
61:         def initialize
62:                 super()
63:                 @spawners = AbstractServerCollection.new
64:                 define_message_handler(:spawn_application, :handle_spawn_application)
65:                 define_message_handler(:reload, :handle_reload)
66:                 define_signal_handler('SIGHUP', :reload)
67:                 
68:                 # Start garbage collector in order to free up some existing
69:                 # heap slots. This prevents the heap from growing unnecessarily
70:                 # during the startup phase.
71:                 GC.start
72:                 if GC.copy_on_write_friendly?
73:                         # Preload libraries for copy-on-write semantics.
74:                         require 'base64'
75:                         require 'phusion_passenger/application'
76:                         require 'phusion_passenger/railz/framework_spawner'
77:                         require 'phusion_passenger/railz/application_spawner'
78:                         require 'phusion_passenger/rack/application_spawner'
79:                         require 'phusion_passenger/html_template'
80:                         require 'phusion_passenger/platform_info'
81:                         require 'phusion_passenger/exceptions'
82:                 end
83:         end

Public Instance Methods

cleanup() click to toggle source

Cleanup resources. Should be called when this SpawnManager is no longer needed.

     # File lib/phusion_passenger/spawn_manager.rb, line 206
206:         def cleanup
207:                 @spawners.cleanup
208:         end
reload(app_root = nil) click to toggle source

Remove the cached application instances at the given application root. If nil is specified as application root, then all cached application instances will be removed, no matter the application root.

Long description: Application code might be cached in memory. But once it a while, it will be necessary to reload the code for an application, such as after deploying a new version of the application. This method makes sure that any cached application code is removed, so that the next time an application instance is spawned, the application code will be freshly loaded into memory.

Raises AbstractServer::SpawnError if something went wrong.

     # File lib/phusion_passenger/spawn_manager.rb, line 179
179:         def reload(app_root = nil)
180:                 @spawners.synchronize do
181:                         if app_root
182:                                 # Delete associated ApplicationSpawner.
183:                                 @spawners.delete("app:#{app_root}")
184:                         else
185:                                 # Delete all ApplicationSpawners.
186:                                 keys_to_delete = []
187:                                 @spawners.each_pair do |key, spawner|
188:                                         if spawner.is_a?(Railz::ApplicationSpawner)
189:                                                 keys_to_delete << key
190:                                         end
191:                                 end
192:                                 keys_to_delete.each do |key|
193:                                         @spawners.delete(key)
194:                                 end
195:                         end
196:                         @spawners.each do |spawner|
197:                                 # Reload all FrameworkSpawners.
198:                                 if spawner.respond_to?(:reload)
199:                                         spawner.reload(app_root)
200:                                 end
201:                         end
202:                 end
203:         end
spawn_application(options) click to toggle source

Spawn an application with the given spawn options. When successful, an Application object will be returned, which represents the spawned application. At least one option must be given: app_root. This is the application’s root folder.

Other options are:

‘lower_privilege’, ‘lowest_user’, ‘environment’, ‘environment_variables’, ‘base_uri’ and ‘print_exceptions‘

See Railz::ApplicationSpawner.new for an explanation of these options.

‘app_type‘

What kind of application is being spawned. Either “rails” (default), “rack” or “wsgi”.

‘spawn_method‘

May be one of “smart”, “smart-lv2” or “conservative”. When “smart” is specified, SpawnManager will internally cache the code of Rails applications, in order to speed up future spawning attempts. This implies that, if you’ve changed the application’s code, you must do one of these things:

  • Reload the application by calling reload with the correct app_root argument.

“smart” caches the Rails framework code in a framework spawner server, and application code in an application spawner server. Sometimes it is desirable to skip the framework spawning and going directly for the application spawner instead. The “smart-lv2” method allows you to do that.

Caching however can be incompatible with some applications. The “conservative” spawning method does not involve any caching at all. Spawning will be slower, but is guaranteed to be compatible with all applications.

The default spawn method is “smart-lv2”.

‘framework_spawner_timeout’ and ‘app_spawner_timeout‘

These options allow you to specify the maximum idle timeout, in seconds, of the framework spawner servers and application spawner servers that will be started under the hood. These options are only used if app_type equals “rails”.

A timeout of 0 means that the spawner server should never idle timeout. A timeout of -1 means that the default timeout value should be used. The default value is -1.

Exceptions:

  • InvalidPath: app_root doesn’t appear to be a valid Ruby on Rails application root.

  • VersionNotFound: The Ruby on Rails framework version that the given application requires is not installed.

  • AbstractServer::ServerError: One of the server processes exited unexpectedly.

  • FrameworkInitError: The Ruby on Rails framework that the application requires could not be loaded.

  • AppInitError: The application raised an exception or called exit() during startup.

     # File lib/phusion_passenger/spawn_manager.rb, line 133
133:         def spawn_application(options)
134:                 if !options["app_root"]
135:                         raise ArgumentError, "The 'app_root' option must be given."
136:                 end
137:                 options = sanitize_spawn_options(options)
138:                 
139:                 if options["app_type"] == "rails"
140:                         if !defined?(Railz::FrameworkSpawner)
141:                                 require 'phusion_passenger/application'
142:                                 require 'phusion_passenger/railz/framework_spawner'
143:                                 require 'phusion_passenger/railz/application_spawner'
144:                         end
145:                         return spawn_rails_application(options)
146:                 elsif options["app_type"] == "rack"
147:                         if !defined?(Rack::ApplicationSpawner)
148:                                 require 'phusion_passenger/rack/application_spawner'
149:                         end
150:                         return Rack::ApplicationSpawner.spawn_application(
151:                                 options["app_root"], options
152:                         )
153:                 elsif options["app_type"] == "wsgi"
154:                         require 'phusion_passenger/wsgi/application_spawner'
155:                         return WSGI::ApplicationSpawner.spawn_application(
156:                                 options["app_root"],
157:                                 options["lower_privilege"],
158:                                 options["lowest_user"],
159:                                 options["environment"]
160:                         )
161:                 else
162:                         raise ArgumentError, "Unknown 'app_type' value '#{options["app_type"]}'."
163:                 end
164:         end

Private Instance Methods

app_name(app_type) click to toggle source
     # File lib/phusion_passenger/spawn_manager.rb, line 354
354:         def app_name(app_type)
355:                 if app_type == "rails"
356:                         return "Ruby on Rails"
357:                 else
358:                         return "Ruby (Rack)"
359:                 end
360:         end
database_error?(e) click to toggle source
     # File lib/phusion_passenger/spawn_manager.rb, line 337
337:         def database_error?(e)
338:                 return ( defined?(Mysql::Error) && e.child_exception.is_a?(Mysql::Error) ) ||
339:                        ( e.child_exception.is_a?(UnknownError) &&
340:                            (
341:                                e.child_exception.real_class_name =~ /^ActiveRecord/ ||
342:                                e.child_exception.real_class_name =~ /^Mysql::/
343:                            )
344:                        )
345:         end
handle_reload(app_root) click to toggle source
     # File lib/phusion_passenger/spawn_manager.rb, line 323
323:         def handle_reload(app_root)
324:                 reload(app_root)
325:         end
handle_spawn_application(*options) click to toggle source
     # File lib/phusion_passenger/spawn_manager.rb, line 272
272:         def handle_spawn_application(*options)
273:                 options = sanitize_spawn_options(Hash[*options])
274:                 app = nil
275:                 app_root = options["app_root"]
276:                 app_type = options["app_type"]
277:                 begin
278:                         app = spawn_application(options)
279:                 rescue InvalidPath => e
280:                         send_error_page(client, 'invalid_app_root', :error => e, :app_root => app_root)
281:                 rescue AbstractServer::ServerError => e
282:                         send_error_page(client, 'general_error', :error => e)
283:                 rescue VersionNotFound => e
284:                         send_error_page(client, 'version_not_found', :error => e, :app_root => app_root)
285:                 rescue AppInitError => e
286:                         if database_error?(e)
287:                                 send_error_page(client, 'database_error', :error => e,
288:                                         :app_root => app_root, :app_name => app_name(app_type),
289:                                         :app_type => app_type)
290:                         elsif load_error?(e)
291:                                 # A source file failed to load, maybe because of a
292:                                 # missing gem. If that's the case then the sysadmin
293:                                 # will install probably the gem. So we clear RubyGems's
294:                                 # cache so that it can detect new gems.
295:                                 Gem.clear_paths
296:                                 send_error_page(client, 'load_error', :error => e, :app_root => app_root,
297:                                         :app_name => app_name(app_type))
298:                         elsif e.child_exception.is_a?(SystemExit)
299:                                 send_error_page(client, 'app_exited_during_initialization', :error => e,
300:                                         :app_root => app_root, :app_name => app_name(app_type))
301:                         else
302:                                 send_error_page(client, 'app_init_error', :error => e,
303:                                         :app_root => app_root, :app_name => app_name(app_type))
304:                         end
305:                 rescue FrameworkInitError => e
306:                         send_error_page(client, 'framework_init_error', :error => e)
307:                 end
308:                 if app
309:                         begin
310:                                 client.write('ok')
311:                                 client.write(app.pid, app.listen_socket_name,
312:                                         app.listen_socket_type)
313:                                 client.send_io(app.owner_pipe)
314:                         rescue Errno::EPIPE
315:                                 # The Apache module may be interrupted during a spawn command,
316:                                 # in which case it will close the connection. We ignore this error.
317:                         ensure
318:                                 app.close
319:                         end
320:                 end
321:         end
load_error?(e) click to toggle source
     # File lib/phusion_passenger/spawn_manager.rb, line 347
347:         def load_error?(e)
348:                 return e.child_exception.is_a?(LoadError) || (
349:                            e.child_exception.is_a?(UnknownError) &&
350:                            e.child_exception.real_class_name == "MissingSourceFile"
351:                 )
352:         end
send_error_page(channel, template_name, options = {}) click to toggle source
     # File lib/phusion_passenger/spawn_manager.rb, line 327
327:         def send_error_page(channel, template_name, options = {})
328:                 require 'phusion_passenger/html_template' unless defined?(HTMLTemplate)
329:                 require 'phusion_passenger/platform_info' unless defined?(PlatformInfo)
330:                 options["enterprisey"] = File.exist?("#{File.dirname(__FILE__)}/../../enterprisey.txt") ||
331:                         File.exist?("/etc/passenger_enterprisey.txt")
332:                 data = HTMLTemplate.new(template_name, options).result
333:                 channel.write('error_page')
334:                 channel.write_scalar(data)
335:         end
spawn_rails_application(options) click to toggle source
     # File lib/phusion_passenger/spawn_manager.rb, line 211
211:         def spawn_rails_application(options)
212:                 spawn_method = options["spawn_method"]
213:                 app_root     = options["app_root"]
214:                 
215:                 if [nil, "", "smart", "smart-lv2"].include?(spawn_method)
216:                         spawner_must_be_started = true
217:                         if spawn_method != "smart-lv2"
218:                                 framework_version = Application.detect_framework_version(app_root)
219:                         end
220:                         if framework_version.nil? || framework_version == :vendor
221:                                 key = "app:#{app_root}"
222:                                 create_spawner = proc do
223:                                         Railz::ApplicationSpawner.new(app_root, options)
224:                                 end
225:                                 spawner_timeout = options["app_spawner_timeout"]
226:                         else
227:                                 key = "version:#{framework_version}"
228:                                 create_spawner = proc do
229:                                         framework_options = { :version => framework_version }
230:                                         if options.has_key?(:print_framework_loading_exceptions)
231:                                                 framework_options[:print_framework_loading_exceptions] = options[:print_framework_loading_exceptions]
232:                                         end
233:                                         Railz::FrameworkSpawner.new(framework_options)
234:                                 end
235:                                 spawner_timeout = options["framework_spawner_timeout"]
236:                         end
237:                 else
238:                         key = "app:#{app_root}"
239:                         create_spawner = proc do
240:                                 Railz::ApplicationSpawner.new(app_root, options)
241:                         end
242:                         spawner_timeout = options["app_spawner_timeout"]
243:                         spawner_must_be_started = false
244:                 end
245:                 
246:                 @spawners.synchronize do
247:                         spawner = @spawners.lookup_or_add(key) do
248:                                 spawner = create_spawner.call
249:                                 if spawner_timeout != 1
250:                                         spawner.max_idle_time = spawner_timeout
251:                                 end
252:                                 if spawner_must_be_started
253:                                         spawner.start
254:                                 end
255:                                 spawner
256:                         end
257:                         begin
258:                                 if spawner.is_a?(Railz::FrameworkSpawner)
259:                                         return spawner.spawn_application(app_root, options)
260:                                 elsif spawner.started?
261:                                         return spawner.spawn_application
262:                                 else
263:                                         return spawner.spawn_application!
264:                                 end
265:                         rescue AbstractServer::ServerError
266:                                 @spawners.delete(key)
267:                                 raise
268:                         end
269:                 end
270:         end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.