2011年1月24日月曜日

railsにおけるデータファイルの出力三段階

画面で選択した検索条件に基づき、データをファイル出力したいという要件は、非常に多いですが、基本に忠実な実装しないとメモリー不足等で大変な事になります。メモリーの利用方法と、応答速度を意識して、改善していきます。

  • メモリー展開+ファイル作成

@posts = Post.all

open(file_name, "wb") do |f|
  @posts.each do |post|
    f.write(post.to_csv + "\r\n")
  end
end

send_file(file_name)

長所
  簡単

短所
  @postsが大量データだった場合に、メモリー不足に陥ります。
  ファイルが出来上がるまで応答を返せない。

  • フェッチループ+ファイル作成

open(file_name, "wb") do |f|
  Post.connection.raw_connection.query("select * from posts", :cache_rows => false).each do |row|
    f.write("#{row[0]},#{row[1]}\r\n")
  end
end

send_file(file_name)

長所
  同時に必要なメモリーが削減する事ができる。

短所
  生SQLを書く必要があり、少しとはいえDB依存のコード(raw connection)を書く必要あり。
  ファイルが出来上がるまで応答を返せない。

  なおrailsにはbatch方式でのquery発行もありますが、複数回SQLを実行してしまうのでスピード面で問題が出る事が多いです。
  • フェッチループ+ネットワーク転送

headers.merge!(
  "Content-Type"              => "application/octet-stream",
  'Content-Disposition'       => "attachment; filename=\"test.csv\"",
  'Content-Transfer-Encoding' => 'binary'
)

self.response_body = proc { |r, o|
  Post.connection.raw_connection.query("select * from posts", :cache_rows => false).each do |row|
    o.write("#{row[0]},#{row[1]}\r\n")
  end
}

長所
  同時に必要なメモリーが削減する事ができる。
  途中で応答が返せる(ネットワーク例外を途中で捕捉できるので尚良しです)。

短所
  生SQLを書く必要があり、少しとはいえDB依存のコード(raw connection)を書く必要あり。

※webrickでは応答は最後になってしまうので注意
※rails3になってまた変わったみたい。 http://stackoverflow.com/questions/3507594/ruby-on-rails-3-streaming-data-through-rails-to-client

  • 結論

要件に応じてメモリー展開+ネットワーク転送(簡単)、もしくは、フェッチループ+ネットワーク転送(無敵)が良いと思う。

2011/01/25 追記


よく考えたらrails3になってArelが導入されたおかげで、sqlは構築するのが楽になりましたね。。

Post.where("created_at < ?", Time.now).to_sql

0 件のコメント:

コメントを投稿