jOOQ:fetchLazyとJDBCドライバの問題



jOOQではfetch()を行うと、対象のデータはすべてメモリ上に読み込まれ、即座にJDBCコネクションがリリースされる(通常はコネクションプールに返却)。しかしこれでは大きなデータをSELECTしたい時に困ってしまう。OutOfMemoryErrorになる可能性があるからだ。

これに対応するために、jOOQにはfetchLazy()というメソッドがあるのだが、結局これを使っても問題は解決しないようだ。つまり、JDBCドライバ自体がResultSetを(どう設定しても)すべてメモリ上にロードしてしまう場合があるため、jOOQで何を使おうが結局OutOfMemoryErrorになる可能性がある。

ともあれ、最初にfetchLazyを説明する。

fetchLazy

これは簡単だ、以下に説明がある。

create.selectFrom(BOOK).fetch().forEach(book-> {
 ....
});

などとやる代わりに、以下とする。簡単だ。

try (Cursor<BookRecord> cursor = create.selectFrom(BOOK).fetchLazy()) {
  while (cursor.hasNext()) {
    BookRecord book = cursor.fetchOne();        
    ....
  }
}

JDBCドライバのフェッチサイズ

上は問題なさそうに見えるのだが、しかし気になることが書いてある。

While using a Cursor prevents jOOQ from eager fetching all data into memory, your underlying JDBC driver may still do that. To configure a fetch size in your JDBC driver, use ResultQuery.fetchSize(int), which specifies the JDBC Statement.setFetchSize(int) when executing the query. Please refer to your JDBC driver manual to learn about fetch sizes and their possible defaults and limitations.

要するにjOOQ側でCursorを使って一行ずつ読み込んでも、JDBCドライバはそれとは無関係に全行をメモリにロードしちゃうかもよ。その場合には、 ResultQuery.fetchSize(int)を使ってフェッチするサイズを制御してくれという。JDBCドライバのマニュアルを見てくれという。

つまり、何も考えずにjOOQのCursorを使えば良いわけではないのだ。。。

JDBCドライバのフェッチサイズが制御不能

しかし、JDBCドライバによって挙動が変わる上に、特にMariaDBでは実際上フェッチサイズを指定できないらしい。以下のような書き込みがある。

色々と面倒なので可能な限り、fetchLazyは使わないことにした。こんな不安な思いをするよりも、fetchLazyが必要になるようなプログラムを書かないに限る。