Dinner with Matz 0

Posted by adelcambre
on Monday, February 25

Last Thursday (February 21, 2008), I attended a dinner sponsored by the OTBC. The dinner was a chance for people in the open source community in Portland to meet a delegation of open source advocates from Shimane Prefecture in Japan, specifically Matsue.

Included in the delegation were professors from the university in Matsue, representatives from Hitachi and Sun Microsystems, and members of the open source community in Japan including the former head of the PostgreSQL user’s group in Japan.

The highlight of the dinner was Yukihiro “Matz” Matsumoto, the creator of the Ruby programming language. There were about 50 people in attendance at the dinner, but I was very lucky in that I was able to sit at the same table as Matz.

It was very cool to have such amazing access to Matz at this small dinner. We had a good mix of people and backgrounds at our table. Other than Matz and myself, there was Monty Williams from Gemstone and his wife, Ben Matasar from DabbleDB, Audrey Eschright a local community evangelist and member of PDX.rb, Michael Bunsen another PDX.rb member and creator of Urban Edibles, and Anselm Hook from Meedan.

This group allowed for some interesting discussions, we had Monty and Ben who are both smalltalk guys giving some insight into that side of the current sphere of programming. This included some discussions of Seaside the smalltalk web framework.

I had the chance to ask Matz some questions that I have been curious about for some time. Primarily involving the seeming current major transition period for Ruby where we are going from one interpreter which Matz wrote himself, at least 4 or 5 different virtual machines (old MRI, new MRI[YARV], IronRuby, jRuby, and Rubinius), most of which Matz hasn’t worked on. For the first time since Ruby was started, the mainline virtual machine was primarily not written by Matz. It was very interesting to see how he feels about this transition.

Overall the evening was a ton of fun. Lots of excellent discussions, and some amazing access to the creator my favorite programming language on the planet.

Was anybody else there? What was your experience like? Also, if I forgot anybody who was sitting at our table, please post a comment.

Masochistic Connection Proxy with Observers 2

Posted by adelcambre
on Thursday, November 15

On a recent project here at PLANET ARGON we needed to use ActiveRecord with a master and slave database setup. We started out using ActsAsReadonlyable but quickly ran into some nasty performance issues. After asking around a bit, the code ninjas over at ActiveReload mentioned that they had a plugin for splitting the ActiveRecord reads and writes to separate databases called Masochism

This worked much better than ActsAsReadonlyable from a performance perspective but there was an issue with some of our observers. Specifically observers that had conditionals which were contigent on the update that triggered the observer. Take the following (somewhat contrived) example:


class Beehive < ActiveRecord::Base
  has_many :bees
end

class Bee < ActiveRecord::Base
  belongs_to :beehive
end

class Bee < ActiveRecord::Observer
  def after_destroy(object)
    object.beehive.destroy if object.beehive.beehive.empty?
  end
end

So, destroy the behive when the last bee in the beehive is destroyed. The problem is that the beehive will only be destroyed if all of the bees have been destroyed but there is a race condition when the last bee is destroyed. The database replication has to push the DELETE down to the slave database before the observer gets run (which basically never happens).

The first solution was to simply wrap the observer in a with_master call (with_master is a method on the connection object in masochism to perform any database queries against the master database). It looked something like this:


class Bee < ActiveRecord::Observer
  def after_destroy(object)
    Comment.connection.with_master do
      object.beehive.destroy if object.beehive.bees.empty?
    end
  end
end

This solved the problem perfectly, the conditional now happens against the master database and will pass at all the right times. But it is a bit ugly to have the with_master call in the observer, the observer shouldn’t care whether it is using masochism or not. Also, we are only using masochism in production, so this breaks on our development copies (the connection only has the with_master method in production).

So after a bit of thinking, and a bit of hacking, I just added the with_master call to ActiveRecord::Observer itself when the plugin is loaded. Here is the patch I used:


Index: vendor/plugins/masochism/lib/active_reload/connection_proxy.rb
===================================================================
--- vendor/plugins/masochism/lib/active_reload/connection_proxy.rb    (revision 2039)
+++ vendor/plugins/masochism/lib/active_reload/connection_proxy.rb    (working copy)
@@ -20,6 +20,10 @@
     def self.setup_for(master, slave = nil)
       slave ||= ActiveRecord::Base
       slave.send :include, ActiveRecordConnectionMethods
+      # extend observer to always use the master database
+      # observers only get triggered on writes, so shouldn't be a performance hit
+      # removes a race condition if you are using conditionals in the observer
+      ActiveRecord::Observer.send :include, ActiveReload::ObserverExtensions
       ActiveRecord::Base.active_connections[slave.name] = new(master, slave)
     end

@@ -60,4 +64,21 @@
       connection.with_master { reload_without_master }
     end
   end
+  
+  module ObserverExtensions
+    def self.included(base)
+      base.alias_method_chain :update, :masterdb
+    end
+    
+    # Send observed_method(object) if the method exists.
+    def update_with_masterdb(observed_method, object) #:nodoc:
+      if object.class.connection.respond_to?(:with_master)
+        object.class.connection.with_master do
+          update_without_masterdb(observed_method, object)
+        end
+      else
+        update_without_masterdb(observed_method, object)
+      end
+    end
+  end
 end

There shouldn’t be much performance hit as observers should only be run during a database write (i.e. hitting the master database) anyway.

I am planning on sending it over to Rick Olson and maybe it will be included in masochism itself soon.

Are you using masochism? Are there other issues with observers? Is there a better way to do this (one thing I thought of is to just run observers in a transaction which masochism runs against the master_db as well)? Let me know in the comments.

Authenticated rss proxy 3

Posted by adelcambre
on Friday, August 17

At PLANET ARGON we use Basecamp for project tracking. Basecamp offers rss feeds of any new posts either globally or per project which is very handy for keeping track of things. The problem is that the rss feeds use http authentication which many feed readers (especially web based ones) don’t support. As a Google Reader user, I was out of luck.

After suffering through using email as my notification system, I decided to write a quick script to convert parameters to the url into http authentication. I initially considered doing this with cgi, but eventually decided to write a quick mongrel handler for the task. It actually turned out to be a lot easier than I thought it would be.

Here is the code:


require 'rubygems'
require 'mongrel'
require 'net/http'

HOSTNAME='your basecamp url'

class SimpleHandler < Mongrel::HttpHandler
  def process(request, response)
    params = {}
    request.params["QUERY_STRING"].split('&').each do |param|
      param = param.split('=')
      params[param[0]] = param[1]
    end
    response.start(200) do |head,out|
      head["Content-Type"] = "application/xml" 
      Net::HTTP.start(HOSTNAME) do |http|
        req = Net::HTTP::Get.new(params["url"])
        req.basic_auth params["user"], params["password"]
        response = http.request(req)
        out.write response.body
      end
    end
  end
end

h = Mongrel::HttpServer.new("0.0.0.0", "9898")
h.register("/feed", SimpleHandler.new)
h.run.join

It works really well, just run the script and point your feed reader at: http://hostname:9898/feed?user=<username>&password=<password>&url=<path of the rss feed>

for a normal global basecamp feed, it would look like: http://hostname:9898/feed?user=andy.delcambre&password=mypassword&url=/feed/recent_items_rss

Robby has confirmed that this works with open id basecamp logins as well.

This could be easily used with any authenticated rss feed you wanted. My goal was to make it general enough and easy enough for everyone at PLANET ARGON to use.

I had no idea it was so easy to add an http server to your script with mongrel. Zed Shaw is the man.