Home > Uncategorized > Passenger Resource Collision

Passenger Resource Collision

01/06/2011

Passenger and Smart Spawn Mode
Phusion Passenger has an intelligent application server worker pool based on Apache for hosting Rails apps. It uses a clever forking process spawner with Ruby Enterprise Edition to Pre-load your Rails application and environment in a parent process before forking off Child workers.

This allows you to load your application once for N workers, speeding up startup time and reducing memory usage dramatically with REE copy-on-write support.

Preventing Resource Collision
However, there is a hidden risk in forking children this way. Each child inherits an initialized Rails environment from the parent, which can cause resource duplication or collision. Passenger resets the database connection out of the box – each Rails worker has it’s own ActiveRecord connection pool.

However, other resources like Redis or Memcache are NOT reset or protected this way. When deploying a production scale application it’s essential to implement this. Otherwise, you could have each of 8+ workers on one server trying to re-use the same connection or file-handle.

PatientsLikeMe solution – Rails Initializer to Reset Resources

require 'passenger_safe_resource'

# Initialize Redis
PassengerSafeResource.load('redis') do
  env = ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
  redis_conf = YAML.load_file("config/redis.yml")[env]
  fail "No configuration for #{env}" unless redis_conf
  host, port, db = redis_conf.split(':')

  REDIS = Redis.new(:host => host, :port => port, :thread_safe => true, :db => db)
end

# Reset Vanity Redis Connection
PassengerSafeResource.load('vanity') do
  Vanity.playground.reconnect!
end

# Reset Memcached Connection
PassengerSafeResource.load('memcache') do
  PassengerSafeResource.reset_memcache(Rails.cache)
end

Helper to Manage Passenger events

module PassengerSafeResource
  # Helper to reset memcache connection
  def self.reset_memcache(cache)
    return unless cache.kind_of?(ActiveSupport::Cache::MemCacheStore)

    cache.instance_variable_get(:@data).reset
  end

  # Helper to load/reset a resource with Passenger
  def self.load(resource_name, &block)
    if defined?(PhusionPassenger)
      PhusionPassenger.on_event(:starting_worker_process) do |forked|
        if forked
          Rails.logger.info "PassengerSafeResource(#{resource_name}): Forking Child, loading in child"
          yield
        else
          Rails.logger.info "PassengerSafeResource(#{resource_name}): Non-Forking Spawn, NOOP"
        end
      end
    else
      Rails.logger.info "PassengerSafeResource(#{resource_name}): Non-Passenger mode, loading resource"
      yield
    end
  end
end
Advertisements
Categories: Uncategorized Tags:
  1. No comments yet.
  1. 01/06/2011 at 6:27 pm
Comments are closed.
%d bloggers like this: