Thursday, November 3, 2011

Passenger tuning for rails application

Guys, it has been a long over due post. I have finished tuning passenger long back but caught up with Diwali celebration.

Anyways generally I prefer simple and easily understandable configuration. So following configuration is as per my best knowledge and google findings. ;)

I have verified my configuration using ab - Apache HTTP server benchmarking tool. Also tried other tools such as Jmeter, httperf. I have also used passenger-memory-stats for finding rails instance size and passenger-status for finding number of request which are pending in global queue.

I have started passenger tuning using passenger nginx user guide. You will also find passenger-apache guide.

As I am using nginx, following configuration is with respect to nginx but same will applies for apache except some extra directives and syntax.

After digging into passenger tuning, I realized that passenger well configured for production. But still Based on nginx-passenger user guide I have collected following list of directive, which we can configure according to our needs.

Directive Default Value Nginx Block Use
passenger_max_pool_size <integer> 6 http Maximum instances on server
passenger_pool_idle_time <integer> 300 sec http instance idle time
passenger_max_instances_per_app <integer> 0 http Maximum instances allowed to single app
passenger_min_instances <integer> 1 http, server, location, if minimum number of application instances which are always active
passenger_pre_start <url> - http Pre start application
passenger_use_global_queue <on|off> on http, server, location, if Turns the use of global queuing on or off
passenger_ignore_client_abort <on|off> off http, server, location, if Ignore client aborts
rails_framework_spawner_idle_time <integer> 1800 sec http, server, location, if FrameworkSpawner server idle time
rails_app_spawner_idle_time <integer> 600 sec http, server, location, if ApplicationSpawner server idle time
passenger_log_level <integer> 0 (Can be 0, 1, 2, 3) http for how much information Passenger should write into error.log
passenger_debug_log_file <filename> error.log http allow to specify the file that debugging and error messages should be written
passenger_pass_header <header name>
http, server, location, if Used to pass headers

Almost all of these directive has default value which is good enough for an applications. But there are some directive which very important to configure depending on our application environment.

1. passenger_max_pool_size :
            The maximum number of Ruby on Rails or Rack application instances that may be simultaneously active. A larger number results in higher memory usage, but improved ability to handle concurrent HTTP clients.
            The value should be at least equal to the number of CPUs (or CPU cores) that you have. If your system has 2 GB of RAM, then we recommend a value of 30. If your system is a Virtual Private Server (VPS) and has about 256 MB RAM, and is also running other services such as MySQL, then we recommend a value of 2.
            I recomond you to find your application instance size using passenger-memory-stats and configure value of passenger_max_pool_size.
            e.g.  If your application instance size is 300 MB and RAM size 2 GB then value of passenger_max_pool_size should be 4 because 1200 MB(4*300) will be used by application instance and remaning for other process.

2. passenger_max_instances_per_app :
            The maximum number of application instances that may be simultaneously active for a single application. This helps to make sure that a single application will not occupy all available slots in the application pool.
            This value must be less than passenger_max_pool_size. A value of 0 means that there is no limit placed on the number of instances a single application may use, i.e. only the global limit of passenger_max_pool_size will be enforced.

3. passenger_pre_start :
            By default, Phusion Passenger does not start any application instances until said web application is first accessed. The result is that the first visitor of said web application might experience a small delay as Phusion Passenger is starting the web application on demand. If that is undesirable, then this directive can be used to pre-started application instances during Nginx startup.
            This directive accepts the URL of the web application you want to pre-start. It may be specified any number of times.

Let's get into live scenarios

1. A server with multiple rails applications (Development and test server)
            I have a server with 16 GB of RAM and 8 core cpu, on which redmine(bug tracking system), test rails app and other developers app instances are running. Means you can say mutiple apps are running.
            According to passenger-memory-stats redmine is taking 300 MB and other rails app 500 MB. So I have kept following configuration
passenger_max_pool_size 16;
# arround 8 GB for rails application and remaning for other process
passenger_pool_idle_time 150;
# reduced idle time as multiple app are running
passenger_max_instances_per_app 8; 
# since single app should not use whole pool size, but simuteniouly should handle mutiple requests
2. A server with single rails application (Production server)
            On my production enviroment we have 8 GB of RAM and 4 core cpu, on which only single rails application is running.
            As my rails application instance size is 500 MB, I have kept following configuration on production
passenger_max_pool_size 10;
# arround 5 GB for rails application and remaning for other process
passenger_pool_idle_time 600;
# Increased as it is production
passenger_min_instances 2;
# atlease two instances should in memory at any time
# pre start my instance at the time of nginx start instade on first request
For testing whether required instances are forked or not use ab (Apache benchmark) as described above.

Reference :