Skip to content

Tag Archive for Ruby on Rails

Phusion Passenger (mod_rails) Memory Management

I’ve been using Phusion Passenger (mod_rails) for about a month to run a production and staging machine with a Ruby on Rails app. So far things have been going really well. Installation was smooth and it’s been really peppy.

However, the app just got hit by a lot of people at once and stopped responding. I didn’t realize the default setting in Passenger is to spawn a max of 6 Rails instances simultaneously. The documentation recommends 2 instances for a VPS with 256MB of memory (almost exactly what I’m running). So, my server spawned the max and then ran out of memory which made it unresponsive.

I hit a couple of small walls while getting this working so I wanted to share the process.

On linux based machines you can run passenger-status to find out the number of Phusion Passenger application instances that are running.

----------- General information -----------
max      = 6
count    = 3
active   = 0
inactive = 3

----------- Applications -----------
/u/apps/myapp/releases/20080921194503:
  PID: 8792      Sessions: 0
  PID: 8789      Sessions: 0
  PID: 8784      Sessions: 0

You can see that the output show the default max value of 6. To change this number you need to edit your httpd.conf and set the PassengerMaxPoolSize option. Make sure this is outside of your VirtualHost or it won’t work… which, I obviously ran into. It should probably look something like this:

LoadModule passenger_module /usr/local/lib/ruby/gems/1.8/gems/passenger-2.0.3/ext/apache2/mod_passenger.so
PassengerRoot /usr/local/lib/ruby/gems/1.8/gems/passenger-2.0.3
PassengerRuby /usr/local/bin/ruby
PassengerMaxPoolSize 2

Once you’re done with that restart Apache and Passenger. Now, to take a look at the memory that Apache and Passenger are using run passenger-memory-stats. You’ll see something like this:

------------- Apache processes -------------
PID   PPID  Threads  VMSize   Private  Name
--------------------------------------------
5842  1     1        10.3 MB  0.2 MB   /usr/local/apache2/bin/httpd -k start
8757  5842  1        10.4 MB  0.3 MB   /usr/local/apache2/bin/httpd -k start
8784  5842  1        10.4 MB  0.2 MB   /usr/local/apache2/bin/httpd -k start
9137  5842  1        10.3 MB  0.2 MB   /usr/local/apache2/bin/httpd -k start
9141  5842  1        10.4 MB  0.2 MB   /usr/local/apache2/bin/httpd -k start
9143  5842  1        10.3 MB  0.2 MB   /usr/local/apache2/bin/httpd -k start
9147  5842  1        10.4 MB  0.3 MB   /usr/local/apache2/bin/httpd -k start
9155  5842  1        10.4 MB  0.3 MB   /usr/local/apache2/bin/httpd -k start
9163  5842  1        10.4 MB  0.3 MB   /usr/local/apache2/bin/httpd -k start
9506  5842  1        10.4 MB  0.3 MB   /usr/local/apache2/bin/httpd -k start
9510  5842  1        10.3 MB  0.2 MB   /usr/local/apache2/bin/httpd -k start
9623  5842  1        10.4 MB  0.3 MB   /usr/local/apache2/bin/httpd -k start
9629  5842  1        10.3 MB  0.2 MB   /usr/local/apache2/bin/httpd -k start
9631  5842  1        10.3 MB  0.2 MB   /usr/local/apache2/bin/httpd -k start
9633  5842  1        10.3 MB  0.2 MB   /usr/local/apache2/bin/httpd -k start
9750  5842  1        10.3 MB  0.2 MB   /usr/local/apache2/bin/httpd -k start
9752  5842  1        10.3 MB  0.2 MB   /usr/local/apache2/bin/httpd -k start
9865  5842  1        10.3 MB  0.2 MB   /usr/local/apache2/bin/httpd -k start
### Processes: 18
### Total private dirty RSS: 4.36 MB

-------- Passenger processes ---------
PID   Threads  VMSize   Private  Name
--------------------------------------
8749  20       6.0 MB   0.5 MB   /usr/local/lib/ruby/gems/1.8/gems/passenger-2.0.3/ext/apache2/ApplicationPoolServerExecutable 0 /usr/local/lib/ruby/gems/1.8/gems/passenger-2.0.3/bin/passenger-spawn-server  /usr/local/bin/ruby  /tmp/passenger_status.5842.fifo
8758  1        5.5 MB   2.9 MB   Passenger spawn server
8789  1        61.6 MB  53.7 MB  Rails: /u/apps/myapp/releases/20080921194503
8792  1        61.8 MB  53.9 MB  Rails: /u/apps/myapp/releases/20080921194503
### Processes: 4
### Total private dirty RSS: 111.03 MB

