CLOVER🍀

That was when it all began.

InfinispanのEvictionとPassivationを復習する

ちょっと前に、InfinispanのCache Loaderの使ったサンプルを書きましたが、この時にEvictionとPassivationの関係がよくわかってなくて、すごくアホなエントリになってしまいました…。

ちゃんと、英単語として読めばよかった…。

まあ、その反省も踏まえて、ここで復習したいと思います。

Eviction
https://docs.jboss.org/author/display/ISPN/Eviction
Cache Passivation
https://docs.jboss.org/author/display/ISPN/Cache+Loaders+and+Stores#CacheLoadersandStores-CachePassivation
What does the passivation flag do?
https://docs.jboss.org/author/pages/viewpage.action?pageId=5832809

Eviction(追い出し)に関する設定は、Infinispanのメモリ内に保持するエントリに関するものです。ここでは、

  • メモリに保持する最大数
  • Evictionのアルゴリズム
  • Evictionを行うスレッドの動作ポリシー

の設定が可能です。

で、ここにCache Loaderの設定を加えます。

なんですが、ここでPassivationを有効にするかどうかで、Cache Loaderに保存される内容が変わります。

Passivation メモリ上のエントリ Cache Loader上のエントリ
無効 EvictionのmaxEntriesで設定した数(*) メモリ上のエントリ+Evictされたエントリ
有効 EvictionのmaxEntriesで設定した数(*) Evictされたエントリ

*maxEntriesで設定した数であることを、厳密に保証するわけではないようです

つまり、Passivationを無効(デフォルト)にした場合は、Cache Loaderが保持するエントリは、メモリ上のエントリを含んだすべてのエントリになります。特に書き込みを同期モードにした場合は、Write-Throughキャッシュとして振る舞うことになります。

反対に、Passivationを有効にした場合は、EvictされたエントリがCache Loaderに置かれるということになるため、メモリ上のエントリとCache Loader上のエントリを合わせてはじめて全体が揃うといったことになります。また、いったんEvictされCache Loaderに移されたエントリが、再度アクティブになるとメモリ上に戻り、今度はCache Loaderからは削除されます。

ここを前はちゃんと理解できていませんでした…。

というわけで、懲りずにまたサンプルを作成。
src/main/scala/InfinispanEvictPassive.scala

import org.infinispan.Cache
import org.infinispan.configuration.cache.ConfigurationBuilder
import org.infinispan.container.entries.CacheEntry
import org.infinispan.eviction.{EvictionStrategy, EvictionThreadPolicy}
import org.infinispan.manager.{DefaultCacheManager, EmbeddedCacheManager}

object InfinispanEvictPassive {
  def main(args: Array[String]): Unit = {
    val manager = new DefaultCacheManager

    val range = (1 to 10)
    val (keys, values) = range.map(i => (s"key$i", s"value$i")).unzip

    println("---------------------------- Passivation -------------------------------------")
    val cachePassivation =
      defineCacheEnablePassivation(manager, "cacheEnablePassivation")
    val advancedCachePassivation = cachePassivation.getAdvancedCache

    keys.foreach { k =>
      printCacheEntryDetail("Cache Passivation Preloaded",
                            advancedCachePassivation.getCacheEntry(k, null, null))
    }

    keys.zip(values).foreach { case (k, v) => cachePassivation.put(k, v) }

    println("------------------------------------------------------------------------------")

    keys.foreach { k =>
      printCacheEntryDetail("Cache Passivation Writed",
                            advancedCachePassivation.getCacheEntry(k, null, null))
    }

    println("---------------------------- Passivation -------------------------------------")

    println("---------------------------- Write-Through -------------------------------------")
    val cacheWriteThrough =
        defineCacheWriteThrough(manager, "cacheWritThrough")
    val advancedCacheWriteThrough = cacheWriteThrough.getAdvancedCache

    keys.foreach { k =>
      printCacheEntryDetail("Cache Write-Through Preloaded",
                            advancedCacheWriteThrough.getCacheEntry(k, null, null))
    }

    keys.zip(values).foreach { case (k, v) => cacheWriteThrough.put(k, v) }

    println("------------------------------------------------------------------------------")

    keys.foreach { k =>
      printCacheEntryDetail("Cache Write-Through Writed",
                            advancedCacheWriteThrough.getCacheEntry(k, null, null))
    }

    println("---------------------------- Write-Through -------------------------------------")
  }

