SlickでStackOverflowErrorが発生する場合の対処法

GitBucketに以前からGitBucketのバッシュレポートでStackOverflowErrorが発生するというバグレポートが上がっていたのですが、最近直接の知り合いからも言われたので重い腰を上げて修正にトライしてみることにしました。

github.com

原因は以下のようなコードで、検索条件をORで繋げている部分が、数が増えるとスタックを食いつぶしてしまうというものでした。

val seq = Range(1, 500)
Accounts
  .filter { t => seq.map { x => t.userId === x.bind }
  .reduce(_ || _)}.list

実は当時Slickにもイシューを上げてありました。

github.com

ただ、このイシューも放置状態ですし、Slickの構造上修正するのも難しそうだったので(ちなみにSlickはクエリビルダで組み立てたASTをSQLコンパイルして実行するという構造になっているのですが、StackOverflowErrorはASTを組み立てる際ではなく、SQLへのコンパイル時の処理で発生していました)、GitBucket側で以下のようにINを使ったクエリに書き換えることにしました。

Accounts.filter { t => t.userId inSerBind(seq) }.list

これで今までStackOverflowErrorになっていた件数を与えてもとりあえずエラーは起きなくなりました。ただ、条件によっては単純にINに書き換えるとインデックスが効かなくなってしまったり、そもそもINへの書き換えができないケースもあるでしょうし、データベースによってはINで指定可能なパラメータ数に上限もあるので、だいぶやっつけ感のあるワークアラウンドです。

Slickではこういうものは素直にネイティブSQLで記述するべきなのかもしれませんが、せっかく強力なクエリビルダがあるのに肝心な複雑な条件指定が必要な箇所で使えないというのも勿体無い気がするので何か良い解決策があるといいのですが…。