Merb/DataMapperをしばらく使っていたのですが、 少なくともバージョン0.9.10, 0.9.11では、 associationの実装にバグがあり、 レコード数が多いテーブルがあると、aggregation系の処理に時間がかかるという問題がある事が分かりました。

例えば、Post.has n, :comments な関係がある時に、 以下のようなコードを実行すると、このようになります。

   1  ?> Post.first.comments.count #=> 188
   2   ~ (0.000865) SELECT "id" FROM "posts" ORDER BY "id" LIMIT 1
   3   ~ (0.000094) SELECT "id", "post_id" FROM "comments" WHERE ("post_id" IN (1)) ORDER BY "id"
   4   ~ (0.000063) SELECT COUNT(*) FROM "comments" WHERE ("post_id" = 1)

2つ目のSQLは不要なのですが、発行されてしまいます。 例えばCommentのレコード数が多かった場合、Commentオブジェクトを大量に生成しようとしてしまうため、致命的な遅さになってしまいます。

目下この問題の解決策を探しているところですが、 Edge-DMのspec/仕様でerrorが出ている状態なので、なかなか手が付けられない感じです。 とりあえず、dm-coreのassociations/relationship.rbの中の、

   1        # @api private
   2        def get_children(parent, options = {}, finder = :all, *args)
   3          parent_value = parent_key.get(parent)
   4          bind_values  = [ parent_value ]
   5  
   6          with_repository(child_model) do |r|
   7            parent_identity_map = parent.repository.identity_map(parent_model)
   8  
   9            query_values = parent_identity_map.keys
  10            bind_values = query_values unless query_values.empty?
  11            query = child_key.zip(bind_values.transpose).to_hash
  12            collection = child_model.send(finder, *(args.dup << @query.merge(optio
  13  ns).merge(query)))
  14  
  15            return collection unless collection.kind_of?(Collection) && collection.any?

の最後の collection.any? で件のSQLが実行されているところまでは分かりました。 DataMapper::CollectionはextlibのLazyArrayを継承しているクラスなのですが、どうもそのへんの仕様が変わったのに追従できてないのかな。 モジュールを過度に分散しすぎるのも、整合性を保つのが大変になるという問題がありますね。 注意深く完全なSpecを書く事を心がけていれば防げる問題かもしれないですが。

posted by Png genki on Wed 8 Apr 2009 at 05:06

桜が散る前に、4/8の夜21時よりMerbJog#2を開催いたします。

詳細・参加申請は以下のサイトをご覧ください。

飛び入り参加もOKです。

よろしくお願いします。

posted by Png genki on Tue 7 Apr 2009 at 06:01

度々忘れるのでメモしておきます。

   1  Merb::ControllerExceptions.constants.sort #=> ["Accepted", "ActionNotFound", "BadGateway", "BadRequest", "Base", "ClientError", "Conflict", "Continue", "Created", "ExpectationFailed", "Forbidden", "GatewayTimeout", "Gone", "HTTPVersionNotSupported", "Informational", "InternalServerError", "LayoutNotFound", "LengthRequired", "MethodNotAllowed", "MovedPermanently", "MovedTemporarily", "MultiPartParseError", "MultipleChoices", "NoContent", "NonAuthoritativeInformation", "NotAcceptable", "NotFound", "NotImplemented", "NotModified", "OK", "PartialContent", "PaymentRequired", "PreconditionFailed", "ProxyAuthenticationRequired", "Redirection", "RequestEntityTooLarge", "RequestRangeNotSatisfiable", "RequestTimeout", "RequestURITooLarge", "ResetContent", "STATUS_CODES", "SeeOther", "ServerError", "ServiceUnavailable", "Successful", "SwitchingProtocols", "TemplateNotFound", "TemporaryRedirect", "Unauthorized", "UnsupportedMediaType", "UseProxy"]

posted by Png genki on Sat 4 Apr 2009 at 22:58

DataMapper#auto_migrate!を実行すると、以下のようなコードが実行される。

   1  self.auto_migrate!(repository_name = nil)
   2    AutoMigrator.auto_migrate(repository_name)
   3  end

