CLOVER🍀

That was when it all began.

Infinispan 7 ことはじめ

先日、Infinispan 7がリリースされましたね。

Infinispan
http://infinispan.org/

Infinispan 7.0.0.Final is out!!
http://blog.infinispan.org/2014/11/infinispan-700final-is-out.html

個人的にけっこう見ているライブラリなのですが、バージョン7から設定ファイルの形式が変わったり、いろいろ変化があるようなので、これを機にちょっとある程度復習的に勉強しなおそうかと思いまして。

Infinispanって?

というわけで、イントロダクション的な。

Infinispanは、JBoss系のプロジェクトで「インメモリ・データグリッド」と呼ばれる分散データストアのカテゴリに属するものです。JBoss AS/WildFlyでのセッションレプリケーションなどにも使われているようですが、単体(アプリケーション内への組み込み、独立したサーバ形態)でも利用可能です。

また、分散構成前提というわけでもなく、ローカルキャッシュとして構成することもできます。

今年に決まったJSR-107(JCACHE)の実装のひとつです。

Getting Started的な

では、まずは使ってみます。

あ、基本的にうちのブログエントリでInfinispanを扱う時は、大半のケースでScalaを使用しているのであしからず…。

依存関係の定義など。
build.sbt

name := "embedded-local-starter"

version := "0.0.1-SNAPSHOT"

scalaVersion := "2.11.2"

organization := "org.littlewings"

scalacOptions ++= Seq("-Xlint", "-deprecation", "-unchecked", "-feature")

incOptions := incOptions.value.withNameHashing(true)

fork in Test := true

parallelExecution in Test := false

libraryDependencies ++= Seq(
  "org.infinispan" % "infinispan-core" % "7.0.0.Final",
  "net.jcip" % "jcip-annotations" % "1.0" % "provided",
  "org.scalatest" %% "scalatest" % "2.2.2" % "test"
)

sbtでInfinispanを使う場合は、以前は依存関係の定義にだいぶてこずっていましたが、今は簡単になったようです。なお、ScalaでInfinispanを扱う時は、Infinispanに使われているアノテーションScalaコンパイラの関係で、jcip-annotationsは依存関係に含めておいた方がよいです。

今回、クラスタリングやサーバ構成はいったん置いておいて、Embeddedなローカルキャッシュとして使用してみます。
src/test/scala/org/littlewings/infinispan/embedded/EmbeddedCacheSpec.scala

package org.littlewings.infinsipan.embedded

import java.util.concurrent.TimeUnit

import org.infinispan.configuration.cache.ConfigurationBuilder
import org.infinispan.manager.DefaultCacheManager
import org.infinispan.transaction.{ LockingMode, TransactionMode }
import org.infinispan.util.concurrent.IsolationLevel

import org.scalatest.FunSpec
import org.scalatest.Matchers._

class EmbeddedCacheSpec extends FunSpec {
  describe("Infinispan Embedded Cache, Gettings Started") {
    // ここに、テストコードを書く!
  }
}

もっとも簡単な例。

    it("non configuration Cache") {
      val manager = new DefaultCacheManager
      val cache = manager.getCache[String, String]

      cache.put("key1", "value1")
      cache.put("key2", "value2")

      cache.get("key1") should be ("value1")
      cache.get("key2") should be ("value2")
      cache.get("not-exists-key") should be (null)

      cache.remove("key1")
      cache.remove("key2")

      cache.get("key1") should be (null)
      cache.get("key2") should be (null)

      cache.stop()
      manager.stop()
    }

起点はDefaultCacheManagerで、getCacheメソッドで取得できるCacheのインスタンスを使用して、キャッシュ管理を行います。Cacheインターフェースはjava.util.Mapインターフェースの拡張でもあるので、操作方法はほぼMapになります。それなりに直感的に扱えるでしょう。

Cacheを取得する時に、名前を付けて取得することもできます。

    it("using Named Cache, non configuration") {
      val manager = new DefaultCacheManager
      val cache = manager.getCache[String, String]("namedCache")

      cache.put("key1", "value1")
      cache.get("key1") should be ("value1")

      cache.getName should be ("namedCache")

      cache.stop()
      manager.stop()
    }

Cacheの管理は、名前ごとに別になります。

続いて、少しCacheの設定をしてみます。まずは、APIを使用して定義する場合。

    it("confirm expiration, define programattically") {
      val manager = new DefaultCacheManager
      manager.defineConfiguration("withExpireCache",
        new ConfigurationBuilder()
          .expiration
          .maxIdle(3000L)
          .lifespan(5000L)
          .build)

      val cache = manager.getCache[String, String]("withExpireCache")

      cache.put("key1", "value1")
      cache.put("key2", "value2")

      TimeUnit.SECONDS.sleep(2)

      cache.get("key1") should be ("value1")

      TimeUnit.SECONDS.sleep(2)

      // maxIdle
      cache.get("key1") should be ("value1")
      cache.get("key2") should be (null)

      TimeUnit.SECONDS.sleep(2)

      // lifespan
      cache.get("key1") should be (null)
      cache.get("key2") should be (null)

      cache.stop()
      manager.stop()
    }

