05-13-2014, 04:09 PM
★point
CSVデータをDTOに詰めてはいけません、絶対に。
今回は、Java に詳しい方には、「そんなの当然」といわれてしまいそうな話なのですが、
Curl ORB を利用して、サーバからCSVデータをダウンロードしたい、という場合、
私などは、CSV形式に整形した文字列を、DTOのフィールドに詰めて、
クライアント側でその値を、ファイル出力(write-to)すればいいじゃん、と簡単に考えてしまいます。
しかし、サーバのメモリは有限なので、CSVデータの容量や、同時利用人数によっては、
CSVデータのためのメモリが確保できず、OutOfMemory 例外が出てしまう可能性があります。
実際にこれで痛い目にもあいました。
この問題を回避するためには、下記のような処理を実装する方法が考えられます。
①サーバでは、CSVデータを、テンポラリとして実ファイルに出力する ※1、2
②サーバからクライアントに、①で出力したファイルの「パス」をDTOのフィールドに詰めて返却する※3
③クライアントは、受け取ったファイルパスからファイルをダウンロードする ※4、5
注意
※1:出力前のCSVデータ自体の確保でOutOfMemoryが出ないように注意すること
具体的には、DBから取得したデータをまとめてリストで保持するのではなく、
たとえば、FETCHデータを1行ごとファイルへ追記出力するなどの処理が必要
(iBatis であれば、RowHandler を利用して、1行ごとに処理することが可能)
■参考「メモリー管理に失敗したJavaアプリの実例」
http://thinkit.co.jp/story/2011/03/11/2051
※2:テンポラリファイルのパスは、クライアントからhttpでアクセスできるパスであること
また、ファイル名にはyyyymmdd_hhmmss+ミリ秒をつけて、同時利用時でもファイル名が衝突しないように注意
※3:テンポラリのファイルパスとは別に、クライアントで保存する際の初期提案用ファイル名も必要
※4:ファイルダウンロード時の例外ハンドリング、
保存ダイアログの初期表示フォルダを指定する機能なども実装すること
※5:サーバで実行される日時バッチなどを組み、定期的にテンポラリファイルを削除する機能を実装すること
もちろん、サーバのメモリが潤沢であったり、出力するCSVデータ容量が極小であったり、
DTOに詰めてやり取りしても問題がない場合はあるので、
上記のような、やや面倒な実装を忌避したくなる気持ちは十分すぎるほどわかります。
しかし、データは肥大化していくものですので、
運用開始直後は問題なく動いていても、ダウンロードの対象データが大きくなるにつれ、
ある時から急に問題が発生するようになる、ということも考えられます。
アプリケーションの将来性を考えるうえでも、
企業で利用するアプリケーションの基盤としては、
絶対に「DTOのなかにCSVを詰めない」という方針を取ったほうが良いと思っています。
CSVデータをDTOに詰めてはいけません、絶対に。
今回は、Java に詳しい方には、「そんなの当然」といわれてしまいそうな話なのですが、
Curl ORB を利用して、サーバからCSVデータをダウンロードしたい、という場合、
私などは、CSV形式に整形した文字列を、DTOのフィールドに詰めて、
クライアント側でその値を、ファイル出力(write-to)すればいいじゃん、と簡単に考えてしまいます。
しかし、サーバのメモリは有限なので、CSVデータの容量や、同時利用人数によっては、
CSVデータのためのメモリが確保できず、OutOfMemory 例外が出てしまう可能性があります。
実際にこれで痛い目にもあいました。
この問題を回避するためには、下記のような処理を実装する方法が考えられます。
①サーバでは、CSVデータを、テンポラリとして実ファイルに出力する ※1、2
②サーバからクライアントに、①で出力したファイルの「パス」をDTOのフィールドに詰めて返却する※3
③クライアントは、受け取ったファイルパスからファイルをダウンロードする ※4、5
注意
※1:出力前のCSVデータ自体の確保でOutOfMemoryが出ないように注意すること
具体的には、DBから取得したデータをまとめてリストで保持するのではなく、
たとえば、FETCHデータを1行ごとファイルへ追記出力するなどの処理が必要
(iBatis であれば、RowHandler を利用して、1行ごとに処理することが可能)
■参考「メモリー管理に失敗したJavaアプリの実例」
http://thinkit.co.jp/story/2011/03/11/2051
※2:テンポラリファイルのパスは、クライアントからhttpでアクセスできるパスであること
また、ファイル名にはyyyymmdd_hhmmss+ミリ秒をつけて、同時利用時でもファイル名が衝突しないように注意
※3:テンポラリのファイルパスとは別に、クライアントで保存する際の初期提案用ファイル名も必要
※4:ファイルダウンロード時の例外ハンドリング、
保存ダイアログの初期表示フォルダを指定する機能なども実装すること
※5:サーバで実行される日時バッチなどを組み、定期的にテンポラリファイルを削除する機能を実装すること
もちろん、サーバのメモリが潤沢であったり、出力するCSVデータ容量が極小であったり、
DTOに詰めてやり取りしても問題がない場合はあるので、
上記のような、やや面倒な実装を忌避したくなる気持ちは十分すぎるほどわかります。
しかし、データは肥大化していくものですので、
運用開始直後は問題なく動いていても、ダウンロードの対象データが大きくなるにつれ、
ある時から急に問題が発生するようになる、ということも考えられます。
アプリケーションの将来性を考えるうえでも、
企業で利用するアプリケーションの基盤としては、
絶対に「DTOのなかにCSVを詰めない」という方針を取ったほうが良いと思っています。