  def defineCacheEnablePassivation(manager: EmbeddedCacheManager, name: String): Cache[String, String] = {
    manager.defineConfiguration(
      name,
      new ConfigurationBuilder()
        .eviction
        .strategy(EvictionStrategy.LIRS)
        .threadPolicy(EvictionThreadPolicy.PIGGYBACK)
        .maxEntries(4)
        .loaders
        .passivation(true)
        .addFileCacheStore
        .ignoreModifications(false)
        .fetchPersistentState(false)
        .location("cache-store-passivation")
        .build)
    manager.getCache(name)
  }

  def defineCacheWriteThrough(manager: EmbeddedCacheManager, name: String): Cache[String, String] = {
    manager.defineConfiguration(
      name,
      new ConfigurationBuilder()
        .eviction
        .strategy(EvictionStrategy.LIRS)
        .threadPolicy(EvictionThreadPolicy.PIGGYBACK)
        .maxEntries(4)
        .loaders
        .passivation(false)
        .addFileCacheStore
        .ignoreModifications(false)
        .fetchPersistentState(false)
        .location("cache-store-write-through")
        .build)
    manager.getCache(name)
  }

  def printCacheEntryDetail(preMsg: String, cacheEntry: CacheEntry): Unit =
    cacheEntry match {
      case null =>
      case _ =>
        val msg = s"""$preMsg =>
                      |   Key[${cacheEntry.getKey}],
                      |   Value[${cacheEntry.getValue}]""".stripMargin
        println(msg)
    }
}

キャッシュの設定は、Passivationを有効にしたもの

  def defineCacheEnablePassivation(manager: EmbeddedCacheManager, name: String): Cache[String, String] = {
    manager.defineConfiguration(
      name,
      new ConfigurationBuilder()
        .eviction
        .strategy(EvictionStrategy.LIRS)
        .threadPolicy(EvictionThreadPolicy.PIGGYBACK)
        .maxEntries(4)
        .loaders
        .passivation(true)
        .addFileCacheStore
        .ignoreModifications(false)
        .fetchPersistentState(false)
        .location("cache-store-passivation")
        .build)
    manager.getCache(name)
  }

無効にしたものの2つを用意しています。

  def defineCacheWriteThrough(manager: EmbeddedCacheManager, name: String): Cache[String, String] = {
    manager.defineConfiguration(
      name,
      new ConfigurationBuilder()
        .eviction
        .strategy(EvictionStrategy.LIRS)
        .threadPolicy(EvictionThreadPolicy.PIGGYBACK)
        .maxEntries(4)
        .loaders
        .passivation(false)
        .addFileCacheStore
        .ignoreModifications(false)
        .fetchPersistentState(false)
        .location("cache-store-write-through")
        .build)
    manager.getCache(name)
  }

ともに、EvictionのアルゴリズムはLIRSで、最大保持数は4です。

Cache Loaderの設定は、ファイル保存で、Passivationを有効にした方は「cache-store-passivation」ディレクトリ、無効にした方は「cache-store-write-through」ディレクトリに保存することにしています。

キャッシュに保存するキーと値は、簡単に作成して

    val range = (1 to 10)
    val (keys, values) = range.map(i => (s"key$i", s"value$i")).unzip

起動時にとりあえず読んでみて、改めてキャッシュに保存して読み出します。

    println("---------------------------- Passivation -------------------------------------")
    val cachePassivation =
      defineCacheEnablePassivation(manager, "cacheEnablePassivation")
    val advancedCachePassivation = cachePassivation.getAdvancedCache

    keys.foreach { k =>
      printCacheEntryDetail("Cache Passivation Preloaded",
                            advancedCachePassivation.getCacheEntry(k, null, null))
    }

    keys.zip(values).foreach { case (k, v) => cachePassivation.put(k, v) }

    println("------------------------------------------------------------------------------")

    keys.foreach { k =>
      printCacheEntryDetail("Cache Passivation Writed",
                            advancedCachePassivation.getCacheEntry(k, null, null))
    }

    println("---------------------------- Passivation -------------------------------------")

