Changing to thin from mongrel

Thin is getting some attention, so I thought we would give it a try.

Installation is just a matter of gem install thin.

Run it with something like

thin -e production -s 6

That’s 6 servers running on 0.0.0.0:3000 to 0.0.0.0:3005

Look at the examples if you need to make a monit recipe.

One thing we have is some code to make individual log files for each server instance. This is how it was with mongrel - we put this code in environment.rb inside the Rails::Initializer block:

if ENV['RAILS_ENV'] == 'production'
  if defined?(Mongrel::HttpServer)
    ObjectSpace.each_object(Mongrel::HttpServer) {|i| @port = i.port}
    @port = "unknown" unless @port && @port.to_i > 0
    config.logger = Logger.new(File.expand_path(
      RAILS_ROOT+"/log/#{ENV['RAILS_ENV']}.#{@port}.log"), 2, 25000000)
  end
end

Somthing very similar will work with thin:

if ENV['RAILS_ENV'] == 'production'
  if defined?(Thin::Server)
    ObjectSpace.each_object(Thin::Server) {|i| @port = i.backend.port}
    @port = "unknown" unless @port && @port.to_i > 0
    config.logger = Logger.new(File.expand_path(
      RAILS_ROOT+"/log/#{ENV['RAILS_ENV']}.#{@port}.log"), 2, 25000000)
  end
end

With this code in place you will get individual log files named production.3000.log, production.3001.log etc.

Finally we were seeing these errors:

terminate called after throwing an instance of 'std::runtime_error'
  what():  unable to delete epoll event: Bad file descriptor

This is a known problem not with thin, but with EventMachine. Grab an updated gem like this:

gem install eventmachine --source http://code.macournoyer.com

Getting close to the database #2 - columns_as_array plugin

Sometimes you just don’t want to instantiate a bunch of ActiveRecord objects for getting some simple information from the database. You might save on memory and it’ll be faster.

I made a very simple plugin that enables you to get all the values for one column in a table with a simple class method named according to the column - so for instance you can just say People.first_names and you will get all of them in a hash - key is the id, and value is the first_name.

View it here - and install with:

script/plugin install http://pennysmalls.com/rails_plugins/column_as_array

Here’s the README:

ColumnAsArray
=============

This extension allows you to get all the values for a particular column from a table in one hash with a simple call using the column’s name in plural. The resulting hash keys are the ids, and the values are the column values.

The idea is that in cases were only one column of data is needed from a table, but all or nearly all items in the table need to be read - perhaps for listing purposes - then we don’t need to instantiate a whole bunch of ActiveRecord objects just for this. This can save a great deal of heap space if there are many columns in a table.

Example
=======

