Returning an image from a Ruby on Rails controller

I am working on an app that needs to return an image file in the controller.  I send over some request parameters to the app, it goes to the controller which pulls out the params and then creates an image based on those parameters.  So instead of rendering the view, the app would need to return a png file.  The send_data method was designed to do exactly this.  Here is the code:

data = File.open(your_image_path,’rb’).read
send_data(data , :filename => your_image_name, :type=>’image/png’)

Installing Nginx on CentOS 5

I recently installed Nginx on another server and forgot how difficult it was the last time I did it.  You need to make sure you have all the dependencies before you can configure the source.  Here are the steps to get a successful install on CentOS 5 using Nginx 0.7.61.

> yum install pcre
> yum install pcre-devel
> yum install zlib
> yum install zlib-devel
> yum install openssl-devel
> yum install openssl
> wget http://sysoev.ru/nginx/nginx-0.7.61.tar.gz
> tar -xvf nginx-0.7.61.tar.gz
> ./configure
> make
> make install

Nginx should be installed after that.   Then you can edit your config in /usr/local/nginx/conf and start the web server using:

> /usr/local/nginx/sbin/nginx

Customizing Rails scaffolding to suit your app

OK…. I wish I would have tried this earlier.  It would have saved me alot of time.  The default scaffolding that comes with Rails 2.3 is pretty good out of the box, but it is never going to fit exactly what you need for every app.  One of my apps is a bit different than the default scaffolding setup.  I have integrated Highslide Javascript Library into my app for the administration functions.  It works very well.  If you havent used it, its basically a lightbox script that allows you to use iframe content (and alot of other stuff) inside the modal popup.  So when I need to edit or add something, I just click on it and a modal popup appears.  When I save it, it closes the modal popup and refreshes the page via ajax or a meta refresh depending on the needs of that page.  It gives you a nice smooth workflow.  Some of the calls to highslide are baked into the controller and views.  This is just one example of things I change from the default scaffolding.  There are plenty of other things I like to add to every controller and view, such as my home-brewed security methods and a shared form for edit/new.  All of this takes alot of time.

In the past I used scaffolding as my base point before I made all the changes.  It was still faster than coding it from scratch, but what if I could make scaffolding generate exactly what I want every time?!  It is actually much easier than I anticipated.  It took me about as much time to change the scaffolding as it did to change one of the resources I created with the scaffolding.  Here is what you need to do.

1.  Locate your Rails source directory – on my Mac it is located here: /Library/Ruby/Gems/1.8/gems/rails-2.3.2    I use Aptana Studio for development (yeah I know.. I should use Textmate.. whatever dude), so I just added the rails source as a project so I could edit it directly from the IDE.

2. Go to this directory ->  rails-2.3.2/lib/rails_generator/generators/components/scaffold/ and first take a look at scaffold_generator.rb.   This class runs the scaffolding which creates all the files using the templates in the subdirectory /templates.  The first thing I wanted to customize in the scaffolding was adding a partial named form.  So I edited the method scaffold_views and added it to the end of the array like so:

def scaffold_views
#ADDED BY JOHN – _form
%w[ index show new edit _form]
end

Next I wanted to get rid of the layout file and css that are created when scaffolding runs.  I rarely use them since I use the base layout and main css for my whole site, so I commented out these lines:

#m.directory(File.join(‘app/views/layouts’, controller_class_path))

……

# Layout and stylesheet.
#m.template(‘layout.html.erb’, File.join(‘app/views/layouts’, controller_class_path, “#{controller_file_name}.html.erb”))
#m.template(’style.css’, ‘public/stylesheets/scaffold.css’)

The possibilities in this file are endless.  Just add/remove whatever you want to/from def manifest

3.  Next you can edit the templates that you want to customize.    /Library/Ruby/Gems/1.8/gems/rails-2.3.2/lib/rails_generator/generators/components/scaffold/templates

I edited the controller template and all of the views.  You will notice that the template uses ruby code for some of the generation:

<% for attribute in attributes -%>
<th><%= attribute.column.human_name %></th>
<% end -%>

Basically you can code it with ruby and it the generator will spit out a string which it writes to the .rb files.  Think of it as code inside code.

I am not exactly sure what this means though, maybe someone can enlighten me:  <%%=

<%%= link_to ‘Show’, <%= singular_name %> %>

