migrating from mongrel to passenger

Posted by chad on July 04, 2008

We migrated our main production server from mongrel to passenger today. Here are the things we learned from the process:

  • Don’t forget to set the apache APXS2 variable - the error won’t show up until you try to start apache after thinking that you successfully installed passenger. You will receive an error like this:
httpd: Syntax error on line 54 of /usr/local/apache2/conf/httpd.conf: API module structure 'passenger_module' in
file /usr/local/passenger/passenger-2.0.1/ext/apache2/mod_passenger.so is garbled - expected signature 41503232 but saw 41503230 -perhaps this is not an Apache module DSO, or was compiled for a different Apache version?
  • You can remove all of the rewrite rules from the apache conf file, but keep this part:
  
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
  
  • You’ll need to keep any apache rewrite rules related to displaying a ‘maintenance’ file.

What follows is the old mongrel apache configuration file, and the new one:

NameVirtualHost 10.x.x.x:80


  ServerName myserver

  DocumentRoot /var/www/apps/myapp/current/public

  
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
  

  # Configure mongrel_cluster
  
    BalancerMember http://127.0.0.1:8300
    BalancerMember http://127.0.0.1:8301
    BalancerMember http://127.0.0.1:8302
    BalancerMember http://127.0.0.1:8303
    BalancerMember http://127.0.0.1:8304
    BalancerMember http://127.0.0.1:8305
  

  RewriteEngine On

  # Prevent access to .svn directories
#  RewriteRule ^(.*/)?.svn/ - [F,L]
#  ErrorDocument 403 “Access Forbidden”

  # Check for maintenance file and redirect all requests
  RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
  RewriteCond %{SCRIPT_FILENAME} !maintenance.html
  RewriteRule ^.*$ /system/maintenance.html [L]

  # Rewrite index to check for static
  RewriteRule ^/$ /index.html [QSA]

  # Rewrite to check for Rails cached page
  RewriteRule ^([^.]+)$ $1.html [QSA]

  # Redirect all non-static requests to cluster
  RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
  RewriteRule ^/(.*)$ balancer://myapp_cluster%{REQUEST_URI} [P,QSA,L]

  # Deflate
  AddOutputFilterByType DEFLATE text/html text/plain text/xml
  BrowserMatch ^Mozilla/4 gzip-only-text/html
  BrowserMatch ^Mozilla/4.0[678] no-gzip
  BrowserMatch bMSIE !no-gzip !gzip-only-text/html

  ErrorLog /var/log/apache2/myapp.com-error_log
  CustomLog /var/log/apache2/myapp.com-access_log combined

To this with passenger apache configuration file:

NameVirtualHost 10.x.x.x:80

  ServerName myapp

  DocumentRoot /var/www/apps/myapp/current/public

  
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
  

  # Check for maintenance file and redirect all requests
  RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
  RewriteCond %{SCRIPT_FILENAME} !maintenance.html
  RewriteRule ^.*$ /system/maintenance.html [L]

ErrorLog /var/log/apache2/myapp.com-error_log
CustomLog /var/log/apache2/myapp.com-access_log combined

acts_as_setting - ruby dynamic setting class

Posted by chad on June 30, 2008

In any large software project there is a concept of setting or configuration. In .NET, there is the System.Configuration class. Rails doesn’t have anything like this, but Most of the time, I have some need to roll my own class for this I recently have been using a setting class I created that loads settings from the database and stores them as constants on a class called ‘Setting’.

My goals were:

1. No repeated hits to the database just to access a property, except occasional reloads for an update to a setting in the database.

2. Runtime errors if a setting doesn’t exist. No returning ‘nil’ if a setting is mistyped.

3. Ability to update settings across multiple machines, in a reasonable amount of time. In this case, five minutes is reasonable.

This code is in lib/setting.rb (i’m still not sure why but i couldn’t get this to work when inside the main setting.rb file in the model directory):

class Setting

  def self.load_settings
    Setting.find(:all).each do |s|
      logger.debug "Loaded Setting: #{s.name}: #{s.value}"
      Setting.instance_eval do
        isnum = ((s.value.to_i.to_s == s.value.to_s) rescue false)
        eval "def #{s.name}\n#{s.value}\nend" if isnum
        eval "def #{s.name}\n\"#{s.value}\"\nend" unless isnum
      end
    end
  end

  self.load_settings

end

Here’s the super simple migration in the pre-rails-2.0 migration format:

class CreateSettings < ActiveRecord::Migration
  def self.up

    create_table "settings", :force => true do |t|
      t.column "name", :string, :limit => 50, :default => "", :null => false
      t.column "value", :string, :default => "", :null => false
    end

    add_index "settings", ["name"], :name => "settings_name_index"
  end

  def self.down
    drop_table :settings
  end
end

Here’s the ‘main’ setting class:

class Setting < ActiveRecord::Base

  serialize :value

end

In environment.rb, in order to reload the settings every five minutes:

require 'thread'
Thread.new { loop {sleep(300); Setting.load_settings rescue nil } }

php endswith

Posted by chad on April 30, 2008

i do work in php as well, grudgingly, and i found myself missing ends_with? today in ruby. seeing this activesupport port for php post makes me miss ruby even more. as most of the commenters point out, it’s lipstick on a pig.