今回は、有効期限(アイドル時間、TTL)の設定をしてみました。これは、3秒間アクセスがなければエントリはなくなり、エントリ自体の有効期限は5秒という設定です。

次に、設定ファイルでCacheを定義してみます。
src/test/resources/infinispan.xml

<?xml version="1.0" encoding="UTF-8"?>
<infinispan
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:infinispan:config:7.0 http://www.infinispan.org/schemas/infinispan-config-7.0.xsd"
    xmlns="urn:infinispan:config:7.0">

  <!-- この中に、cache-containerの定義を書く! -->

</infinispan>

最小構成としては、こんな感じです。

で、先ほどのAPIでCache定義した時と、同等の定義を書いてみます。

  <cache-container name="withExpireCacheManager" default-cache="withExpireCache">
    <local-cache name="withExpireCache">
      <expiration lifespan="5000" max-idle="3000" />
    </local-cache>
    <jmx />
  </cache-container>

Infinispan 7から設定ファイルの書き方が大きく変わっていて、JBoss ASやWildFlyの設定ファイルに書かれていた時みたいな定義方法になったようです。

あ、jmxタグ入れてる…。

このあたりが、ちゃんと見直そうと思った理由のひとつだったり。

設定ファイルを読み込む場合は、DefaultCacheManagerのコンストラクタ引数にファイル名を与える形でOKです。

    it("confirm expiration, define configuration-file") {
      val manager = new DefaultCacheManager("infinispan.xml")
      val cache = manager.getCache[String, String]("withExpireCache")

      cache.put("key1", "value1")
      cache.put("key2", "value2")

      TimeUnit.SECONDS.sleep(2)

      cache.get("key1") should be ("value1")

      TimeUnit.SECONDS.sleep(2)

      // maxIdle
      cache.get("key1") should be ("value1")
      cache.get("key2") should be (null)

      TimeUnit.SECONDS.sleep(2)

      // lifespan
      cache.get("key1") should be (null)
      cache.get("key2") should be (null)

      cache.stop()
      manager.stop()
    }

動作的には、先ほどと同じですね。

あと、設定ファイルを書いた時に

  <cache-container name="withExpireCacheManager" default-cache="withExpireCache">

の「default-cache」属性がちょっと気になっていたのですが、以前のdefaultタグの代わりなんでしょうね、きっと。
ここで指定された名前のCacheが、デフォルトのCache定義としてテンプレート的に使用されるという形のようですし、この内容を他のCacheが継承・上書きすることができるという点も変わらなさそうです。

The name of the default cache is defined in the element of the XML configuration file, and additional caches can be configured using the ,, or elements.

1.1. Configuring Cache declaratively

今回のドキュメントでは、前回よりもこのあたりの説明がないような気もしますが。

確認してみましょう。

Cacheの定義。

  <cache-container name="simpleCacheManager" default-cache="defaultCache">
    <local-cache name="defaultCache">
      <expiration lifespan="5000" max-idle="3000" />
    </local-cache>
    <local-cache name="transactionalCache">
      <locking isolation="REPEATABLE_READ" />
      <transaction
          mode="NON_XA"
          locking="OPTIMISTIC" />
    </local-cache>
  </cache-container>

default-cacheで指定しているものにはExpirationを指定して、これとは別にTransactionalなCacheを定義しました。

確認。

    it("define configuration cache") {
      val manager = new DefaultCacheManager("infinispan.xml")

      val defaultCache = manager.getCache[String, String]
      val transactionalCache = manager.getCache[String, String]("transactionalCache")
      val nonDefinedCache = manager.getCache[String, String]("nonDefinedCache")

      defaultCache.getCacheConfiguration.expiration.lifespan should be (5000)
      defaultCache.getCacheConfiguration.expiration.maxIdle should be (3000)

      // extends default cache
      transactionalCache.getCacheConfiguration.expiration.lifespan should be (5000)
      transactionalCache.getCacheConfiguration.expiration.maxIdle should be (3000)
      // specified configuration
      transactionalCache
        .getCacheConfiguration
        .locking
        .isolationLevel should be (IsolationLevel.REPEATABLE_READ)
      transactionalCache
        .getCacheConfiguration
        .transaction
        .transactionMode should be (TransactionMode.TRANSACTIONAL)
      transactionalCache
        .getCacheConfiguration
        .transaction
        .lockingMode should be (LockingMode.OPTIMISTIC)

      nonDefinedCache.getCacheConfiguration.expiration.lifespan should be (5000)
      nonDefinedCache.getCacheConfiguration.expiration.maxIdle should be (3000)

      defaultCache.stop()
      transactionalCache.stop()
      nonDefinedCache.stop()

      manager.stop()
    }

名前を指定していないCacheと、定義していない名前のCacheはdefault-cacheの設定内容が入っていますし、Transactionalにした方のCacheもExpirationの設定が入っています。

この挙動、実は忘れていたり…。

ちょっと最近は時間の確保が難しくなってきているのですが、これからも少しずつ見ていこうと思います。

今回作成したコードは、こちらに置いています。
https://github.com/kazuhira-r/infinispan-getting-started/tree/master/embedded-local-starter

GitHubのサンプル置き場も、新しくしましたー。