オライリーより定期便にて献本御礼。

良本。だがはじめに断っておくと、本書は Ruby や Ruby on Rails の本ではない。Ruby on Rails入門をお探しであれば「10日でおぼえる Ruby on Rails入門教室」を勧めておく。

データベースを多用するエンタープライズWebサイトを、どうやってスケールアウトするように設計するかの指針を示した本である。本書が「エンタープライズRails」となっているのは、著者の選択肢がたまたま Ruby on Rails と PostgreSQL だったというだけで、本書の知見は PHP + MySQL だろうが Catalyst + Oracle だろうが使える。「キャパシティプランニング」に連なる一冊だ

本書「エンタープライズRails」は、AmazonでエンタプライズWebに携わったのち、Ruby on Rais のスタートアップ企業である CourseAdvisor (2007年ワシントンポストが買収)で開発を指揮してきた著者が示した、「Ruby on Railsは遅い」に対する見事な反論。

目次 - Book:エンタープライズ Railsより抜粋
監訳者まえがき
はじめに
1章 全体像
2章 プラグインによる構成
3章 モジュールによる構成
4章 砦としてのデータベース
5章 堅牢なデータモデルの構築
6章 第3正規形へのリファクタリング
7章 ドメインデータ
8章 複合キーとドメインキー正規形
9章 トリガーを使って複雑な関係を保証する
10章 多重テーブル継承
11章 View-Backedモデル
12章 マテリアライズドビュー
13章 SOA入門
14章 SOAの検討
15章 XML-RPCサービス
16章 サービスへのリファクタリング
17章 REST入門
18章 RESTful Webサービス
19章 エンドツーエンドのキャッシュ
索引

それでは、何が遅いのか。

それはデータベース構成であり、キャッシュ構成であり、そしてRESTfulnessだ。本書ではこれらを学(んで|なおして)いく。本書に出てくるコードの量は、RubyよりもむしろSQLの方が多いのではないかと思えるほど。そして時には「反Rails的」な作業すら厭わない。

というわけで、

2009-07-23 - 8時40分が超えられない - subtech
Ruby, Rails はエンタープライズに耐えうるか?

は正解だが、

Rails の便利機能つかってる?

は大外れ。それどころかActiveRecordをHashでやるやり方まで提案している。

その方法とは、

P. 21
class Hash
  def method_missing(method, *params)
    method_string = method.to_s
    if method_string.last == "="
      self[method_string[0..-2]] = params.first
    else
      self[method_string]
    end
  end
end

と、Hashクラスそのものを改造した上で、

MyObject.find(:all)

となっている箇所を

MyObject.connection.select_all("select * from my_object")

として、四万オブジェクトへのアクセスを7秒から3秒に縮めたというものであるが、これには「ときどきRubyist」にすぎない私ですらつっこみどころがいくつかある。

まず、元のコードはそのままではRuby 1.8.xでは動かないということ。確かにRuby on RailsではString#lastを追加するのだけれども、こういう言語そのものを拡張するような場合に、Railsに依存するというのは不気味というしかない。

次に、Hashのような基礎的なオブジェクトを、普通のCGIのように起動されてから終了するまでの寿命しかない環境ならとにかく、永続的に使われることが多い環境で、しかもよりによってmethod_missingを追加するという手法でやるのはあまりにマナー違反がひどいのではないかということ。こういう場合は、Hashを継承したクラスを作ってやるのがOOPの作法ではあるが、しかしMyObject.connection.select_all()の返す結果は素のHash。どうしたらよいか。

せっかくRubyを使っているのだから、私ならこうするというのが、以下の例である。

module KMHash
  def method_missing(method, *params)
    method_string = method.to_s
    if (method_string[-1] == "="[0])
      self[method_string[0..-2]] = params.first
    else
      self[method_string]
    end
  end
end

# this works
dan = Hash.new.extend KMHash
dan.kogai = 'blogger'
p dan.kogai

# this throws an exception
matz = Hash.new
matz.yukihiro = 'rubyist'
p matz.yukihiro

Rubyらしく、特異メソッドを追加するという方法を使ってみた。特異メソッドでもmethod_missiongが追加できるというのは素敵だ。余談だが、あるオブジェクトを一時的に別のクラスに属させたい場合、Rubyにおける作法というのはどうなっているのだろう。Perlの場合、一時的にblessしなおすという方法が使えるのだが(rubyにおいては、これはobj.class = whateverに相当するが、これは動かない)。やはり特異メソッドでいいのだろうか。

あと、本書の主題であるDB、キャッシュ、RESTfulnessの三つに関して、最後のRESTfullnessが弱く感じた。最もRESTに関しては「RESTful Webサービス」というRESTでまるごと一冊があるので、本書では一章さくにとどめるでよかったのかも知れないが、少なくとも「後で後悔しないURI設計」に関しては語っておくべきではなかったのか。これはHashの拡張ポリシーに通用するのだけれども、著者に限らずRails使いというのは「一度その目的で使われてしまった名前はもう後で使えない」ということにものすごく無頓着な印象がある。

以上に見られるように、著者はあくまでエンタプライズWebのエキスパートであって、Rubyという言語や、Ruby on Railsというフレームワークのエキスパートとは違うという印象を強く受けた。その意味で本書は Ruby (on Rails)? がらみの本としては異色であり、それだけに私のような「外国人」にも得るところの大きな一冊だった。Ruby on Rails はおろか、Rubyを使っていない他の読者にとってもそのようになるのではないだろうか。

Dan the Man with too Many Frameworks to Reframe