オライリーより定期便にて献本御礼。
良本。だがはじめに断っておくと、本書は 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分が超えられない - subtechRuby, Rails はエンタープライズに耐えうるか?
は正解だが、
Rails の便利機能つかってる?
は大外れ。それどころかActiveRecordをHashでやるやり方まで提案している。
その方法とは、
P. 21class 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
組み込みクラスも大胆に変更するのがRubyらしいと思うのですが。この変更で追加されるのも意外な機能でもないし。
あと、method_string[-1] == "="[0] は、
method_string[-1] == ?= がいいかな。