Mike Perham

On Ruby, software and the Internet

Touch a File

February 27th, 2010 · 1 Comment

Here’s how to touch a file using Ruby, easy as 1-2-3:

  File.utime(access_time, mod_time, filename)

→ 1 CommentTags: Ruby

The Trouble with Ruby Finalizers

February 24th, 2010 · 6 Comments

I was test driving Devil, the developer’s image library, recently to see if it would work for us in a long-living daemon. Task #1 to that end is to verify the absence of memory leaks, which seem to be common in image libraries. It was almost immediately apparent that Devil contained a large memory leak. So I worked with John Mair to fix the issue.

Devil has a Devil::Image class which uses a finalizer to delete native resources when the image is garbage collected. The problem is that Ruby finalizers are notoriously difficult to use properly so often times they aren’t actually run. Here’s why:

class Devil::Image
    attr_reader :name, :file
 
    def initialize(name, file)
        @name = name
        @file = file
 
        ObjectSpace.define_finalizer( self, proc { IL.DeleteImages(1, [name]) } )
    end
end

So what’s wrong with this code? The issue is that the finalizer proc is a closure which holds a reference to it’s self, thus making it impossible for the image object to ever be garbage collected. When creating a finalizer proc, you should always use a class method to create the proc so that it does not hold a reference to the corresponding instance, like so:

  def initialize(name, file)
      @name = name
      @file = file
 
      ObjectSpace.define_finalizer( self, self.class.finalize(name) )
  end
  def self.finalize(name)
    proc { IL.DeleteImages(1, [name]) }
  end

A subtle and evil bug, just like its namesake!

→ 6 CommentsTags: Ruby

Changelog vs Commitlog

February 18th, 2010 · 5 Comments

One of the things I really like about some software projects is when they provide an actual changelog or release notes. RabbitMQ released 1.7.2 the other day and I asked the developers if they could link to a changelog. They pointed me to this page. Unfortunately this is not exactly what I had in mind. To me, a changelog is a brief overview of the changes in a version that is digestible by the end user. The key factor is that a changelog is not machine-generated but written by a project developer for the project’s users. The RabbitMQ changelog is far too verbose (one entry per commit, along with merge noise).

Here’s a few examples of good changelogs: memcache-client, Java, Nokogiri, Resque, Redis.

Personally I consider a changelog one of the best indicators of a well run OSS project. If you run an OSS project, please consider supplying release notes or a changelog so that other developers can follow your project with ease!

Update: looks like I just missed the changelog for RabbitMQ. Alexis was kind enough to point me to the release notes in the comments.

→ 5 CommentsTags: Software

Asynchronous DNS Resolution

February 10th, 2010 · 4 Comments

Ruby has a serious scalability problem most Rubyists are unaware of. When you lookup the IP address for a hostname, the entire Ruby process blocks by default. If you have a slow DNS server, your process can grind to a halt waiting for hostname resolution. Ruby comes standard with a fix, resolv-replace, which provides a DNS resolver that does not block the entire process. It does however block the Thread, like any other instance of blocking I/O.

So I wrote an EventMachine-aware DNS resolver that ensures that your asynchronous operations don’t block while performing DNS resolution. Take a look at em-resolv-replace and give it a whirl.

→ 4 CommentsTags: Ruby

Cassandra and EventMachine

February 9th, 2010 · 6 Comments

I spent this past weekend adding eventmachine support for the Cassandra gem. We’re using Cassandra at OneSpot as our next-gen data store and need EM support. They were nice enough to pull my changes yesterday so the next release of the thrift_client and cassandra gems should work in EM. You just need to do this:

    require 'thrift_client/event_machine'
    EM.run do
      Fiber.new do
        @twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :transport => Thrift::EventMachineTransport, :transport_wrapper => nil)
        @twitter.clear_keyspace!
        EM.stop
      end.resume
    end

The key is the :transport and :transport_wrapper options which override the default, Socket-based implementation. Like all of my EventMachine code, this requires Ruby 1.9.

→ 6 CommentsTags: Ruby

Scalable Ruby Processing with EventMachine

January 27th, 2010 · 5 Comments

I gave a talk at Austin On Rails last night on using EventMachine, focused on maximizing concurrency when processing a message queue. There were a lot of questions, mostly revolving around the flow of execution within EventMachine code. To this point, there were two common stumbling points people seemed to have:

  • Ruby developers are not used to treating blocks as true callbacks where they are executing at some point in the future. Blocks are usually yielded by the method they are passed to. Understanding when a block will be called is confusing.
  • Understanding how Fibers work and how they can make an asynchronous API appear to be synchronous to the outside world is tricky.

I hope everyone came away a little more knowledgeable about EventMachine and the types of problems it can solve. Here’s the slides for others to peruse. The presentation was recorded and I will link to recordings when I find out about them.

Scalable Ruby Processing with EventMachine (Keynote 2009, 1.2 MB)
Scalable Ruby Processing with EventMachine (Scribd)
Scalable Ruby Processing with EventMachine (Audio MP3, 49MB)
Scalable Ruby Processing with EventMachine (Vimeo)

→ 5 CommentsTags: Ruby

Varnish on 32-bit systems

January 18th, 2010 · 1 Comment

We run three small EC2 instances for content caching purposes at OneSpot. These systems are 32-bit machines with 1.7GB of RAM. Originally we figured even on a small system Varnish could flood a 100Mb line so we wouldn’t need a more expensive, large EC2 instance. This blog post explains why this turned out to be a poor choice.

Executive summary: Varnish really, really wants to run on a 64-bit system. Don’t run it on 32-bit systems if possible.

