Monday, January 28, 2008

My Default CentOS Setup

This script will probably only be valid for a week, but I thought I would share my ideas on a good CentOS 5 (64bit) install with Ruby on Rails, MySQL and Apache that will work with a capistrano deployment from subversion:

yum -y update
yum -y install ruby ruby-libs ruby-mode ruby-rdoc ruby-irb ruby-ri ruby-docs ruby-devel mysql mysql-devel mysql-server mysql-admin subversion httpd svn

togglesebool httpd_can_network_connect

cd /tmp
wget http://rubyforge.org/frs/download.php/29548/rubygems-1.0.1.tgz
tar -xvf rubygems-1.0.1.tgz
cd rubygems-1.0.1
ruby setup.rb

gem install rails json rfacebook acts_as_ferret capistrano mongrel mongrel_cluster pdf-toolkit actionmailer actionpack actionwebservice activerecord activeresource activesupport acts_as_taggable acts_as_versioned ferret google4r calendar mysql sources vpim mime-type --include-dependencies

#Fedora 8 also wants
yum -y install rubygem-mongrel gcc ruby-mysql
yum install gd gd-devel zlib-devel openssl-devel
gem install gem_plugin daemons capistrano --include-dependencies
gem install mongrel mongrel_cluster railsmachine --include-dependencies
gem install --version=2.7 mysql -- --with-mysql-config=/usr/bin/mysql_config

or

yum -y install ruby ruby-libs ruby-mode ruby-rdoc ruby-irb ruby-ri ruby-docs ruby-devel mysql mysql-devel mysql-server mysql-admin subversion httpd svn rubygem-mongrel gcc ruby-mysql gd gd-devel zlib-devel openssl-devel partimage
gem install -y rails -v=2.0.2
gem install json rfacebook acts_as_ferret capistrano mongrel mongrel_cluster pdf-toolkit actionmailer actionpack actionwebservice activerecord activeresource activesupport acts_as_taggable acts_as_versioned ferret google4r calendar mysql sources vpim mime-type ferret mime-types solr-ruby ruby-openid mechanize pdf-toolkit mongrel mongrel_cluster railsmachine bio --include-dependencies


I tried to make it as silent and complete as possible so that I could let the computer do all the work.

Thursday, January 24, 2008

Mysql Master to Master Replication

This will setup two Amazon EC2 CentOS 5 machines to cluster replicate all their MySQL databases.

Server 1: /etc/my.sql


#replication
server-id=1
log-bin=mysql-bin
binlog-ignore-db=mysql
binlog-ignore-db=test

#information for becoming slave.
master-host =
master-user = replication
master-password = slave
master-port = 3306



Server 2: /etc/my.sql

#replication
server-id=2
log-bin=mysql-bin
binlog-ignore-db=mysql
binlog-ignore-db=test

#information for becoming slave.
master-host =
master-user = replication
master-password = slave
master-port = 3306


Restart /etc/init.d/mysql restart on both servers.

Then run the following SQL on...
server 1:

STOP SLAVE;
CHANGE MASTER TO MASTER_HOST='10.253.15.15',MASTER_USER='replication',MASTER_PASSWORD='slave';
START SLAVE;
GRANT ALL PRIVILEGES ON *.* TO 'replication'@'' identified by 'slave';

server 2:

MASTER_HOST='10.253.65.221',MASTER_USER='replication',MASTER_PASSWORD='slave';
STOP SLAVE;
show slave status\G;
GRANT ALL PRIVILEGES ON *.* TO 'replication'@'' identified by 'slave';


You should then be able to create and remove databases on either servers and it will replicate to the other.

I tried to use "load data from master" but it only worked on very simple data. On large sets, it was better to use mysqlhotcopy (perl script that came with mysql on centos)

On CentOS, you first need to export the mysql directory in the /etc/exports file like this:
var/lib/mysql 10.253.15.15(rw,no_root_squash)
and then run:
exportfs -r

then on the machine with the tables, you run:
mkdir /tmp/mounted_mysql_directory
mount -o vers-3 10.253.65.221:/var/lib/mysql /tmp/mounted_mysql_directory

Now that you have the directory exported and mounted, you can hot copy the files from server 1 to server 2:

mysqlhotcopy --addtodest --resetmaster --resetslave -u root -p yourdatabasename_development /tmp/mounted_mysql_directory

The resetmaster and resetslave are important here. MySQL will keep an index in the replication log where it thinks its master is and where it is as a slave. After I copy the database over, I want them to start fresh. So, from here you can look at the master status on server 1 and master status on server 2 to get these indexes. then you can run CHANGE MASTER TO with an extra parameter:
MASTER_LOG_POS=98;
if the index was 98. This will then get them both in sync and ready to accept further transactions to apply.

You can also skip a given number of replication transactions using:
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = N;
just be careful what you skip.

Coming from a Notes/Domino background, this idea of replicating transactions was a little new. Notes, will keep a sequence number for each record and keep the data in sync. It does not matter how the data got into its current state, it will just do its best at keeping the data replicated (using sequence numbers and time stamps as needed).

MySQL is more transactional based. The documents talk about the my.cnf parameters like "binlog-ignore-db" will act as a filter for how transactions get written to the replication log. To a Notes person, this seems a little inefficient. It means to keep the data in sync, you have to run the exact same transactions on each server (even if the operations are repetitive and act on the same records). In Notes, it will be able to skip ahead to the final state rather than going through each transaction to get to the final state. MySQL has no such ability.

As a result, MySQL cannot just sync itself if you run into problems. This is also true when you add a member to the cluster or replace a failed one. To get things in sync again, you really have to think it through and make sure you lock tables as needed, get the data the same with copy tools like mysqlhotcopy and let the transactions start again on the cluster when you know it is ready.

I would also expect this would have a different scalability path than Notes - if your bottleneck is the amount of transactions you are getting, then adding additional replicas may not help in MySQL where it would in Notes.

Wednesday, January 16, 2008

Upgrading to rails 2.0.2

There were just a few too many cool features in 2.0 that I wanted so I have taken the plunge in a project. I did wait for 2.0.2, so it is not really bleeding edge - but I feel nice and up to date.
The process was fairly straight forward even though I knew I used a lot of plugins and deprecated methods. I had to perform the following to get it working:
  • gem install rails
  • script/plugin install svn://errtheblog.com/svn/plugins/classic_pagination
  • script/plugin install http://svn.rubyonrails.org/rails/plugins/in_place_editing/
I also referenced:
After restarting the server, users will get a warning about cookies. However, this is a small price to pay for cookie based sessions.