>> Widget.names
=> {7135=>"Big widget", 33=>"Old widget", 100=>"Fast widget"
>> Thing.updates_ats
=> {2865=>"2008-02-04 09:57:55", 2344=>"2008-01-31 10:24:31", 1823=>nil, 260=>nil}

We use singularize to get the column name from the method name, and this works so that column names that are already plural need to have an extra ’s’:

>> Client.format_choicess
=> {7135=>"csv,xml", 33=>"xml", 100=>"csv,xls", 2110=>"csv", 167=>"xml"}

Getting close to the database in Rails

Sometimes ActiveRecord wraps your data up too much, and you don’t want or need all that convenient but processor-cycle consuming abstraction.

We had a case where we wanted to delete nearly 3 million records from our database. The conditions for deletion were a little complex, and writing a single SQL query for it was not practical.

So it’s nice to do it in Ruby, but instantiating 3m ActiveRecord objects is just not an option. Well, we didn’t need to - you can get data more directly from the database by talking directly to the (mysql in our case) connection.

Here’s how to get a list of ids from the database that were last updated last year.


result = ModelClass.connection.execute("SELECT id FROM table
            WHERE updated_at < '2008-01-01'")
ids = []

result.each {|row|  ids << row[0].to_i}

Then you can go through the ids and do what you like with them, perhaps compare them to other lists of ids from other tables (which is what we did).

Deleting the records is easy, just use ModelClass.delete(the_id) which works without instantiating an object. Use with care!

Breakage / Fixage in Rails 2.0.2

Apart from well known stuff like start_form_tag being deprecated, these things broke for us with Rails 2.0.2:

1. Super is no longer called in tests. Use setup_with_fixtures instead, which will work in rails 2.0.2 and future versions where the bug is fixed.

2. The paths to partials used by ActionMailer have changed - now we must use “controllername/partialname” as opposed to “../controllername/partialname”

3. The handling of plusses in urls has changed. It probably changed in this changeset. The result is that passing a + in a url path no longer gets translated to a space in the handling done by rails. So now we have correct handling according to RFC2396.

How to find image sizes in rails without image science

We have a nice new server which runs 64 bit fedora linux. It’s quick and good. But alas FreeImage won’t compile on this architecture. So bang goes any chance of using Image science.

But really I am only interested in finding the dimensions of my images. And it shouldn’t be hard. I found this post about using the gd library to resize images. Well I don’t need to resize images, but I thought it was a good approach, so I hacked it to do what I wanted.

Here is my code (credit to Damien Tanner for the original code). Very short and simple, and you just need the gd library installed together with the header files. On my system ‘yum install gd-devel’ did the trick.

if !ENV['HOME']
  ENV['INLINEDIR'] = RAILS_ROOT + "/tmp"
end

require 'inline'

class ImageInfo
  SUPPORTED_FORMATS = %w(jpg jpeg png gif)

  def initialize(filename, type=nil)
    @filename = filename
    @type = SUPPORTED_FORMATS.index(type || @filename[/[^\.]*$/].downcase)
  end

  def height
    unless @height
      image_size
    end
    @height
  end

  def width
    unless @width
      image_size
    end
    @width
  end

  def fetch_image_size(filename, image_type); end

  def image_size
    if @type
      fetch_image_size(@filename, @type)
    else
      raise "Unknown type of image"
    end
  end

  inline do |builder|
    builder.include '"gd.h"'
    builder.add_link_flags "-lgd"

    builder.c <<-"END"
    void fetch_image_size(char *filename, int image_type) {
      gdImagePtr im_in;
      FILE *in;

      in = fopen(filename, "rb");
      /* Support diff image types: jpg jpeg png gif */
      switch(image_type) {
        case 0:
        case 1: im_in = gdImageCreateFromJpeg(in);
            break;
        case 2: im_in = gdImageCreateFromPng(in);
            break;
        case 3: im_in = gdImageCreateFromGif(in);
            break;
      }
      fclose(in);
      if (im_in) {
        rb_iv_set(self, "@width", INT2FIX(im_in->sx));
        rb_iv_set(self, "@height", INT2FIX(im_in->sy));
      }
    }
    END
  end
end

To use it, do something like this:

begin
  iinfo = ImageInfo.new(filepath)
  my_image_width = iinfo.width
  my_image_height = iinfo.height
rescue Exception=>e
  # check errors
end

Image science breakage - SystemExit

Image Science is a small plugin that does just the stuff you need from RMagick, without all the weight that RMagick brings with it. It uses FreeImage to do the manipulations. (By the way, the MacPorts installation of that completely failed on my Leopard Mac, I advise you compile it by hand.)

I was getting these kind of errors from my dev site:

SystemExit (exit):
    /usr/local/lib/ruby/gems/1.8/gems/RubyInline-3.6.5/lib/inline.rb:70:in `exit'
    /usr/local/lib/ruby/gems/1.8/gems/RubyInline-3.6.5/lib/inline.rb:70:in `rootdir'
    /usr/local/lib/ruby/gems/1.8/gems/RubyInline-3.6.5/lib/inline.rb:84:in `directory'
    /usr/local/lib/ruby/gems/1.8/gems/RubyInline-3.6.5/lib/inline.rb:258:in `so_name'
    /usr/local/lib/ruby/gems/1.8/gems/RubyInline-3.6.5/lib/inline.rb:294:in `load_cache'
    /usr/local/lib/ruby/gems/1.8/gems/RubyInline-3.6.5/lib/inline.rb:678:in `inline'
    /usr/local/lib/ruby/gems/1.8/gems/image_science-1.1.3/lib/image_science.rb:84

Not nice. Curiously everything worked fine on the production system. A bit of digging in the code was required. Image Science uses a gem called RubyInline to allow it to compile and install C extensions to Ruby on the fly. And RubyInline places these extensions in a directory called “.ruby_inline”. It chooses this directory from either the environment setting INLINEDIR, or if that isn’t set then HOME.

And there is the problem - my dev system was running as apache which does not have a home directory.

I added this kind of code:

if RAILS_ENV == "development"

  ENV['INLINEDIR'] = RAILS_ROOT + "/tmp"

end

And I made sure that tmp in my rails dir was writeable by apache (but do not make it world writable, that won’t work either).

Exception notifier does not work with Ruby 1.8.6p111

Not only does exception notifier not work, you probably don’t know that it doesn’t work either. All your code has just become super-exception-free all of a sudden.

Ah, wishful thinking.

If you have installed Ruby 1.8.6p111 then you will want to take note of the comments here.

Just modify the file vendor/plugins/exception_notification/views/_environment.rhtml to say:

* <%= "%-*s: %s" % [max.length, key, @request.env[key].to_s.strip] %>

instead of

* <%= "%*-s: %s" % [max.length, key, @request.env[key].to_s.strip] %>

Why Ruby is not my favourite programming language

Actually it is my favourite. But the title comes from this article (which itself was inspired and copied from the famous paper about pascal by Brian Kernighan).

On with business though - Ruby has some serious wrinkles that produce counter-intuative and plain strange behaviour. One might say that they violate the Principle of Least Surprise. But POLS is somewhat personal - surprising to whom? (just me maybe!)

Surprising result #1

b=1
=> 1
a if a=b
NameError: undefined local variable or method `a' for main:Object
        from (irb):2

This is odd indeed. It seems that the Ruby parser thinks that a is a method at the beginning of the line. But when it parses a=b it will know (and remember) that it is a variable. However, when the condition passes and it comes to execute ‘a’, it does not apply its new found knowledge about the nature of a - it still goes for the method. And hence the error.

Surprising result #2

c=1 unless defined? c
=> nil
c
=> nil

This is a bit easier to explain. Once the parser has seen c=1 it enters c in the symbol table, it knows that c exists as a variable. Hence it is defined (as nil). So c=1 is never executed.

Surprising result #3

class A
  attr_accessor :a
  def b
    @a=0
    x=a
    a=x+1
    puts("a is #{a}")
    puts("@a is #{@a}")
  end
end
=&gt nil
c = A.new
=> #<A:0x32fb7c>
c.b
a is 1
@a is 0

No mystery here really - it’s a nice gotcha though - a= does not call self.a=, it just assigns a local variable. It is just a little confusing unless you think carefully.

Surprising result #4

[0.0, 1/0.0].sort
=> [0.0, Infinity]
[0.0, 0/0.0].sort
ArgumentError: comparison of Float with Float failed

Thanks to Evan Weaver for pointing this out to me. This is due to 0/0.0 returning the value NaN (as opposed to 1/0.0 which returns Infinity). And the comparison with NaN by sort gives the strange error message.

Let me know if you spot any more oddities.

Blog Archives

Navigation


About this blog

A blog about Ruby, Rails and other tech. Mostly.


Find Something?