情報共有です。まず、前提から。
hajimeniさんがブログで詳しく紹介してくれています。
// Connector/JでOutOfMemoryError
http://d.hatena.ne.jp/hajimeni/20120406/1333677789
<恥ずかしながら衝撃の事実>
ということで、個人的にも衝撃的だったのですが、
MySQLではカーソル検索、つまり selectCursor() をやっても、
(デフォルトでは)メモリ対策にはなりません。
ResultSetが検索した瞬間に全件持ってしまっているので。
<ちょっとした疑問>
でも、なんでももっと早くこの問題が話題にならなかったか?
(他のO/Rマッパでも同じ話、どっかで誰かの耳に入ってもよさそう)
現在のインフラだとある程度の件数の保持は耐えられちゃう、
ってところがあるかと考えています。
実際に、実案件で8万件のCSV出力でうまくいったと報告があります。
ただ、12万件では OutOfMemoryError になったという報告も。
漠然としてはいますが、つまり何万件程度であれば耐えてしまうと。
<デフォルトにはできない>
DBFluteで、MySQLのときだけ Integer.MIN_VALUE をデフォルトに、
としたいところですが、ちょっとそう踏み切れない悩ましい問題が。
一つ、そうすると今度は CursorHandler の中でのDBアクセスが
できなくなってしまい、DBFluteのアップグレードで既存アプリの
プログラムが動かなくなってしまう可能性があること。
どうやら、Integer.MIN_VALUE にして ResultSet のDB接続を
開いたままにすると、その同じ Connection で別のSQLを実行する
ことができないようです。実際にやると例外が発生します。
(他のDBMSでは問題なくフェッチ中でもDBアクセスができる)
業務的には CursorHandler の中のDBアクセスは別トランザクションで
やることが多いわけで、新しく実装する場合は全然良いのですが、
やはり、少ないとはいえ既存プログラムで落ちる可能性があるというのは、
DBFluteの運用としてはなかなか許容しづらいもので。
(コンパイルエラーにできるならまだしも)
二つ、カーソル検索でも通常は一レコードずつフェッチするのではなく、
JDBCドライバの中である程度は「束」でデータをフェッチしてきて、
I/Oのパフォーマンス劣化を防いでいたりするものです。
(って、正確なところは不明ですが、そのために fetchSize って概念があって)
しかし、MySQLでフェッチするモードの場合は、
「恐らく」一レコードずつフェッチしているように見えるので、
逆に既存のカーソル検索利用箇所の処理が遅くなってしまう可能性もあり、
一つ目ほど決定的ではありませんが、踏み切る足枷の一つになります。
また、実績という面でも、MySQLのフェッチ実装がどれだけ信頼できるか、
そういうところでも不安があり、デフォルトにはなかなか踏み切れないと。
<とりあえずオプションで指定>
DBFluteでは、そろそろリリース予定の DBFlute-0.9.9.4A にて、
dfprop の設定で「カーソル検索のときのFetchSize」を指定できる
ようにしました。そこで Integer.MIN_VALUE と指定すればOKです。
(他のDBでもパフォーマンス調整で活用できるので非DB依存のプロパティで)
もし、これをデフォルトにするぞーと意気込むときが来ても、
すごく簡単に実装できるようにということで、このような形に。
センシティブなところなので、単体テストめちゃ書きました。
<それでもまだ悩む>
時限爆弾を抱えるよりアップグレードで落ちる方がまだ幸せ、
SandBoxだからできること、いまのうちにやっておくか...
(このままだと時限爆弾は増えていくわけだし)
って考えて「えいやっ」とデフォルトの設定にするか悩むところです。