torutkのブログ

ソフトウェア・エンジニアのブログ

添付したサイズの大きなファイルをダウンロードするとパンクする

物理メモリ1GB、CentOS 5.5, Redmine 1.3.2, Passengerの環境です。
数百MBのファイルを添付してますが、そのダウンロードができず、かつRedmineへの他のアクセスができず、マシンへssh接続したコンソールの応答が異常に重い状態となりました。

topコマンドでみると、物理メモリの空きがなく、スワップを大量に使っており、プロセス*1RSSが数百MBとなっています。いわゆるスラッシング状態となってしまったようです。

Redmineに添付したファイルをダウンロードすると、ファイルをメモリに一括読み込みして処理するような動きです。

以前、同様の環境で数百MBの添付ファイルを扱っていた際はパンク状態になることはなく使えてましたが、そのマシンは物理メモリが3GBあったため、数百MBのファイルダウンロードについてはスラッシングに陥ることはなかったと思われます。

対策調査

原因は、次のURLと同じと思われます。
プログラマーのメモ。要チェックや! : win apache railsで巨大ファイルのsend_fileでメモリリークを防ぐ。mod_xsendfileを使う方法

この記事には、

send_fileで大きいファイルを送ると、rubyのタスクでメモリがファイル容量分増えて、大きいファイルを送るとメモリ使用量がすごいことになる。
調べてみたら、mod_xsendfileで解消できるみたい。

とあり、apacheにmod_xsendfile を入れることで解消するとあります。

Redmineについての対処は、英語ですが次のURLに端的に記述されています。
enabling x_sendfile to efficiently send large files - Redmine

  • mod_xsendfileをインストール
  • httpd.confに設定を追加
  • attachments controller(Rubyで書かれたRedmineのソースファイル)を x_sendfile を使用するよう修正

となります。

対処メモ(CentOS 5.5)

CentOS 5.5の場合、mod_xsendfileパッケージはepelにあるので、yumでepelからmod_xsendfileをインストールします。

httpd.confの設定については、mod_xsendfileをパッケージでインストールすると、/etc/httpd/conf.d/xsendfile.conf が置かれるので、ここに追記しました(ここでなくてもよいかも)。

XSendFile on
XSendFilePath /var/lib/redmine-1.3.2/files
  • XSendFilePathの記述は、実パス(≠シンボリックリンク)でないとダウンロード時にファイルが見つからないエラーが発生します。

attachments controller の修正は、

add ":x_sendfile=>true" to the end of "def download" in attachments_controller.rb

です。

この処置でhttpdを再起動して数百MBの添付ファイルをダウンロードしたら問題なく動くようになりました。

*1:確かhttpdだったと思う