これはPassivationを有効にした方のものですが、無効にした方もだいだい同じです。

起動時と終了時の読み出しは、AdvancedCacheからCacheEntryを取得して、nullでなければキーと値を出力するようにしています。nullの場合は、何も出ません。

  def printCacheEntryDetail(preMsg: String, cacheEntry: CacheEntry): Unit =
    cacheEntry match {
      case null =>
      case _ =>
        val msg = s"""$preMsg =>
                      |   Key[${cacheEntry.getKey}],
                      |   Value[${cacheEntry.getValue}]""".stripMargin
        println(msg)
    }

では、実行してみます。

> run
[info] Running InfinispanEvictPassive 
[info] ---------------------------- Passivation -------------------------------------
[error] 4 29, 2013 12:28:40 午前 org.infinispan.factories.GlobalComponentRegistry start
[error] INFO: ISPN000128: Infinispan version: Infinispan 'Delirium' 5.2.1.Final
[error] 4 29, 2013 12:28:41 午前 org.infinispan.jmx.CacheJmxRegistration start
[error] INFO: ISPN000031: MBeans were successfully registered to the platform MBean server.
[info] ------------------------------------------------------------------------------
[info] Cache Passivation Writed =>
[info]    Key[key1],
[info]    Value[value1]
[info] Cache Passivation Writed =>
[info]    Key[key2],
[info]    Value[value2]
[info] Cache Passivation Writed =>
[info]    Key[key3],
[info]    Value[value3]
[info] Cache Passivation Writed =>
[info]    Key[key4],
[info]    Value[value4]
[info] Cache Passivation Writed =>
[info]    Key[key5],
[info]    Value[value5]
[info] Cache Passivation Writed =>
[info]    Key[key6],
[info]    Value[value6]
[info] Cache Passivation Writed =>
[info]    Key[key7],
[info]    Value[value7]
[info] Cache Passivation Writed =>
[info]    Key[key8],
[info]    Value[value8]
[info] Cache Passivation Writed =>
[info]    Key[key9],
[info]    Value[value9]
[info] Cache Passivation Writed =>
[info]    Key[key10],
[info]    Value[value10]
[info] ---------------------------- Passivation -------------------------------------
[info] ---------------------------- Write-Through -------------------------------------
[error] 4 29, 2013 12:28:41 午前 org.infinispan.jmx.CacheJmxRegistration start
[error] INFO: ISPN000031: MBeans were successfully registered to the platform MBean server.
[info] ------------------------------------------------------------------------------
[info] Cache Write-Through Writed =>
[info]    Key[key1],
[info]    Value[value1]
[info] Cache Write-Through Writed =>
[info]    Key[key2],
[info]    Value[value2]
[info] Cache Write-Through Writed =>
[info]    Key[key3],
[info]    Value[value3]
[info] Cache Write-Through Writed =>
[info]    Key[key4],
[info]    Value[value4]
[info] Cache Write-Through Writed =>
[info]    Key[key5],
[info]    Value[value5]
[info] Cache Write-Through Writed =>
[info]    Key[key6],
[info]    Value[value6]
[info] Cache Write-Through Writed =>
[info]    Key[key7],
[info]    Value[value7]
[info] Cache Write-Through Writed =>
[info]    Key[key8],
[info]    Value[value8]
[info] Cache Write-Through Writed =>
[info]    Key[key9],
[info]    Value[value9]
[info] Cache Write-Through Writed =>
[info]    Key[key10],
[info]    Value[value10]
[info] ---------------------------- Write-Through -------------------------------------
[success] Total time: 3 s, completed 2013/04/29 0:28:41

共に、同じ結果です。

ここで、Cache Loaderに出力されたファイルの内容を見てみます。

まずはPassivationを有効にした方から。

$ cat cache-store-passivation/cacheEnablePassivation/3288064
^C^A�^C*^F^C
>^Dkey4>^Fvalue4^C
>^Dkey6>^Fvalue6^C
>^Dkey2>^Fvalue2^C
>^Dkey1>^Fvalue1^C
>^Dkey8>^Fvalue8^C
>^Dkey9>^Fvalue9

