メモリー管理に失敗したJavaアプリの実例
O/Rマッピング・ツールと運用設計ミス
本稿では、Java EEアプリケーションで使われることの多いO/Rマッピング・ツールにおけるサイジングの重要性について解説します。
今回取り上げる事例は、JavaのWebアプリケーション開発ではかなり有名なO/Rマッピング・ツールである「iBatis」*1を使ったものです。
- [*1] 現在は、MyBatisと名前を変えて、継続的にリリースされています。
- [mybatis[http://www.mybatis.org/java.html]] ,
- [google code:mybatis[http://code.google.com/p/mybatis/]]
問題となったアプリケーションでは、データベースの接続からクエリーを投げるところまで、全面的にiBatisを利用していました。採用された理由は、既存のSQLを利用しつつ、動的SQLを簡単に作成できるところでした。初回リリース後しばらく経過しても、特に大きな問題もなく、システムは順調に利用されていました。
その後、機能改善と追加機能の要望が挙がりました。システムは安定稼働しているので、拡張しても問題ないだろうと判断して改修することになりました。この際に、定期バッチ処理で使っていた一括検索処理を一部、Webアプリケーションへ移行することになりました。
この検索処理は、定期的にデータベースから特定期間のデータをCSVで出力するものです。このバッチ側ではiBatisは利用していませんでしたが、SQLの部分は、ほぼそのままWebアプリケーションへ移植することができました。
図1: アプリケーションの機能を改修した事例 |
テスト時には、特に問題もなく動作していたので、そのままリリースすることになったのですが、しばらくした後、問題が発生します。
ある日、Webアプリケーションへ移行した一括検索処理を実行すると、とある条件に限ってシステムが停止するとの報告を受けます。ログを調べてみると、OutOfMemoryが発生しており、そのエラーはiBatisを使った検索処理にて発生していました。
しかしこれは、かつてバッチ処理で実装していたときには、問題がなかった条件です。偶発的なものなのかどうかを調査するため、数回同じように処理をさせましたが、かなりの確率で同じエラーとなります。
障害を細かく追跡し、たどり着いた問題のコードは、以下のようなものでした。
これは、通常のiBatisの利用方法です。しかし実際には、この通常の利用方法が大問題です。問題を引き起こした原因は、以下の条件が重なった結果でした。
- システム改修のタイミングで、他システムからのデータが大量に追加された
- このコードをWebアプリケーションに移植した際の、単体機能のテストでは問題がなかった
- システム改修後、複数のユーザーが同時に処理を行う運用に変わっていた
結果、検索結果となるList
これは、想定されるデータ・サイズの見積もりミスとも言えますし、利用条件のミス・マッチとも取れますし、追加要件に対応したコードになっていないとも考えられます。
しかし、システム稼働後には、ヒープ・サイズの変更もできず、ハードウエアの増設も簡単にはできません。このような状況になった場合、どのように対応するのが望ましいのでしょうか。