Class: PhusionPassenger::Railz::FrameworkSpawner

Inherits:
AbstractServer show all
Includes:
Utils
Defined in:
lib/phusion_passenger/railz/framework_spawner.rb

Overview

This class is capable of spawning Ruby on Rails application instances quickly. This is done by preloading the Ruby on Rails framework into memory, before spawning the application instances.

A single FrameworkSpawner instance can only hold a single Ruby on Rails framework version. So be careful when using FrameworkSpawner: the applications that you spawn through it must require the same RoR version. To handle multiple RoR versions, use multiple FrameworkSpawner instances.

FrameworkSpawner uses ApplicationSpawner internally.

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

Defined Under Namespace

Classes: Error

Constant Summary

Constants inherited from AbstractServer

AbstractServer::SERVER_TERMINATION_SIGNAL

Instance Attribute Summary

Attributes inherited from AbstractServer

#last_activity_time, #max_idle_time, #next_cleaning_time

Instance Method Summary collapse

Methods inherited from AbstractServer

#server_pid, #start_synchronously, #started?, #stop

Constructor Details

#initialize(options = {}) ⇒ FrameworkSpawner

Creates a new instance of FrameworkSpawner.

Valid options are:

  • :version: The Ruby on Rails version to use. It is not checked whether this version is actually installed.

  • :vendor: The directory to the vendor Rails framework to use. This is usually something like “/webapps/foo/vendor/rails”.

  • :print_framework_loading_exceptions: Whether exceptions that have occurred while loading the Ruby on Rails framework should be printed to STDERR. The default is true.

It is not allowed to specify both version and vendor.

All other options will be passed on to ApplicationSpawner and RequestHandler.

Note that the specified Rails framework will be loaded during the entire life time of the FrameworkSpawner server. If you wish to reload the Rails framework’s code, then restart the server by calling AbstractServer#stop and AbstractServer#start.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/phusion_passenger/railz/framework_spawner.rb', line 72

def initialize(options = {})
  if !options.respond_to?(:'[]')
    raise ArgumentError, "The 'options' argument not seem to be an options hash"
  end
  @version = options[:version]
  @vendor  = options[:vendor]
  if options.has_key?(:print_framework_loading_exceptions)
    @print_framework_loading_exceptions = options[:print_framework_loading_exceptions]
  else
    @print_framework_loading_exceptions = true
  end
  if !@version && !@vendor
    raise ArgumentError, "Either the 'version' or the 'vendor' option must specified"
  elsif @version && @vendor
    raise ArgumentError, "It is not allowed to specify both the 'version' and the 'vendor' options"
  end
  
  super()
  self.max_idle_time = DEFAULT_FRAMEWORK_SPAWNER_MAX_IDLE_TIME
  define_message_handler(:spawn_application, :handle_spawn_application)
  define_message_handler(:reload, :handle_reload)
end

Instance Method Details

#reload(app_root = nil) ⇒ Object

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 by a FrameworkSpawner. 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:

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

  • FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly.



204
205
206
207
208
209
210
211
212
# File 'lib/phusion_passenger/railz/framework_spawner.rb', line 204

def reload(app_root = nil)
  if app_root.nil?
    server.write("reload")
  else
    server.write("reload", app_root)
  end
rescue SystemCallError, IOError, SocketError
  raise Error, "The framework spawner server exited unexpectedly"
end

#spawn_application(app_root, options = {}) ⇒ Object

Spawn a RoR application using the Ruby on Rails framework version associated with this FrameworkSpawner. When successful, an Application object will be returned, which represents the spawned RoR application.

All options accepted by ApplicationSpawner.new and RequestHandler.new are accepted.

FrameworkSpawner will internally cache the code of 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:

  • Restart this FrameworkSpawner by calling AbstractServer#stop, then AbstractServer#start.

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

Raises:

  • AbstractServer::ServerNotStarted: The FrameworkSpawner server hasn’t already been started.

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

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

  • ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly.

  • FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly.



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/phusion_passenger/railz/framework_spawner.rb', line 150

def spawn_application(app_root, options = {})
  assert_valid_app_root(app_root)
  options = sanitize_spawn_options(options)
  options["app_root"] = app_root
  # No need for the ApplicationSpawner to print exceptions. All
  # exceptions raised by the ApplicationSpawner are sent back here,
  # so we just need to decide here whether we want to print it.
  print_exceptions = options["print_exceptions"]
  options["print_exceptions"] = false
  
  begin
    server.write("spawn_application", *options.to_a.flatten)
    result = server.read
    if result.nil?
      raise IOError, "Connection closed"
    end
    if result[0] == 'exception'
      e = unmarshal_exception(server.read_scalar)
      if print_exceptions && e.respond_to?(:child_exception) && e.child_exception
        print_exception(self.class.to_s, e.child_exception)
      elsif print_exceptions
        print_exception(self.class.to_s, e)
      end
      raise e
    else
      pid, listen_socket_name, socket_type = server.read
      if pid.nil?
        raise IOError, "Connection closed"
      end
      owner_pipe = server.recv_io
      return Application.new(app_root, pid, listen_socket_name,
        socket_type, owner_pipe)
    end
  rescue SystemCallError, IOError, SocketError => e
    raise Error, "The framework spawner server exited unexpectedly"
  end
end

#startObject

Overrided from AbstractServer#start.

May raise these additional exceptions:

  • FrameworkInitError: An error occurred while loading the specified Ruby on Rails framework.

  • FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/phusion_passenger/railz/framework_spawner.rb', line 100

def start
  super
  begin
    result = server.read
    if result.nil?
      raise Error, "The framework spawner server exited unexpectedly."
    else
      status = result[0]
    end
    if status == 'exception'
      child_exception = unmarshal_exception(server.read_scalar)
      stop
      if @version
        message = "Could not load Ruby on Rails framework version #{@version}: " <<
          "#{child_exception.class} (#{child_exception.message})"
      else
        message = "Could not load Ruby on Rails framework at '#{@vendor}': " <<
          "#{child_exception.class} (#{child_exception.message})"
      end
      options = { :vendor => @vendor, :version => @version }
      if @print_framework_loading_exceptions
        print_exception(self.class.to_s, child_exception)
      end
      raise FrameworkInitError.new(message, child_exception, options)
    end
  rescue IOError, SystemCallError, SocketError
    stop
    raise Error, "The framework spawner server exited unexpectedly"
  end
end