Posted by aaron
on Monday, November 05
So i know it may be old school, but I like getting svn commit messages, especially when working on a small team. I found ElliotH's post about A better subversion post-commit hook than commit-email.pl, but it wasnt entirely working.
Most of the credit goes to him, I just fixed the colorization.
[UPDATED]: for some reason, new lines werent looking good in all emails
#!/usr/bin/ruby -w
# A Subversion post-commit hook. Edit the configurable stuff below, and
# copy into your repository's hooks/ directory as "post-commit". Don't
# forget to "chmod a+x post-commit".
# ------------------------------------------------------------------------
# You *will* need to change these.
address="FOO@SOME_DOMAIN.com"
sendmail="/usr/sbin/sendmail"
svnlook="/usr/bin/svnlook"
# ------------------------------------------------------------------------
require 'cgi'
# Subversion's commit-email.pl suggests that svnlook might create files.
Dir.chdir("/tmp")
# What revision in what repository?
repo = ARGV.shift()
rev = ARGV.shift()
# Get the overview information.
info=`#{svnlook} info #{repo} -r #{rev}`
info_lines=info.split("\n")
author=info_lines.shift
date=info_lines.shift
info_lines.shift
comment=info_lines
# Output the overview.
body = "<p><b>#{author}</b> #{date}</p>"
body << "<p>"
comment.each { |line| body << "#{CGI.escapeHTML(line)}<br/>\n" }
body << "</p>"
body << "<hr noshade>"
# Get and output the patch.
changes=`#{svnlook} diff #{repo} -r #{rev}`
body << "<pre>"
changes.each do |top_line|
top_line.split("\n").each do |line|
color = case
when line =~ /^Modified: / || line =~ /^=+$/ || line =~ /^@@ /: "gray"
when line =~ /^-/: "red"
when line =~ /^\+/: "blue"
else "black"
end
body << %Q{<font style="color:#{color}">#{CGI.escapeHTML(line)}</font><br/>\n}
end
end
body << "</pre>"
# Write the header.
header = ""
header << "To: #{address}\n"
header << "From: #{address}\n"
header << "Subject: [SVN] #{repo} revision #{rev}\n"
header << "Reply-to: #{address}\n"
header << "MIME-Version: 1.0\n"
header << "Content-Type: text/html; charset=UTF-8\n"
header << "Content-Transfer-Encoding: 8bit\n"
header << "\n"
# Send the mail.
begin
fd = open("|#{sendmail} #{address}", "w")
fd.print(header)
fd.print(body)
rescue
exit(1)
end
fd.close
# We're done.
exit(0)
Posted by aaron
on Saturday, November 03
So I remember a couple months ago playing with JRuby, and while fibonacci was super fast, Rails was way off.. ActiveRecord performance 6x-10x slower than MRI...
Looks like its getting better. Disclaimer: These are really really simple non-scientific tests.
Local mysql database, MYISAM, table people, with 2 columns, (id, name). 100k rows
> jruby -J-server -O script/console production
>> Benchmark.measure {10000.times {Person.find :first}}.total
=> 2.286
> ./script/console production
>> Benchmark.measure {10000.times {Person.find :first}}.total
=> 1.7
Mongrel
> ab -n 1000 http://localhost:3001/people/1
Requests per second: 95.02 [#/sec] (mean)
On Glassfish v3 with: RAILS_ENV=production jruby -J-server -O -S glassfish_rails glass2, after a bit of warmup.
> ab -n 1000 http://localhost:8080/glass2/people/1
Requests per second: 48.25 [#/sec] (mean)
turning logging mostly off
> ab -n 1000 http://localhost:8080/glass2/people/1
Requests per second: 56.70 [#/sec] (mean)
All in all, thats impressive. Congrats to Charles, Ola, and the whole JRuby crew. I dont think i'll be putting this in production yet, but I'm very interested to hear from others that have.
Posted by aaron
on Tuesday, October 30
In
Super fast IP to lat/lng in Rails, I showed a solution for fast IP to lat/lng resolution in rails. I called it "Super fast" because it performed orders of magnitude faster the the RESTful interface, but it was also "Super fast" to implement. That being said,
Kyle made a comment to check out the GeoIP gem. I had heard of
MaxMind before, but I didnt want to spend hundreds of dollars to solve this problem. What I didnt know was they also have a free download of their "lite" datasource. They have a
GeoLiteCountry and
GeoLiteCity version, although only the City version has lat/lng info. They provide wrappers in most languages (including Ruby), and while Kyle's suggested
geoip, I found
geoip_city on RubyForge which I like a bit better.
[UPDATED]: I had forgotten to include the install instructions for getting the GeoIP C library.
Install the C bindings, the gem (which isnt packaged as a gem for easy download) and get the data.
wget http://www.maxmind.com/download/geoip/api/c/GeoIP.tar.gz
tar -zxvf GeoIP.tar.gz
cd GeoIP
./configure && make && sudo make install
wget http://rubyforge.org/frs/download.php/27077/geoip_city-0.1.gem
sudo gem install geoip_city-0.1.gem
wget http://www.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
gunzip GeoLiteCity.dat.gz
sudo mkdir /usr/local/share/GeoIP
sudo mv GeoLiteCity.dat /usr/local/share/GeoIP/GeoLiteCity.dat
Then the ruby part:
>> require 'geoip_city'
>> g = GeoIPCity::Database.new('/usr/local/share/GeoIP/GeoLiteCity.dat')
>> res = g.look_up('4.2.2.2')
>> puts "lat: #{res[:latitude]} lng: #{res[:longitude]}"
lat: 38.0 lng: -97.0
So the question is (although its probably obvious), which is faster?
>> Benchmark.measure { 1000.times {Hostip.geocode('4.2.2.2')}}.total
=> 1.02
>> Benchmark.measure { 100000.times {g.look_up('4.2.2.2')}}.total
=> 0.5
Conclusion? The C library is MUCH faster than my database version. 200k req/s vs. 1k req/s. As a side note, I also tested the "geoip" gem, and it was about 3x faster than my database version.
But all in all, for the 5 minutes it took me to write the original version, it was fast enough... but MaxMind GeoLiteCity + the geoip_city gem is Super fast-er.
Posted by val
on Thursday, August 16
We found that sometimes monit fails to restart all mongrel instances after deployment and some of them end up running with the pid file gone. Since there is no pid, monit believes the instance is not running so it tries to start a new one on the same port and, of course, fails. Which leads to stale mongrel instances with old code. We’re investigating a long term solution but in the meantime have wrapped the mongrel_rails start script with a replacement which finds and kills the stale mongrel instances before starting a new one.
#!/usr/bin/env ruby
class MongrelController
def self.run_mongrel(args)
pid = extract_pid(args)
kill_stale_process(pid) if pid
system "/bin/mongrel_rails #{ args.join(' ') }"
end
def self.extract_pid(args)
(args[0] == 'start') && (i = args.index('-P')) && args[i + 1]
end
def self.kill_stale_process(pid)
mongrel_processes(pid).each { |p| process_running?(p) && Process.kill(9, p) }
end
def self.mongrel_processes(pid)
`ps axww -o 'pid command'`.split(/\n/).inject([]) do |mongrels, process|
mongrels << process[/^\s*(\d+)/][$1].to_i if process.match(%r{/bin/mongrel_rails\s.*\s-P\s#{ pid }\b})
mongrels
end
end
def self.process_running?(pid)
pid && (`ps -p #{ pid }`.split(/\n/).size == 2)
end
end
MongrelController.run_mongrel(ARGV)
Posted by val
on Tuesday, August 14
If you use
nagios for monitoring of your rails instances, you might want to get notification not only via email or
SMS-messages but to your
AIM when you are online. The script (
libexec/aim_notifier.rb) utilizes the
Net::TOC gem for sending out notifications:
#!/usr/bin/env ruby
require 'rubygems'
require 'net/toc'
user = 'your_bot_name'
password = 'bot_password'
msg = ARGV[0].to_s.gsub('\n', "\n")
client = Net::TOC.new(user, password)
client.connect
sleep 3
buddies = []
client.buddy_list.each_group { |g, b| buddies = b if g == 'Friends' }
buddies.each do |b|
b.send_im(msg) if b.available?
end
sleep 3
client.disconnect
You need to add any account you want to be notified to bot’s friends (either by logging to
AIM using the bot account or using Net::TOC’s ability to add friends).
The last piece is to add a new notifier in
etc/objects/commands.cfg as:
define command{
command_name notify-service-by-aim
command_line $USER1$/aim_notifier.rb $ARG1$ $ARG2$ "***** Nagios *****\n\nNotification Ty
pe: $NOTIFICATIONTYPE$\n\nService: $SERVICEDESC$\nHost: $HOSTALIAS$\nAddress: $HOSTADDRESS$\nState:
$SERVICESTATE$\n\nDate/Time: $LONGDATETIME$\n\nAdditional Info:\n\n$SERVICEOUTPUT$"
}
and to append it to the list of notifiers defined for a contact template in
etc/objects/commands.cfg:
service_notification_commands notify-service-by-email,notify-service-by-aim
Repeat the configuration if you want to use the AIM notification for hosts as well.