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

最近の釣果ならぬYak Shaving状況:

  • watch.s21g.com が重たくて固まる
  • CouchDB-adapterを疑ってmysqlに戻す
  • まだ遅いのでdm-aggregatesを調査
  • dm-core が原因っぽいのでdm-coreのソースを見る
  • dm-core のスペックが通らない
  • postgresが必要なのでインストール
  • Mac用インストーラではdo_postgresがmakeできない
  • postgresをソースからインストール <- いまここ

ということで、MacOSにPostgresを入れるためにユーザを作成する手順のメモ。

   1  # dscl . -create /Users/postgres
   2  # dscl . -create /Users/postgres UserShell /bin/bash 
   3  # dscl . -create /Users/postgres RealName "Postgres"
   4  # dscl . -create /Users/postgres UniqueID 451
   5  # dscl . -create /Users/postgres PrimaryGroupID 451
   6  # dscl . -create /Users/postgres NFSHomeDirectory /usr/local/pgsql

UID/GIDの根拠は自信が無い。UIDは500未満にしておくとログイン画面にでないようになるそうだ。

Alas, life is full of yakshaving!

See Also

posted by Png genki on Tue 7 Apr 2009 at 23:47

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

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

飛び入り参加もOKです。

よろしくお願いします。

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

DataMapperはSQLを解さないアダプタも利用可能なので、この方法でSQLが実行できるかどうかはアダプタを選びますが、一般的には以下のようにすれば直接SQLを実行できます。

   1  Post.repository.adapter.execute("sql string")

結果は、例えばmysqlを使っている場合にはDataObjects::Mysql::Resultが帰ってきます。

posted by Png genki on Tue 7 Apr 2009 at 00:57

DataMapperでSetterメソッドを上書きして、プロパティの設定をコントロールするには、以下のようにします。

   1    def uri=(uri)
   2      attribute_set :uri, ::URI.parse(uri).normalize.to_s
   3    end

posted by Png genki on Sun 5 Apr 2009 at 06:56

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

   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

Merb/DataMapper/CouchDBの環境で rspec を使う場合には、 spec_helper.rb で以下のように設定すると良いようです。

   1    config.before(:all) do
   2      DataMapper::AutoMigrator.auto_migrate(nil,
   3        *(DataMapper::Resource.descendants - [Merb::DataMapperSessionStore]))
   4    end

セッションストアを使っている場合は、 Merb::DataMapperSessionStore用のrepositoryを 別途指定すると良いと思います。

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

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

今週末の4/5(日)にMerbJogを開催いたします。 Merb/Rubyなど、プログラミングに関する会話をしながら皇居の周りを走ります。 詳細・参加登録は以下のページをご覧ください。

http://atnd.org/events/510

開催時刻は多少変動する可能性があります。 雨天、悪天候の場合は延期となります。 ご了承ください。

posted by Png genki on Fri 3 Apr 2009 at 16:34

急遽大門駅付近で開催されることになった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