6つのエントリが確認できます。maxEntriesは、4ですからね。

続いて、Passivationを無効にした方です。

$ cat cache-store-write-through/cacheWritThrough/3288064
^C^A�^C*	^C
>^Dkey4>^Fvalue4^C
>^Dkey3>^Fvalue3^C
>^Dkey6>^Fvalue6^C
>^Dkey5>^Fvalue5^C
>^Dkey2>^Fvalue2^C
>^Dkey1>^Fvalue1^C
>^Dkey8>^Fvalue8^C
>^Dkey7>^Fvalue7^C
>^Dkey9>^Fvalue9

…9つしかない?と言いたいところですが、実際には表示できない文字があるので、そこにあるんでしょうなぁ。

では、もう1度プログラムを起動してみます。

> run
[info] Running InfinispanEvictPassive 
[info] ---------------------------- Passivation -------------------------------------
[error] 4 29, 2013 12:32:39 午前 org.infinispan.factories.GlobalComponentRegistry start
[error] INFO: ISPN000128: Infinispan version: Infinispan 'Delirium' 5.2.1.Final
[error] 4 29, 2013 12:32:39 午前 org.infinispan.jmx.CacheJmxRegistration start
[error] INFO: ISPN000031: MBeans were successfully registered to the platform MBean server.
[info] Cache Passivation Preloaded =>
[info]    Key[key1],
[info]    Value[value1]
[info] Cache Passivation Preloaded =>
[info]    Key[key2],
[info]    Value[value2]
[info] Cache Passivation Preloaded =>
[info]    Key[key4],
[info]    Value[value4]
[info] Cache Passivation Preloaded =>
[info]    Key[key6],
[info]    Value[value6]
[info] Cache Passivation Preloaded =>
[info]    Key[key8],
[info]    Value[value8]
[info] Cache Passivation Preloaded =>
[info]    Key[key9],
[info]    Value[value9]
[info] ------------------------------------------------------------------------------
[info] Cache Passivation Writed =>
[info]    Key[key1],
[info]    Value[value1]
[info] Cache Passivation Writed =>
[info]    Key[key2],
[info]    Value[value2]
[info] Cache Passivation Writed =>
[info]    Key[key3],
[info]    Value[value3]
[info] Cache Passivation Writed =>
[info]    Key[key4],
[info]    Value[value4]
[info] Cache Passivation Writed =>
[info]    Key[key5],
[info]    Value[value5]
[info] Cache Passivation Writed =>
[info]    Key[key6],
[info]    Value[value6]
[info] Cache Passivation Writed =>
[info]    Key[key7],
[info]    Value[value7]
[info] Cache Passivation Writed =>
[info]    Key[key8],
[info]    Value[value8]
[info] Cache Passivation Writed =>
[info]    Key[key9],
[info]    Value[value9]
[info] Cache Passivation Writed =>
[info]    Key[key10],
[info]    Value[value10]
[info] ---------------------------- Passivation -------------------------------------
[info] ---------------------------- Write-Through -------------------------------------
[error] 4 29, 2013 12:32:40 午前 org.infinispan.jmx.CacheJmxRegistration start
[error] INFO: ISPN000031: MBeans were successfully registered to the platform MBean server.
[info] Cache Write-Through Preloaded =>
[info]    Key[key1],
[info]    Value[value1]
[info] Cache Write-Through Preloaded =>
[info]    Key[key2],
[info]    Value[value2]
[info] Cache Write-Through Preloaded =>
[info]    Key[key3],
[info]    Value[value3]
[info] Cache Write-Through Preloaded =>
[info]    Key[key4],
[info]    Value[value4]
[info] Cache Write-Through Preloaded =>
[info]    Key[key5],
[info]    Value[value5]
[info] Cache Write-Through Preloaded =>
[info]    Key[key6],
[info]    Value[value6]
[info] Cache Write-Through Preloaded =>
[info]    Key[key7],
[info]    Value[value7]
[info] Cache Write-Through Preloaded =>
[info]    Key[key8],
[info]    Value[value8]
[info] Cache Write-Through Preloaded =>
[info]    Key[key9],
[info]    Value[value9]
[info] Cache Write-Through Preloaded =>
[info]    Key[key10],
[info]    Value[value10]
[info] ------------------------------------------------------------------------------
[info] Cache Write-Through Writed =>
[info]    Key[key1],
[info]    Value[value1]
[info] Cache Write-Through Writed =>
[info]    Key[key2],
[info]    Value[value2]
[info] Cache Write-Through Writed =>
[info]    Key[key3],
[info]    Value[value3]
[info] Cache Write-Through Writed =>
[info]    Key[key4],
[info]    Value[value4]
[info] Cache Write-Through Writed =>
[info]    Key[key5],
[info]    Value[value5]
[info] Cache Write-Through Writed =>
[info]    Key[key6],
[info]    Value[value6]
[info] Cache Write-Through Writed =>
[info]    Key[key7],
[info]    Value[value7]
[info] Cache Write-Through Writed =>
[info]    Key[key8],
[info]    Value[value8]
[info] Cache Write-Through Writed =>
[info]    Key[key9],
[info]    Value[value9]
[info] Cache Write-Through Writed =>
[info]    Key[key10],
[info]    Value[value10]
[info] ---------------------------- Write-Through -------------------------------------
[success] Total time: 2 s, completed 2013/04/29 0:32:40