Now, that may look like a lot of Apache instances running but you can see the “Total private dirty RSS” (real memory usage of the processes) is only 4.36 MB which is nothing in the scheme of things. More importantly, the real memory that the Rails instances are taking is 111.03 MB which my server can handle. Remember, you’ll probably have MySQL and other applications running along with these processes, so don’t up the PassengerMaxPoolSize to the point of bringing your server to its knees.

Installing ImageMagick/RMagick on Leopard

I’ve heard many horror stories of developers trying to install ImageMagick/RMagick to manipulate images. Fortunately, when I needed to install RMagick to use with the attachment_fu plugin, I ran across a fantastic script at OnRails.org by Solomon White (many thanks). It gave the steps to install RMagick from source without MacPorts or Fink.

I made the following minor changes to get it working for me and posted the script below.

  • Changed ‘wget’ to ‘curl -O’
  • Updated a couple of links that weren’t working for me
  • Updated sourceforge links to the east coast
  • Updated links to latest version of source code (as of today)
#!/bin/sh
curl -O http://download.savannah.gnu.org/releases/freetype/freetype-2.3.5.tar.gz
tar xzvf freetype-2.3.5.tar.gz
cd freetype-2.3.5
./configure --prefix=/usr/local
make
sudo make install
cd ..

curl -O http://superb-east.dl.sourceforge.net/sourceforge/libpng/libpng-1.2.23.tar.bz2
tar jxvf libpng-1.2.23.tar.bz2
cd libpng-1.2.23
./configure --prefix=/usr/local
make
sudo make install
cd ..

curl -O http://www.ijg.org/files/jpegsrc.v6b.tar.gz
tar xzvf jpegsrc.v6b.tar.gz
cd jpeg-6b
ln -s `which glibtool` ./libtool
export MACOSX_DEPLOYMENT_TARGET=10.5
./configure --enable-shared --prefix=/usr/local
make
sudo make install
cd ..

curl -O ftp://ftp.remotesensing.org/libtiff/tiff-3.8.2.tar.gz
tar xzvf tiff-3.8.2.tar.gz
cd tiff-3.8.2
./configure --prefix=/usr/local
make
sudo make install
cd ..

curl -O http://superb-east.dl.sourceforge.net/sourceforge/wvware/libwmf-0.2.8.4.tar.gz
tar xzvf libwmf-0.2.8.4.tar.gz
cd libwmf-0.2.8.4
make clean
./configure
make
sudo make install
cd ..

curl -O http://www.littlecms.com/lcms-1.17.tar.gz
tar xzvf lcms-1.17.tar.gz
cd lcms-1.17
make clean
./configure
make
sudo make install
cd ..

curl -O http://superb-east.dl.sourceforge.net/sourceforge/ghostscript/ghostscript-8.61.tar.gz
tar zxvf ghostscript-8.61.tar.gz
cd ghostscript-8.61/
./configure  --prefix=/usr/local
make
sudo make install
cd ..

curl -O http://mirror.cs.wisc.edu/pub/mirrors/ghost/GPL/current/ghostscript-fonts-std-8.11.tar.gz
tar zxvf ghostscript-fonts-std-8.11.tar.gz
sudo mv fonts /usr/local/share/ghostscript

curl -O ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick-6.3.7-1.tar.gz
tar xzvf ImageMagick-6.3.7-1.tar.gz
cd ImageMagick-6.3.7
export CPPFLAGS=-I/usr/local/include
export LDFLAGS=-L/usr/local/lib
./configure --prefix=/usr/local --disable-static --with-modules --without-perl --without-magick-plus-plus --with-quantum-depth=8 --with-gs-font-dir=/usr/local/share/ghostscript/fonts
make
sudo make install
cd ..

To test that ImageMagick was installed properly you can check the version number.

convert -version

[you should see something like this]
Version: ImageMagick 6.3.7 11/26/07 Q8 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2007 ImageMagick Studio LLC

To test that ImageMagick is working properly you can run the following test. It will create a copy of the ImageMagick logo.

