Rails caching + Phusion Passenger Smart Spawning
If your web app runs on Rails and you deploy with Phusion Passenger, you are probably aware of the issues inherent in smart spawning. When smart spawning is on, each new spawned process shares file descriptors with the parent process. This means that if your app has a persistent connection to a server (say, memcached), whenever Passenger spawns off another instance you HAVE to close and reopen any connections.
If you use Rails’ integrated MemCacheStore support for memcached, there isn’t a “clean” way to reset the connection. Your two solutions are either to monkey patch ActiveSupport::Cache::MemCacheStore or to use instance_variable_get to access the underlying memcache object. The latter solution is slightly cleaner than the first (I suppose…) so I’ll run you through it.
First of all, in config/environment.rb, let’s add the boilerplate code to register a block to be executed when a new worker process is spawned:
if defined?(PhusionPassenger)
PhusionPassenger.on_event(:starting_worker_process) do |forked|
if forked
# Magic goes here
else
# No need to do anything.
end
end
end
You should already be familiar with that from the Passenger docs, but I include it for completeness. What we really need is the magic line that goes in that block to reset Rails’ connection to memcache. That line is:
Rails.cache.instance_variable_get(:@data).reset if Rails.cache.class == ActiveSupport::Cache::MemCacheStore
Just pop that in under
if forked
and it will take care of resetting the memcache connection for you. Why does this work? The @data instance variable is the memcache connection object in MemCacheStore. It has a reset method. instance_variable_get is a method that will return instance variables even if there isn’t an accessor for them. Pretty simple!
I’ve submitted a patch to the Rails team, and hopefully they will deem it fit to be added into Rails. If they do, Rails.cache.reset will be all you have to put there! It does have flaws — if they change the implementation of the MemCacheStore class, this line will have to be changed as well. But until a true reset method is added, this is probably the best solution.