AutoMigrator#auto_migrateは以下のようになっている。

   1  def self.auto_migrate(repository_name = nil, *descendants)
   2    auto_migrate_down(repository_name, *descendants)
   3    auto_migrate_up(repository_name, *descendants)
   4  end

第二引数以降でDMのクラスリストを指定できる。 省略すると、DataMapper::Resource.decendantsが指定されたものとして動作する。 特定のリソースだけまとめてauto_migrateするには便利そうだ。

posted by Png genki on Sat 4 Apr 2009 at 21:58

急遽大門駅付近で開催されることになったCouchDB勉強会のレポートです。

参加者: @maiha, @yugui, @yamaz, @takiuchi

そもそもCouchDBは何かというと、 Apacheのプロジェクト で、分散、耐障害性、スキーマフリー、ドキュメント指向なデータベスで、 RESTfulなAPIを使って制御します。

結構前から存在していたのですが、取りかかるきっかけがなくてスルーしていました。 しかし、dm-couchdb-adapterを使ってMerb/DataMapperで利用可能という事が分かり、にわかに盛り上がってきました。

早速、couchdbをインストールします。 いまのところ、ソースからcouchdb-0.9.0をインストールするのが一番良いようです。 macportsのcouchdb-0.9.0aでは動作が微妙に異なっているようでうまく動きませんでした。

dm-couchdb-adapterは、dm-moreにバンドルされているのですが、 そのままでは一部機能が利用できなかったので、 問題の分析を行いながら、 @maihaさんが改良を加えていきました。 改良版はこちらにあります。 使ってみたい場合は、maiha/dm-moreをcloneしてきて、

   1  % cd adapters/dm-couchdb-adapter
   2  % sudo rake install

すればOKです。 基本的な使い方は上記GitHubリポジトリのREADMEを読めば分かります。

結論として、普通にMerbアプリを作れるようになりました。 試しに作りかけの社内ツール的なものをMerb/DM/CouchDBの構成にしてみました。

http://watch.s21g.com

Future Worksとしては、

  • read_manyでのorderの指定。emitの第一引数を使う。逆順をどうするか。
  • count以外のaggregate (max, min, sum, avg) のサポート

などがありますね。

posted by Png genki on Fri 3 Apr 2009 at 14:46

あのAsakusa.rbから、dm-paginationバージョン0.3.2がリリースされました。

dm-pagination-0.3.2 is out from Asakusa.rb

最近の改善が取り込まれています。 dm-paginationはMerb with DataMapper用のpaginationを提供するプラグインです。 Rails用の pagination_scope の姉妹プラグインです。

See Also

posted by Png genki on Tue 31 Mar 2009 at 10:45

Passengerは非常に便利なのですが、 通常の設定では、アプリケーションへのアクセスが無い状態が5分程度続くと、プロセスがkillされてしまい、次回にアクセスする時に時間がかかるようになります。

これを回避するためには、httpd.confなどで、以下のように指定します。

   1  PassengerPoolIdleTime 0

これで、プロセス数の限界に達して追い出されるまで、Idle時間によってkillされる事は無くなります。

See Also

posted by Png genki on Sat 28 Mar 2009 at 20:36

Rails向けのiPhoneサイト作成用プラグインのメモです。

See Also

posted by Png genki on Wed 11 Mar 2009 at 08:30

@maihaさんを中心として、私やMerb勉強会の参加者によって作り続けていたMerbのRuby-1.9.1対応のパッチ群が、Merb本体に取り込まれました。

ss

http://github.com/wycats/merb/commits/

Merb勉強会での活動が実を結んだわけです。 めでたいです。ありがとうございます。

Merb-1.1では、Ruby-1.9.1に対応したMerbがリリースされそうです。

Merb 1.1 roadmap

posted by Png genki on Wed 11 Mar 2009 at 06:01

MerbでExpiresやCache-Controlを設定する場合、 アクションメソッドの中で、以下のようにすれば良いようです。

   1  headers['Cache-Control'] = 'public'
   2  headers['Expires'] = 3.days.from_now.utc.rfc2822

posted by Png genki on Thu 26 Feb 2009 at 18:41