Passivationを無効にした方は、前回保存した全てのエントリが読み込めています。対して、Passivationを有効にした方はEvictされたと思われるものしか読み込めていないことになります。

ここを、前に動かした時は変な理解のままエントリを書いたと…。

この前回Evictされた内容を読み出してしまうのが嫌だとかいうことであれば、purgeOnStartupで調整するんでしょうね。

5/6 追記
EvictionとPassivationをテーマにしていたのですが、せっかくなのでCacheLoaderを外した場合の挙動も書いておきますね。

Cacheの設定としては、以下のようにしてみます。

  def defineCacheNoStore(manager: EmbeddedCacheManager, name: String): Cache[String, String] = {
    manager.defineConfiguration(
      name,
      new ConfigurationBuilder()
        .eviction
        .strategy(EvictionStrategy.LIRS)
        .maxEntries(4)
        .build)
    manager.getCache(name)
  }

キャッシュを使用するコードとしては、他のサンプルと同じように。

    println("---------------------------- No-Store -------------------------------------")
    val cacheNoStore =
        defineCacheNoStore(manager, "cacheNoStore")
    val advancedCacheNoStore = cacheNoStore.getAdvancedCache

    keys.foreach { k =>
      printCacheEntryDetail("Cache No-Store Preloaded",
                            advancedCacheNoStore.getCacheEntry(k, null, null))
    }

    keys.zip(values).foreach { case (k, v) => cacheNoStore.put(k, v) }

    println("------------------------------------------------------------------------------")

    keys.foreach { k =>
      printCacheEntryDetail("Cache No-Store Writed",
                            advancedCacheNoStore.getCacheEntry(k, null, null))
    }

    println("---------------------------- No-Store -------------------------------------")

実行すると

[info] ---------------------------- No-Store -------------------------------------
[error] 5 06, 2013 7:32:21 午後 org.infinispan.jmx.CacheJmxRegistration start
[error] INFO: ISPN000031: MBeans were successfully registered to the platform MBean server.
[info] ------------------------------------------------------------------------------
[info] Cache No-Store Writed =>
[info]    Key[key3],
[info]    Value[value3]
[info] Cache No-Store Writed =>
[info]    Key[key5],
[info]    Value[value5]
[info] Cache No-Store Writed =>
[info]    Key[key7],
[info]    Value[value7]
[info] Cache No-Store Writed =>
[info]    Key[key10],
[info]    Value[value10]
[info] ---------------------------- No-Store -------------------------------------

という結果になり、キャッシュへの保存数が制限されるような挙動になります。

が、maxEntriesに設定した数を保証してくれるわけでもないみたいなので、目安ですね。

実際、10個のエントリを保存するためにmaxEntriesは32まで上げないとダメでした…。