Dynamic Instantiation of Property Accessors 1

Posted by chad on March 24, 2008

Jay Fields describes a scenario for using dynamic method definition instead of method missing. Today i think i found a reason to use both method missing and dynamic method definition, for both speed and safety.

I have an ActiveRecord class that uses a serialized Hash as one of its parameters. There are about a dozen keys in that hash that are most frequently used, however there may be others. I want those most frequently used keys to be exposed as methods on the containing object like this:


class Query < ActiveRecord::Base

DYNAMIC_METHODS = [:my_key1, :my_key2, :my_other_key_exposed_as_method ]

serialize :params

def method_missing(methodname, *args)
if self.class.methods.include?(methodname)
super
elsif DYNAMIC_METHODS.include?(methodname.to_sym)
_build_dynamic_fields
self.send(methodname, *args)
else
super
end
end

# dynamically alias these methods to retrieve their results from the
# params object
def initialize(initparams=nil)
super
end

# called if a caller ever requests one of the dynamic methods above
def _build_dynamic_fields()
DYNAMIC_METHODS.each() do |m|
(class << Query;Query; end).class_eval do
define_method m do |*args|
return nil if params.nil?
self.send(’params’).send(’[]’, m.to_sym) rescue nil
end
end
end
end

Samsung SyncMaster 245BW

Posted by chad on March 17, 2008

Samsung SyncMaster 245BWHighly recommended monitor - just bought yesterday for $419.00, and the picture quality and ability to run 1920×1200 is just incredible. Also, this post on FutileShop shows how to get it to rotate into portrait orientation.

no mod_ruby is damaging Ruby’s viability 1

Posted by chad on February 14, 2008

Peter Cooper’s post on the lack of a ‘mod_ruby’ is dead on. I am working on a site with millions of hits per month and i couldn’t even begin to credibly suggest that we do any ‘production’ work in ruby.

It is not (just) that Rails isn’t reliable enough. Deployment isn’t brain dead enough. You have to run mongrel, tune the number of instances, then run monit and have it restarting mongrel instances. Then have something or someone watching monit when it fails to restart mongrel every month or so.

Deploying rails requires a significant amount of brainpower from an above average engineer to design the deployment. PHP requires only a rudimentary understanding of FTP.

Math.max or why is there no integer maximum function in ruby

Posted by chad on December 07, 2007

There is no Math.max function. Instead, use an array:

>> [2,3,4].max
=> 4

I would argue there should be, because it is not intuitively obvious that this type of integer comparison would live off of an array. One argument could be that the array approach allows for non-integer types to be compared, and that the increase in flexibility has an increase in obscurity as its side-effect…

>> ["chicken","fox"].max
=> "fox"

parsing excel files in ruby - the parseexcel gem 9

Posted by chad on November 28, 2007

Highly recommended gem does exactly what it says. Here’s the code to turn an entire worksheet from an excel doc into a multi-dimensional array:

wb = Spreadsheet::ParseExcel.parse(filename)
rows = wb.worksheet(worksheet).map() { |r| r }.compact
grid = rows.map() { |r| r.map() { |c| c.to_s('latin1')}.compact rescue nil }

Just ‘gem install parseexcel’ (though i installed from source before i realized it was a pre-packaged gem).

Starting a thread in environment.rb - don’t do it!

Posted by chad on November 15, 2007

I have a project that requires a standalone server for background processing. I could have used backgroundrb but it seems like overkill, and it has this comment on the documentation page, which is a year old, and which tells me the gem is not in active development.

WARNING: start/stop/restart is broken in 0.2.1, please use the server script directly until we have figured out the issue.
So, in development mode, rather than start a standalone server, I put this code in development.rb:

Thread.new do
Server.start()
end

For reasons i’ve yet to determine, because the thread is started in the initialization code, the file include logic doesn’t work properly. Now, this could be a problem with DRb, which is how the server talks with the rest of the app, or it could be a problem with the file include logic in rails. Either way, the first attempt to contact the server works, and on the second attempt to contact the server, we get this error:

A copy of Server has been removed from the module tree but is still active!

For now, I’m just running the server using script/runner in a separate process, and it works fine.

really really long regular expressions - don’t do it!

Posted by chad on November 08, 2007

For one of my projects, we have a list of around 100 domains in a ‘blacklist’. Because of the way the blacklist works, we need to allow non-programmers to enter sites using simple wildcards. Then I convert that into a regular expression:

Regexp.new("^#{regexp.gsub(".","\\.").gsub("*",".*")}$",Regexp::IGNORECASE)

When spidering sites, if a site we find is in this list, we exclude it. I was building a regular expression of all these sites and ‘unioning’ them all together into one mega-regular expression. Then, to see if a domain is on the ‘blacklist’, i just do:

domain =~ my_mega_regex

However, what I found is that a regular expression longer than about 159 separate predicates causes ruby to segfault. This happens on ruby 1.8.4 and 1.8.5. Here’s the simplist code I can repro this with:

r=Regexp.new(/^$/);1.upto(1000) { |i| r= Regexp.union(r,Regexp.new("^#{i}$"));puts i unless "foo" =~ r }