Play framework with Scala その6

| # Comments
Play framework with Scala を使ってみようシリーズです。
その1
その2
その3
その4
その5

先日、Play! 1.1.1 がリリースされました。
今回のリリースはバグフィックスとのことですので、大きな機能追加はないようです。
開発者MLによると、3月に1.2のリリースを予定しているという話がありました。

さて、今回はPlay! on GAE with ScalaでMemcacheを使ってみます。
Play!には標準でキャッシュのためのAPIが備わっていますが、GAEモジュールを利用することでGAEのmemcacheにデータをキャッシュすることができます。

今回のサンプルのソースは https://github.com/ueshin/play-hello/tree/play-hello-0.0.4 でブラウズできます。
Play!本体とsienaモジュールをアップデート(play-1.1.1、siena-1.4)してありますので適宜インストールもしくは読み替えをして下さい。

Scalaモジュールの修正

Play! on GAE with Scalaのキャッシュ機能には次の2つの不具合があるため、現在提供されているPlay!、およびScalaモジュールでは正しく動作しません。(2011.01.29現在)

  • ScalaモジュールのScalaCacheが、Actorを起動しようとする。
    -> GAEではThreadが利用できない(必然的にActorも利用できない)ため、エラーとなってしまう。
    -> github上では修正済みですが、まだリリースされていません。
  • Play!起動時の初期化フローが正しくない(と思われる)ため、GAECacheを利用するよう設定されない。
    -> Java版でも同様の現象となります。
    -> Play! 1.1.1 でも修正されていませんでした。 => ticket #527

上記2点は、Scalaモジュール0.8を少しだけ修正することで回避できます。

既存のScalaモジュールをコピーする

コピー先のバージョン指定はなんでもいいですし、コピーせずにそのまま修正でも構わないです。
指定したバージョンはapplication.confファイルに反映してください。

$ cd ${PLAY_HOME}/modules
$ cp -R scala-0.8 scala-0.8.1

scala-0.8.1/src/play/cache/ScalaCache.scalaを修正

15行目をlazyにする。

 13   private def prefixed(key: String) = "__" + key
 14 
 15   private lazy val cacheActor =
 16     actor{
 17       link{self.trapExit = true;loop{react{case Exit(from: Actor, exc: Exception) =>

Actorを遅延評価にすることで、勝手にActorが起動するのを防いでいます。
これで1点目の修正ができました。

scala-0.8.1/src/play/cache/CacheDelegate.javaを修正

14行目あたりにstatic初期化ブロックを作ってCacheの初期化をする。

 12 abstract class CacheDelegate {
 13 
 14     static { Cache.init(); }
 15 
 16     public void add(String key, Object value, String expiration) {
 17         Cache.add(key, value, expiration);
 18     }

Cacheの初期化はPlay!起動時に行われるんですが、GAECacheが設定される前に初期化されてしまうので、ここで再度初期化してあげます。
初期化をここですべきかどうかはアレですが(w)、暫定的に、ということで、本対応を待ちましょう。

Scalaモジュールをビルド

同梱されているbuild.xmlファイルのままではビルドが成功しないので、8行目を修正します。

  7     <path id="project.classpath">
  8         <pathelement path="${play.path}/framework/play.jar"/>
  9         <fileset dir="${play.path}/framework/lib">

antコマンドでビルドします。

$ ant -Dplay.path=${PLAY_HOME}

ビルドが正常に完了したらオッケーです。
application.confファイルのScalaモジュールのバージョンを新しく作成したバージョンに合わせてください。

Cacheを使う

キャッシュするには、play.cache.Cacheクラス(実体はplay.cache.ScalaCacheクラス)を使います。

Java版で提供されている各種メソッドも利用できますが、今回はScala版特有の機能を利用しました。

Application.scala

 53         val(followings, users, cachedAt) = Cache.get(followingsCacheName(currentUser.email), "10min") {
 54           val followings = Follow.followings(currentUser)
 55           val users = followings.flatMap(f=> User.get(f.following).map(user=>(f->user))).toMap
 56           (followings, users, new Date)
 57         }

これは、Option#getOrElseのような動作になります。
要するに、キャッシュが存在したらそれを使い、なければ後続のブロックが処理された結果を新たにキャッシュして返す、というわけです。

キャッシュのためのキーを第1引数、存続時間が第2引数となります。
キャッシュ対象となるインスタンスのクラスはSerializableである(@serializableアノテーションが付いている)必要があります。(=> User.scala / Follow.scala

簡単ですね!

ちなみにGAEでない場合には、サーバーのメモリ上か、Memcachedをキャッシュとして利用します。
また、Memcachedと互換のあるプロトコルを実装しているもの(kumofsMembaseなど)であればキャッシュ先として利用可能です。(・・・と思います。すいません、試してません。。。)

サンプルアプリ

実際にGAE上で動作しているサンプルアプリを公開しています。

ログインして、Followings画面に表示される一覧をキャッシュに登録しています。
保持期間は10分、もしくは新たにフォローするまで、となっています。

いつになっても見た目がしょぼいのでどなたかがデザインをあててくれる事を切に願います。

comments powered by Disqus

Twitter Icon

AdSense

Creative Commons License
このブログはクリエイティブ・コモンズでライセンスされています。
Powered by Movable Type 5.14-ja

Google検索

カスタム検索

2013年10月

    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31