I am assuming its something like escaping a quote.. but I dont really have a clue.  I was still able to get it to output the code I wanted without knowing exactly what it does.

Anyways… You can change these files around to spit out the code you want.  Its very easy to customize and it can save you a ton of time if you are a heavy scaffolding user.  There is probably a way to turn this into a plugin or use a symlink, so you don’t have to edit the original files, but I guess I am lazy.

Backing up MySQL on the crontab with a shell script

I recently moved to a master/slave setup with MySQL.  At first I was thinking.. “Oh this is great, I have a backup database now in case anything breaks on the master. “  Don’t go there.. think about it for a second.  If something gets corrupted on the master, then it is going to push the changes over to the slave and then you will have 2 corrupted databases.  It is not that hard to create a backup, though, using mysql_dump + shell script + crontab.  That way you can have a nightly backup and reload it into MySQL if anything gets corrupted.  Here is what you do:

Create a file with your favorite editor called backup.sh and add the following to it:

#!/bin/sh
NOW=$(date +”%b-%d-%y”)
mysqldump -u youruserid -pyourpassword –all-databases > /path/to/mysql_backups/mysql_backup_$NOW.sql

The 2nd line in this file sets a variable for the current date in format like this:  Aug-26-09.  The last line uses the mysql_dump command to output all your databases to a file that appends the current date to the file name using the NOW variable.  If you did not append the date to the mysql backup, then it would get overwritten every night.  NOTE:  you may also want to create a delete script, so this cron does not fill up your filesystem.

Next thing you need to do is change the permission on the file so that you can execute it.  I am just assigning RWX for everyone, because I am too lazy to do it the right way (you probably just want to make this executable by the crontab user)

> chmod 777 backup.sh

The last thing you have to do is add it to your crontab. 0 0 * * * *, makes the cron execute it at midnight every night.

crontab -e
0     0       *       *       *       /path/to/your/backup.sh

Now this script will execute every night and export your entire database.  Now, dont you feel better??  Thought so.

Posted in MySQL. 2 Comments »

A quick performance tune for mysql – innodb_buffer_pool_size

If you are using MySQL with InnoDB tables, you should really look at the innodb_buffer_pool_size.   This tells MySQL how much memory it should use to cache data on your InnoDB tables.  This cuts down on disc IO.  It can save you alot of lookup time if you are pulling out of the cache as opposed to making another call to the database table.  As a good rule of thumb this value should be set to 10% larger than the size of your database… that is assuming you have that much memory available on your server.  By default innodb_buffer_pool_size is set to 8MB.  If you have a decent sized data set, and most of you probably do, then you are going to want to increase this.  My database is about 1GB, so I set my innodb_buffer_pool_size to 2GB, so that I can account for some growth in the future.  Just be sure not to set the value too high.  If you only have 2GB of memory on your box, then don’t set it to 2GB.  This can cause paging at the OS level and that is bad news.

So here is how you set that parameter in mysql

open up your /etc/my.cnf in your favorite editor and add the parameter in the mysqld section

[mysqld]
innodb_buffer_pool_size = 2G

Then you restart mysql and the changes should be picked up.

/etc/init.d/mysqld restart

You can double check by issuing the following command in mysql client.  It will show you the variable and its value

mysql> SHOW VARIABLES;

NOW THATS A QUICK AND EASY PERFORMANCE TUNE!!

Ruby on Rails & MySQL Master Slave replication tutorial

I have been running a web app on 1 server since 2005 and we have seen a good bit of traffic. We are currently ranked 8200 according to quantcast.com with just over 200,000 visitors a month. Traffic is picking up and I expect it to almost double in Q4.  NewRelic shows all green right now with about 250ms response time which is fine.  But I am worried that if traffic doubles, the database may become a little stressed.  So I have decided to be pro-active and go ahead and add another server to the database layer.  Right now, I have Nginx in front of mongrel cluster (4 instances) and MySQL as the database.  All of this is running on the same server.  So my new setup will be:

Server 1:  Nginx > Mongrel Cluster > MySQL master

Server 2: MySQL slave

NOTE:  RailsEnvy has a really good video series on scaling your database (and many other great videos on other subjects), so you may want to check that out just to get an overview of the process.

In order to get this working, Rails will need to be able to perform writes only on master and reads on both master and slave.  But, lets talk about setting up the replication first.  Here is how you set it up:

1. Set binary logging on the master server.  Edit /etc/my.conf