Varnish wants to memory map the entire cache. This means the entire cache needs to be able to fit into virtual memory. On a 64-bit system, VM is virtually unlimited. On a 32-bit system, processes usually have access to a maximum of 3GB of virtual memory. Since you also need to allocate stack space and other standard process requirements, in practice people don’t recommend more than 2GB of cache space for Varnish on 32-bit systems. Pretty small for a web content cache. If you want Varnish to use an entire disk for a cache, it must run on a 64-bit system.

We had a few minutes of outage recently due to this architecture. We read some Varnish tuning tips and decided to modify our default configuration. Specifically we raised the minimum thread count from 1 to 500. Because, after all, “ idle threads are cheap“. But they are only cheap on 64-bit systems where allocating hundreds of MB for extra stack space is a no brainer! When we rolled out this change, the process ran out of memory and couldn’t allocate the extra threads. Klaxons went off and I rolled back the changes. Over the next few months, we’ll be upgrading our caches to 64 bit so that we don’t need to worry about sizing issues moving forward.

→ 1 CommentTags: Software

Speaking on January 26th

January 6th, 2010 · No Comments

I’ve been enjoying my holiday break (perhaps a bit too much since I’ve produced no new blog content) but to shake off the cobwebs I’ve signed up to speak at Austin on Rails this month on “Scalable Ruby Processing with EventMachine”. I’ll discuss the advantages of event-driven programming in general, why it’s especially useful to the Ruby world and some of the work I’ve been doing in my spare time on my Evented project. Hope to see you there!

→ No CommentsTags: Ruby

Event-Driven Applications

December 1st, 2009 · 1 Comment

Getting concurrency in Ruby is tough: Ruby 1.8 threads are green so they don’t execute concurrently. Ruby 1.9 threads are native but they don’t execute concurrently due to the GIL (global interpreter lock) necessary to ensure thread-safety with native extensions. Only JRuby provides a stable, concurrent Ruby VM today. On top of that, writing thread-safe code is tough – code execution is non-deterministic and so everyone gets it wrong, the code is hard to test and bugs painful to track down.

For these reasons, I would argue that IO-intensive applications need to either use an event-driven application model or a language designed for concurrency like Clojure. Since I like to work with Ruby, the former is the route to follow.

This overview is important to understand because the main deployment pattern with Rails apps is to instantiate 5-10 Rails processes, which can each handle one request at a time. If a request takes 5-10 seconds to process (maybe it is calling Amazon S3 or SimpleDB), that entire Rails process is stuck waiting for the data. Even a multi-threaded Rails application is limited due to the GIL. For this reason, people use a message queue to handle long-running tasks but often that just passes the buck: now the message queue processor is the one stuck for 5-10 seconds instead. You don’t have a user waiting for a response but you still are limited in how fast you can process the queue based on the amount of memory you have and the number of daemon processes you can start.

EventMachineLogoneverblock

This is where an event-driven model would help immensely. The fundamental tools at your disposal are NeverBlock and EventMachine. EventMachine provides the reactor, the fundamental “switch” in your application which decides what code is ready to run now, and NeverBlock provides various drop-in replacements for the common Ruby code used for network and IO: mysql and postgres database drivers, tcp sockets, etc. Using these, the message queue processor can process many messages at the same time: there’s never any concurrent execution but as one message performs some IO request, eventmachine and neverblock will seamlessly switch to handle another message while waiting for the IO response. That’s the fundamental difference with threaded code: instead of switching threads at a non-deterministic point in the future, event-driven code only switches when the code tries to perform IO. Your code does not need to be thread-safe because your code will not be interrupted while modifying variables and data structures in memory.

Sounds good, right? Well, a few caveats:

  • CPU-intensive processes won’t gain much. There’s still only a single actual thread of execution under the covers so event-driven applications will only take advantage of a single processor/core.
  • Your application should run on Ruby 1.9 to take advantage of Fibers. Fibers have been backported to Ruby 1.8 but I encourage you to try Ruby 1.9. Most extensions are Ruby 1.9 safe now and Rails is fully supported on Ruby 1.9. Without Fibers, your application code needs to change dramatically to work as success/error callbacks. With Fibers, your code needs little change and can be written in the more familiar procedural style.
  • Application exception handling becomes tricky, just as with threads. It’s easy to lose an exception.

Next time, we’ll take a deeper look into some event-driven code and how it works.

→ 1 CommentTags: Ruby · Software

EventMachine Examples

November 2nd, 2009 · 3 Comments

I needed to create a simple, but IO-intensive, thumbnailing service for OneSpot last week. This service acts as a proxy to S3 and so a blocking implementation would not scale well, even if threaded. I wanted to use EventMachine instead. Lessons learned:

  • The programming model is a mind twist and takes a long time to understand. The sprinkling of implementation at various layers makes it harder. I’ve spent several days now reading through EventMachine, Thin, Rack and em-http-request source code.
  • There’s no non-trivial examples out there. It seems like every example is 10 lines of “Hello World” code with no samples of how to integrate multiple pieces. Ok, here’s a 10 line async web server. Now how do I integrate an async call to the DB? How do I make an async 3rd party web service call?
  • There’s no testing support. No libraries for doing async testing and no best practices or suggestions on how to test.

So how can we make things better rather than just complain? I’m going to show you a non-trivial example. In return, I want you to send me more examples. Evented is my new Github repository for EventMachine examples. The first example is that big chunk of code that I puzzled over for the last few days which implements the thumbnailing service using Thin, S3, image_science and em-http-request. But I want more examples and I’d love to hear ideas on how to test this type of code. Leave a comment, send a pull request, and help me help you!

→ 3 CommentsTags: Ruby