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.
ROOT_UID | = | 0 |
The user ID of the root user. | ||
ROOT_GID | = | 0 |
The group ID of the root user. |
[R] | app_root | The application root of this spawner. |
[RW] | time | An attribute, used internally. This should not be used outside Passenger. |
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 ArgumentError will be raised.
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.
The environment argument allows one to specify the RAILS_ENV environment to use.
[ show source ]
# File lib/passenger/railz/application_spawner.rb, line 78 78: def initialize(app_root, lower_privilege = true, lowest_user = "nobody", environment = "production") 79: super() 80: begin 81: @app_root = normalize_path(app_root) 82: rescue SystemCallError => e 83: raise ArgumentError, e.message 84: rescue ArgumentError 85: raise 86: end 87: @lower_privilege = lower_privilege 88: @lowest_user = lowest_user 89: @environment = environment 90: self.time = Time.now 91: assert_valid_app_root(@app_root) 92: define_message_handler(:spawn_application, :handle_spawn_application) 93: end
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.
[ show source ]
# File lib/passenger/railz/application_spawner.rb, line 101 101: def spawn_application 102: server.write("spawn_application") 103: pid, socket_name, using_abstract_namespace = server.read 104: if pid.nil? 105: raise IOError, "Connection closed" 106: end 107: owner_pipe = server.recv_io 108: return Application.new(@app_root, pid, socket_name, 109: using_abstract_namespace == "true", owner_pipe) 110: rescue SystemCallError, IOError, SocketError => e 111: raise Error, "The application spawner server exited unexpectedly" 112: 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.
[ show source ]
# File lib/passenger/railz/application_spawner.rb, line 128 128: def spawn_application! 129: # Double fork to prevent zombie processes. 130: a, b = UNIXSocket.pair 131: pid = safe_fork(self.class.to_s) do 132: safe_fork('application') do 133: begin 134: a.close 135: channel = MessageChannel.new(b) 136: success = report_app_init_status(channel) do 137: ENV['RAILS_ENV'] = @environment 138: Dir.chdir(@app_root) 139: if @lower_privilege 140: lower_privilege('config/environment.rb', @lowest_user) 141: end 142: require 'config/environment' 143: require 'dispatcher' 144: end 145: if success 146: start_request_handler(channel) 147: end 148: rescue SignalException => e 149: if e.message != AbstractRequestHandler::HARD_TERMINATION_SIGNAL && 150: e.message != AbstractRequestHandler::SOFT_TERMINATION_SIGNAL 151: raise 152: end 153: end 154: end 155: end 156: b.close 157: Process.waitpid(pid) rescue nil 158: 159: channel = MessageChannel.new(a) 160: unmarshal_and_raise_errors(channel) 161: 162: # No exception was raised, so spawning succeeded. 163: pid, socket_name, using_abstract_namespace = channel.read 164: if pid.nil? 165: raise IOError, "Connection closed" 166: end 167: owner_pipe = channel.recv_io 168: return Application.new(@app_root, pid, socket_name, 169: using_abstract_namespace == "true", owner_pipe) 170: end
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.
[ show source ]
# File lib/passenger/railz/application_spawner.rb, line 178 178: def start 179: super 180: begin 181: unmarshal_and_raise_errors(server) 182: rescue IOError, SystemCallError, SocketError 183: stop 184: raise Error, "The application spawner server exited unexpectedly" 185: rescue 186: stop 187: raise 188: end 189: end