EventMachine now supports epoll, bringing large increases in performance and scalability to Ruby programs.

Epoll(7) is a alternative mechanism for multiplexed I/O that is available in Linux 2.6 kernels. It features significantly greater performance than the standard select(2) mechanism, when used in applications that require very large numbers of open I/O descriptors.

EventMachine has always used select(2) because its behavior is well standardized and broadly supported. But select becomes unreasonably slow when a program has a very large number of file descriptors or sockets. Ruby’s version of select hardcodes a limit of 1024 descriptors per process, but heavily loaded processes will start to show performance degradation even after only a few hundred descriptors are in use.

Epoll is an extended version of the poll(2) call, and it solves the problems with select. Programs based on epoll can easily scale past Ruby’s 1024-descriptor limit, potentially to tens of thousands of connectors, with no significant impact on performance.

(Another alternative which is very similar to epoll in principle is kqueue, supplied on BSD and its variants.)

This note shows you how to use epoll in your programs.

Compiling EventMachine to use epoll.

You don’t have to do anything to get epoll support in EventMachine. When you compile EventMachine on a platform that supports epoll, EM will automatically generate a Makefile that includes epoll. (At this writing, this will only work on Linux 2.6 kernels.) If you compile EM on a platform without epoll, then epoll support will be omitted from the Makefile, and EM will work just as it always has.

Using epoll in your programs.

First, you need to tell EventMachine to use epoll instead of select (but see below, as this requirement will be removed in a future EventMachine version). Second, you need to prepare your program to use more than 1024 descriptors, an operation that generally requires superuser privileges. Third, you will probably want your process to drop the superuser privileges after you increase your process’s descriptor limit.

Using EventMachine#epoll

Call the method EventMachine#epoll anytime before you call EventMachine#run, and your program will automatically use epoll, if available. It’s safe to call EventMachine#epoll on any platform because it compiles to a no-op on platforms that don’t support epoll.

  require 'rubygems'
  require 'eventmachine'

  EM.epoll
  EM.run {
    ...
  }

EventMachine#epoll was included in this initial release only to avoid changing the behavior of existing programs. However, it’s expected that a future release of EM will convert EventMachine#epoll to a no-op, and run epoll by default on platforms that support it.

Using EventMachine#set_descriptor_table_size

In Linux (as in every Unix-like platform), every process has a internal table that determines the maximum number of file and socket descriptors you may have open at any given time. The size of this table is generally fixed at 1024, although it may be increased within certain system-defined hard and soft limits.

If you want your EventMachine program to support more than 1024 total descriptors, you must use EventMachine#set_descriptor_table_size, as follows:

  require 'rubygems'
  require 'eventmachine'

  new_size = EM.set_descriptor_table_size( 60000 )
  $>.puts "New descriptor-table size is #{new_size}"

  EM.run {
    ...
  }

If successful, this example will increase the maximum number of descriptors that epoll can use to 60,000. Call EventMachine#set_descriptor_table_size without an argument at any time to find out the current size of the descriptor table.

Using EventMachine#set_descriptor_table_size ONLY affects the number of descriptors that can be used by epoll. It has no useful effect on platforms that don’t support epoll, and it does NOT increase the number of descriptors that Ruby’s own I/O functions can use.

set_descriptor_table_size can fail if your process is not running as superuser, or if you try to set a table size that exceeds the hard limits imposed by your system. In the latter case, try a smaller number.

Using EventMachine#set_effective_user

In general, you must run your program with elevated or superuser privileges if you want to increase your descriptor-table size beyond 1024 descriptors. This is easy enough to verify. Try running the sample program given above, that increases the descriptor limit to 60,000. You will probably find that the table size will not be increased if you don’t run your program as root or with elevated privileges.

But of course network servers, especially long-running ones, should not run with elevated privileges. You will want to drop superuser privileges as soon as possible after initialization. To do this, use EventMachine#set_effective_user:

  require 'rubygems'
  require 'eventmachine'

  # (Here, program is running as superuser)

  EM.set_descriptor_table_size( 60000 )
  EM.set_effective_user( "nobody" )
  # (Here, program is running as nobody)

  EM.run {
    ...
  }

Of course, you will need to replace “nobody” in the example with the name of an unprivileged user that is valid on your system. What if you want to drop privileges after opening a server socket on a privileged (low-numbered) port? Easy, just call set_effective_user after opening your sockets:

  require 'rubygems'
  require 'eventmachine'

  # (Here, program is running as superuser)

  EM.set_descriptor_table_size( 60000 )

  EM.run {
    EM.start_server( "0.0.0.0", 80, MyHttpServer )
    EM.start_server( "0.0.0.0", 443, MyEncryptedHttpServer )

    EM.set_effective_user( "nobody" )
    # (Here, program is running as nobody)

    ...
  }

Because EventMachine#set_effective_user is used to enforce security requirements, it has no nonfatal errors. If you try to set a nonexistent or invalid effective user, set_effective_user will abort your program, rather than continue to run with elevated privileges.

EventMachine#set_effective_user is a silent no-op on platforms that don’t support it, such as Windows.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.