convert logo: logo.gif

The newest member of the crew

Glenn Fu & Aldenta

For years my brother, Glenn, and I have talked about working together. I’m ecstatic to say that it finally happened! Glenn started his own business, Glenn Fu, and we’re teaming up on projects. He’s a strong developer and getting his feet wet in the small-business/freelance world. Right away, he picked up Ruby on Rails and is making sure our code is solid by keeping us on track with BDD and using RSpec. It’s extremely refreshing working with Glenn who makes me challenge my business process and development style. Every business could use that type of shot in the arm.

I’m so happy working with you Bro!

has_many :through Self-referential Example

While using an association table for the first time with Ruby On Rails I had a bit of trouble finding an easy to understand example of has_many :through. I needed to build a self-referencing table where People could have other People as friends through a Friendship. I was getting “stack too deep” errors, “could not find the association” errors, and also crashing Webrick before I figured out the correct setup. Here’s how I did it:

# the people table
create_table :people do |t|
  t.column :name, :string
end

# the friendships association table
create_table :friendships do |t|
  t.column :person_id, :integer
  t.column :friend_id, :integer
  t.column :authorized, :boolean, :default => false
end

class Friendship < ActiveRecord::Base
  # don't have to give class_name or foreign_key b/c ActiveRecord reflection works here
  belongs_to :person

  # make sure to give class_name and foreign_key b/c ActiveRecord doesn't know what friend is
  belongs_to :friend, :class_name => "Person", :foreign_key => "friend_id"
end

class Person < ActiveRecord::Base
  # tell ActiveRecord that a person has_many friendships or :through won't work
  has_many :friendships

  # create the has_many :through relationship
  has_many :friends, :through => :friendships

  # an example of how to get only the authorized friends
  has_many :authorized_friends, :through => :friendships, :source => :friend, :conditions => [ "authorized = ?", true ]

  # an example of how to get only the unauthorized friends
  has_many :unauthorized_friends, :through => :friendships, :source => :friend, :conditions => [ "authorized = ?", false ]
end

Execute Rails Code Before the View is Rendered

At one time, I was looking for a way to execute code for every action in my controller before the view was rendered. I saw this come up again yesterday in the #rubyonrails IRC channel so I looked at it a bit more. Currently, Rails provides a before_filter method which will “run before actions on this controller are performed” or an after_filter which will “run after actions on this controller are performed.” What I wanted was a before_render filter which would run right at the end of the action but before render/view was executed.

Here is one idea how to do this. Since you want the code to run right before render is executed just override the render method that’s in ActionController. You can put the following code inside any of your individual controllers or add it to your ApplicationController and it will run in all of your controllers.

protected
  def render(options = nil, deprecated_status = nil, &block)
    # your code goes here
    @rockon = "rock and roll!"

    # call the ActionController::Base render to show the page
    super
  end

This is just an example, and maybe someone will take this a step further and actually create a before_render filter plugin. It would be nice to have the same flexibility as before_filter and after_filter which allows you to include “:only” or “:exclude” certain actions.

Writing a Custom FormBuilder in Rails

I’m currently working on a Ruby on Rails application where I need lots of text fields that have the same properties. I decided to override text_field and have it output all of the extra attribues automatically. This helps keep the view code cleaner, lets me change all of these text fields in one place, and also helps me avoid typing mistakes (at least in this section of the code). Thanks to Ruby on Rails core team member Rick Olson (aka technoweenie) for pointing me to his LabeledFormHelper plugin, which taught me this technique.

Before the custom FormBuilder my initial view code looked something like:

<% form_for(:spreadsheet, @spreadsheet, :url => { :action => 'create' }) do |s| %>

    <% fields_for :numbers, @spreadsheet.numbers do |f| %>

      <%= f.text_field :field1, :onkeypress => 'return isNumberKey(event);', :maxlength => 3 %>
      <%= f.text_field :field2, :onkeypress => 'return isNumberKey(event);', :maxlength => 3 %>

      ... and so on

    <% end %>
<% end %>

So I created a custom FormBuilder in my helper to do the extra text_field work for me:

module SpeadsheetsHelper

  # tell all of these methods to use my custom FormBuilder
  [:form_for, :fields_for, :form_remote_for, :remote_form_for].each do |meth|
    src = <<-end_src
      def speadsheets_#{meth}(object_name, *args, &proc)
        options = args.last.is_a?(Hash) ? args.pop : {}
        options.update(:builder => SpeadsheetsFormBuilder)
        #{meth}(object_name, *(args << options), &proc)
      end
    end_src
    module_eval src, __FILE__, __LINE__
  end

  # the custom FormBuilder
  class SpeadsheetsFormBuilder < ActionView::Helpers::FormBuilder    

    # add onkeypress and set maxlength of field to 3 to all text fields
    def text_field(method, options={})
      super(method, options.merge(:onkeypress => 'return isNumberKey(event);', :maxlength => 3))
    end

  end
end

Now my view code is much simpler and helps avoid goofups:

<% spreadsheets_form_for(:spreadsheet, @spreadsheet, :url => { :action => 'create' }) do |s| %>

    <% spreadsheets_fields_for :numbers, @spreadsheet.numbers do |f| %>

      <%= f.text_field :field1 %>
      <%= f.text_field :field2 %>

      ... and so on

    <% end %>
<% end %>

Notice in the view that instead of using form_for and fields_for you need to use the custom methods spreadsheets_form_for and spreadsheets_fields_for which were created in SpeadsheetsHelper.

Plugin: Bookmark A Page In Your PDF

With the long, yet wonderful, books in PDF format these days (Agile Web Development with Rails by Dave Thomas and David Heinemeier Hansson with Leon Breedt, Mike Clark, Thomas Fuchs, and Andreas Schwarz [570 pages]; Programming Ruby by Dave Thomas, with Chad Fowler and Andy Hunt [864 pages]) there needs to be a better way to digitally bookmark where you left off. For some reason Adobe Reader (Acrobat Reader) doesn’t have this capability built in. I found a plugin for Acrobat Reader to do multiple bookmarks per document from PDF Hacks. However, I really just want it to be simple like a physical book – you bookmark the page you’re on when you stop and go to that page when you pick back up. So here is my plugin to bookmark your page in Adobe Reader.

Download:

PDF Bookmark 1.2

Change Log:

  • 1.2 – Nov 30, 2008 – Use documentFileName on PDFs that don’t have a docID. Add French translation (TitCouille). Add installation paths for Linux (TitCouille).
  • 1.1 – Dec 20, 2007 – Update to work with Adobe Reader 8 due to security changes (app.trustedFunction)
  • 1.0 – Sep 15, 2006 – Initial release

Installation:

Adobe Reader 9 – copy pdf_bookmark.js to:

  • Mac: ~/Library/Application Support/Adobe/Acrobat/9.0_x86/JavaScripts (or similar)
  • Linux: /home/[User Name]/.adobe/Acrobat/9.0/JavaScripts/
  • Windows XP: C:\Documents and Settings\[User Name]\Application Data\Adobe\Acrobat\9.0\JavaScripts (or similar)
  • Windows 7 & Vista: C:\Users\[User Name]\AppData\Roaming\Adobe\Acrobat\9.0\JavaScripts

Adobe Reader 8 – copy pdf_bookmark.js to:

  • Mac: ~/Library/Acrobat User Data/8.0_x86/JavaScripts (or similar)
  • Linux: /home/[User Name]/.adobe/Acrobat/8.0/JavaScripts/
  • Windows: C:\Documents and Settings\[User Name]\Application Data\Adobe\Acrobat\8.0\JavaScripts (or similar)

Adobe Reader 7 – copy pdf_bookmark.js to:

  • Mac: ~/Library/Acrobat User Data/7.0/JavaScripts (or similar)
  • Linux: /home/[User Name]/.adobe/Acrobat/7.0/JavaScripts/
  • Windows: C:\Documents and Settings\[User Name]\Application Data\Adobe\Acrobat\7.0\JavaScripts (or similar)

Note: you may have to create the JavaScripts folder if it doesn’t exist. Thanks for all of the comments and emails about working systems and folder!

Usage:

Open your document in Adobe Reader. Then choose Tools -> Bookmark from the menu. “Bookmark This Page” adds the bookmark. “Go To Bookmark” sends you to the bookmark you created. It remembers which PDF document you bookmarked so it works on all of your PDFs simultaneously. (Tested with Acrobat Reader 7, Adobe Reader 8 (thanks Michael) and Adobe Reader 9 on Mac OS X, Linux (thanks TitCouille) and Windows)

PDF Bookmark Plugin Screenshot (8.0)