読者です 読者をやめる 読者になる 読者になる

seratch's weblog in Japanese

About Scala, Java and Ruby programming in Japaense. If you need English information, go to http://blog.seratch.net/

JDBC っぽいけど non-blocking な DB アクセス #scalajp

Scala

を実現するライブラリをつくり、週末に公開しました。

概要

ScalikeJDBC の extension という位置づけで ScalikeJDBC-Async という名前です。

https://github.com/seratch/scalikejdbc-async

内部では postgresql-async/mysql-async という Netty ベースの独自ドライバーを使っています。

https://github.com/mauricio/postgresql-async

ただし postgresql-async をそのまま使うのは結構プリミティブな感じなので、いかに隠蔽して使いやすくするかにこだわって実装しました。 とりあえずそれについてはまずまずうまくいったのではないかと思っています。

例えば postgreslq-async をそのまま使うとこのサンプルのようにsendQuery とか sendPreparedStatement (これらは JDBC とは互換性はありません)みたいなのを呼び出す必要があり、また postgresql-async の ResultSet は「とりあえず Any 型からキャストすりゃいいじゃん」的なスタイルなので、キャストするようなコードを書く必要があります。

https://github.com/mauricio/postgresql-async-app/blob/master/app/models/MessageRepository.scala

ScalikeJDBC-Async はこれらを隠蔽して、従来の JDBC ラッパー(機能が増えてきて最近は単純にそうも言えなくなってきたけど)としての ScalikeJDBC とほぼ同じ使い勝手で non-blocking な DB アクセスができるようになっています。結果は Scala 2.10 標準の Future 型で返ってきます。

import scalikejdbc._, SQLInterpolation._, async._
import scala.concurrent._, duration._, ExecutionContext.Implicits.global

// コネクションプールの設定はこれだけ
AsyncConnectionPool.singleton("jdbc:postgresql://localhost:5432/db", "user", "password")

val c = Company.syntax("c")
val company: Future[Option[Company]] = AsyncDB.withPool { implicit s =>
  withSQL { 
    select.from(Company as c).where.eq(c.id, 123)
  }.map(Company(c)).single.future()
}

トランザクションも直接使うと自分で begin、commit/rollback する必要がありますが AsyncDB.localTx { implicit tx => } でお手軽に使えます。

https://github.com/seratch/scalikejdbc-async/blob/master/src/test/scala/programmerlist/ExampleSpec.scala

// create a new record within a transaction
val created: Future[Company] = AsyncDB.localTx { implicit tx =>
  for {
    company <- Company.create("ScalikeJDBC, Inc.", Some("http://scalikejdbc.org/"))
    seratch <- Programmer.create("seratch", Some(company.id))
    gakuzzzz <- Programmer.create("gakuzzzz", Some(company.id))
    xuwei_k <- Programmer.create("xuwei-k", Some(company.id))
  } yield company
}

現状

まだアルファバージョンというステータスですが、基本的な機能の動作は保証されています。

本当はパフォーマンス測定などもやりたいとは思っていますが、まだそこまではやれていません。ただ、このベンチマークで Finagle が Slick から Finagle の mysql クライアントに切り替えたら相当速くなったので、同じような効果は期待できるのではないかと思っています*1

http://www.techempower.com/benchmarks/

postgresql-async の方も基本的には動作する印象ですが、細かいバグやちょっと不安定っぽい挙動を見かけることがあり、issue を報告したりしています。できるだけ contribute していきたいと思っています。

Go Reactively!

最近 Scala 界隈で "reactive" というキーワードがホット*2ですが RDB を使って reactive なアプリケーションを書きたいという方は ScalikeJDBC-Async をお試しください。

*1:PR が merge されたのは Round5 の前だったけど反映されたのは Round6 から?

*2:http://typesafe.com/blog/why_do_we_need_a_reactive_manifesto%3F