[mysqld]
# add this line anywhere in the [mysqld] section
log-bin=mysql-bin

2. Set the server ids on master and slave in /etc/my.conf

#MASTER
[mysqld]
server-id=1
#SLAVE
[mysqld]
server-id=2

3. Grant the slave permission to pull the data from the master (replace XX.XX.XX.XX with your slave’s IP address)

mysql>grant replication slave on *.* to ‘replication’@XX.XX.XX.XX identified by ’slavepass’;

4. Perform a read-lock on the master

mysql> FLUSH TABLES WITH READ LOCK;

5. Record the file and position on the master.  It should give you output like this.  Record the file and position to use later

mysql> SHOW MASTER STATUS;
+——————+———-+————–+——————+
| File                            | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+——————+———-+————–+——————+
| mysql-bin.000001 |  1467771 |              |                  |
+——————+———-+————–+——————+

6. Export data on master using mysql_dump

> mysqldump –all-databases –lock-all-tables >dump.db

7. Unlock the tables on master

mysql> UNLOCK TABLES;

8. FTP the tar file of the mysql snapshot to your slave box

9. Stop MySQL on the slave

/etc/init.d/mysqld stop

10. You need to start slave using –skip-slave option.  I changed the init.d script to do this.  Probably an easier way to do it, but whatever..  It was line 61 in /etc/init.d/mysqld.  Just make a backup of that file, so you can replace it after you start mysql

# Pass all the options determined above, to ensure consistent behavior.
# In many cases mysqld_safe would arrive at the same conclusions anyway
# but we need to be sure.
/usr/bin/mysqld_safe   –skip-slave –datadir=”$datadir” –socket=”$socketfile” \
–log-error=”$errlogfile” –pid-file=”$mypidfile” \
>/dev/null 2>&1 &

11. Start mysql on slave.  Then change /etc/init.d/mysql back to the original (without –skip-slave option)

> /etc/init.d/mysqld start

12. Import the data into the slave

> mysql < dbdump.db

13. Configure master on the slave box using the values for your host,user, and password.  Fill in the last 2 with the values you recorded from step 5

mysql> CHANGE MASTER TO
-> MASTER_HOST=’your_host’,
-> MASTER_USER=’replication’,
-> MASTER_PASSWORD=’user_password’,
-> MASTER_LOG_FILE=’mysql-bin.000001′,
-> MASTER_LOG_POS=1467771;

14. Start the slave replication on the slave server

mysql> START SLAVE;

Now your slave should be getting updates from the master. Go ahead and change some stuff on the master and see if the slave replicates.  You can also view the status of the slave by using this mysql command:

mysql> SHOW SLAVE STATUS;

CONFIGURING RAILS TO WORK WITH MASTER/SLAVE

The next step in the process is to get rails to communicate with the master/slave setup. The easiest way to do this is to use the machoism plugin.  You can get it at github.  There are a few different branches of this, but I am using the one from technoweenie (I had problems getting the mislav version to work)

http://github.com/technoweenie/masochism/tree/master

It is easy to install.  Just go to your rails app root directory and issue the following command

> script/plugin install http://github.com/technoweenie/masochism.git

Next you need to configure your database.yml to for masochism.  One interesting thing I found out that I was not aware of is that you can set the login as a variable, so you can reuse it later in the config file.  Here is my config

login: &login
adapter: mysql
database: yourapp_production
username: yourusername
password: yourpassword# default configuration (slave)
production:
<<: *login
host: XX.XX.XX.XX

# setup for masochism (master)
master_database:
<<: *login
host: XX.XX.XX.XX

Then add the following lines to your environment.rb file

# in environment.rb
config.after_initialize do
  if Rails.env.production?
    ActiveReload::ConnectionProxy::setup!
  end
end

Restart your app server and you should be good to go.

I thought I was until I figured out that there was a conflict between masochism and another plugin I use called use_db.  Masochism seems to be overriding the database config for use_db, so it is restricting me to only the master/slave databases.  I will post when I figure out a solution to this.

MySQL & HAProxy tutorial

I have 2 database servers.  One server runs a script that updates about 8 million products.  This takes a long time ;-)   And I can’t use MySQL master/slave because the data is not production worthy until the script fully completes.  Instead of using Master/Slave, I push the binary files from database server 1 to database server 2 when the script completes.  So each of the databases will be turned off at some point.  Therefore, I need to use HAProxy to forward the requests to the working server when the other one is turned off.  And when the product script is not running and binary files are not being pushed, I want both of them running in the cluster.  So here is application flow:

web server requests -> haproxy -> database servers 1 & 2

In order to make this work we need HAProxy installed and we need a health check script for both databases, so HAProxy knows when one is not running (no requests will be forwarded to this server).  Here are the steps:

First I need to install haproxy on my web server (CentOS Linux), so that it can forward the requests to the 2 MySQL instances.  Go here:

http://haproxy.1wt.eu/#down

Download the latest stable source and make it.

tar -xvf haproxy-1.3.20.tar.gz
cd haproxy-1.3.20
make install

You dont have to ./configure and make.  Just “make install”.  Next step is to set up a config file.  Most people choose /etc/haproxy.cf

Here is a what my final config looks like:

global
        log 127.0.0.1 daemon debug
        stats socket /tmp/stats
        maxconn 4096
        pidfile /var/run/haproxy.pid
        daemon
defaults
        log global
        mode tcp
        option dontlognull retries 3 option redispatch
        maxconn 2000
        contimeout 5000
        clitimeout 50000
        srvtimeout 50000
listen  MySQL XX.XX.XX.XX:3305
        mode tcp
        option  httpchk
        stats enable
        stats uri /haproxy-stats
        balance roundrobin
        server mysql_slave XX.XX.XX.XX:3306 check port 9200 inter 12000 rise 3 fall 3
        server mysql_slave XX.XX.XX.XX:3306 check port 9200 inter 12000 rise 3 fall 3

Of course, you need to replace XX.XX.XX.XX with your server IPs.  The first IP in the “listen” section is the IP address where HAProxy is installed.  The last 2 lines in this file are where you put the IP addresses for your database servers.

After your config is set up, you can start haproxy with this command:

> /usr/local/sbin/haproxy -f /etc/haproxy.cfg -p /var/run/haproxy.pid

Then use your SQL client (I am using SQLyog) to login to mysql using port 3305.  If you are able to login, then that means that HAProxy is listening on port 3305 and forwarding the requests to the slaves.

The next thing you need to do is add some health checks, so that if one of the slaves goes down, then HAProxy will stop forwarding requests to the non-working slave.  For this I referenced a tutorial written by Unai Rodriquez  - Having HAProxy check mysql status through a xinetd script Essentially, you just create a xinetd service that listens on port 9200 and returns an HTTP status 200 or 500 depending on the state of the MySQL server.  HAProxy stops forwarding requests if it sees a status 500.

You can test this by stopping and starting MySQL and watching http://yourhost/haproxy-stats.  This will show if the servers are up or down.  After you verify that it is working correctly, it is time to change your Ruby on Rails (or whatever framework you are using) configuration to use port 3305.  Deploy your code, restart your app server and you should have  a working mysql cluster.

Time Zone troubles

Well… I have been ignoring this for a few weeks, because it is not a huge deal, but I thought I might as well fix this.

Problem: The created_at and updated_at dates do not match my system time.  Every time I save a record, ActiveRecord records the date 5 hours ahead of my local time.  This was messing up some of my cronjobs that run 5 minutes before midnight.  Some of the records were not being found, because the date was recorded five hours in the future.

Solution:  After some digging on Google, I figured out that this is set in config/environment.rb.

# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run “rake -D time” for a list of tasks for finding time zone names.
config.time_zone = ‘UTC’

I did some more digging and found that UTC is 5 hours ahead of ET, so ActiveRecord was using this timezone instead of my local timezone.  The first fix I tried did not work, which was to chagne config.time_zone =”Eastern Time (US & Canada)’  I restarted my app server and still saw created_at was five hours ahead of my server’s system time.  So then I just commented it out and restarted the app again.  Walah.. it works now.  So the correct config to use system timezone is as follows:

# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run “rake -D time” for a list of tasks for finding time zone names.
#config.time_zone = ‘Eastern Time (US & Canada)’

No more worries

First Post

I am starting this blog, because I thought it might be useful for some Rails developers out there.  Other rails bloggers have saved my ass plenty of times, so why not write about my problems & solutions and maybe I can help out some other frustrated developer.  I own a web startup called CouponShack.com.  We have been around for a while and we just finished rewriting our site in Rails 2.3.  We use Nginx in front of Mongrel and also use Sphinx search.