Infinispanのクラスタリング時に使える機能である、L1 Cachingをちょっと試してみました。
L1 Caching
https://docs.jboss.org/author/display/ISPN/Clustering+modes#Clusteringmodes-L1Caching
L1 Cachingとは?
クラスタリングのモードが、Distributionの時に意味を成す機能です。
Distributionモードの時には、自分のローカルにデータがない時は他のNodeにデータを取りに行く(RPCが発生する)わけですが、L1 Cachingを有効にしているとこの時に取得したデータをキャッシュしてRPCを発生しないようにすることができます。
何度もリモートNodeにデータを取得しに行ってしまうようなケースで、有効にするとよいと書かれています。
L1 Cachingを有効にすると、文字通りデータをキャッシュするわけですが、このL1キャッシュの有効な期間は設定することができます。
L1 Cachingを有効にするための最低限の設定は
<l1 enabled="true" />
なのですが、他にもこんな設定を行うことができます。
属性名 | デフォルト値 | 意味 |
---|---|---|
enabled | false | trueにすると、L1 Cacheを有効にする。デフォルトでは無効 |
invalidationThreshold | 0 | L1 Cacheが無効化される時に、マルチキャストかユニキャストを使用するのかを決定する。デフォルトでは、マルチキャストが使用される。もしこのしきい値を-1にした場合は、ユニキャストが常に使用される。デフォルト値である0を指定した場合は、マルチキャストが使用される |
lifespan | 60000 | L1 Cacheのエントリの最大の有効期限。デフォルトは10分 |
cleanupTaskFrequency | 60000 | どのくらいの頻度で、L1 Cacheをクリーンアップするタスクを実行させるかを設定する。デフォルトは10分 |
onRehash | false(?) | 有効にすると、リハッシュ発生時に削除されたエントリは、削除されるのではなくL1 Cacheに移動する。デフォルトは、無効になっている模様… |
この説明でInvalidationがどうのこうのあるように、L1 Cachingは利用にはトレードオフがあることがドキュメントには書かれています。
まず、キーが更新された時には、L1 Cacheに乗っているエントリを無効化するために、Invalidation Messageが必要になります。L1 Cacheは、リクエストを行うNodeがエントリをローカルにキャッシュし、変更を監視するようにします。L1 Cacheのエントリは、メモリの使用を制御するために、内部的な有効期限を持ちます。
L1 Cacheを有効にすることは、ローカルにないキーを繰り返して読み出す際のパフォーマンスを改善しますが、メモリ使用量はある程度増加させてしまいます。
これは、Gridのデータが無効化されるものの「ほとんど読み取り」であることのパフォーマンスと、分散された時のスケーラビリティのよいトレードオフを表しています。
L1 Cacheを正しく理解しましたか?正しいアプローチは、あなたのアプリケーションでL1 Cacheを有効にした時と無効にした時でベンチマークテストを行い、何が最適なアクセスパターンなのかを確認することです。
使ってみる
ドキュメントを読んだところで、実際にL1 Cachingを使ってみましょう。
まずはbuild.sbt。
name := "infinispan-l1-caching" version := "0.0.1-SNAPSHOT" scalaVersion := "2.10.2" organization := "littlewings" fork in run := true resolvers += "JBoss Public Maven Repository Group" at "http://repository.jboss.org/nexus/content/groups/public-jboss/" libraryDependencies ++= Seq( "org.infinispan" % "infinispan-core" % "5.3.0.Final", "net.jcip" % "jcip-annotations" % "1.0" )
JGroupsの設定は端折ります。Infinispanの設定。
src/main/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:5.3 http://www.infinispan.org/schemas/infinispan-config-5.3.xsd" xmlns="urn:infinispan:config:5.3"> <global> <transport clusterName="l1-caching-cluster"> <properties> <property name="configurationFile" value="jgroups.xml" /> </properties> </transport> <globalJmxStatistics enabled="true" jmxDomain="org.infinispan" cacheManagerName="DefaultCacheManager" /> </global> <namedCache name="l1CacheEnabled"> <jmxStatistics enabled="true" /> <clustering mode="distribution"> <hash numOwners="1" /> <l1 enabled="true" /> </clustering> </namedCache> </infinispan>
L1 Cachingは有効にしています。Distributionモード時のnumOwnersは、今回は1としています。
L1 Cachingは他のNodeのデータをキャッシュするという話なので、Nodeがひとつだと意味がありません。というわけで、浮いてもらうサーバを用意。
起動時のサーバ名でキーの名前と範囲が変わり、5秒に1度自分が担当している範囲のキーに対応する値を更新します。
src/main/scala/EmbeddedCacheServer.scala
import org.infinispan.manager.DefaultCacheManager trait EmbeddedCacheServerSupport { val nodeNameTo: Map[String, Int] = Map("node1" -> 10, "node2" -> 20) val increase: Int = 5 } object EmbeddedCacheServer extends EmbeddedCacheServerSupport { def main(args: Array[String]): Unit = { val start = nodeNameTo .get(args.toList.headOption.getOrElse("")) .getOrElse(throw new IllegalArgumentException(s"Required NodeName[${nodeNameTo.keys.mkString(" or ")}]")) val cache = new DefaultCacheManager("infinispan.xml").getCache[Any, Any]("l1CacheEnabled") cache.addListener(new CacheListener) val fun = (now: Long) => { (start to start + increase).foreach { i => cache.put(s"key$i", s"value${i}-${now}") } Thread.sleep(5 * 1000L) } Stream .continually(System.currentTimeMillis) .foreach(fun) } }
一緒にListenerもくっつけています。
src/main/scala/CacheListener.scala
import org.infinispan.notifications.Listener import org.infinispan.notifications.cachelistener.annotation.{CacheEntryInvalidated, CacheEntryVisited} import org.infinispan.notifications.cachelistener.event.{CacheEntryInvalidatedEvent, CacheEntryVisitedEvent} @Listener class CacheListener { @CacheEntryVisited def cacheEntryVisited(event: CacheEntryVisitedEvent[_, _]): Unit = println(s"Entry Visited Event: $event") @CacheEntryInvalidated def cacheEntryInvalidated(event: CacheEntryInvalidatedEvent[_, _]): Unit = println(s"Invalidated Event: $event") }
Invalidationイベントが発生しそうな気がしたので、そちらを監視するのと、Remote Nodeに読み込みが行くかどうかをみるためにVisitedイベントを監視しています。
最後、Remote Nodeからデータを読み出すだけのプログラムです。こちらがL1 Cacheを活用することを想定しています。10秒おきに3回データを読み出し、これを5回繰り返します。
src/main/scala/InfinispanL1Caching.scala
import org.infinispan.manager.DefaultCacheManager object InfinispanL1Caching extends EmbeddedCacheServerSupport { def main(args: Array[String]): Unit = { val manager = new DefaultCacheManager("infinispan.xml") val cache = manager.getCache[String, String]("l1CacheEnabled") cache.addListener(new CacheListener) println(cache.asInstanceOf[org.infinispan.CacheImpl[_, _]].getConfigurationAsXmlString) try { val readValues = () => { nodeNameTo.values.foreach { v => (v to v + increase).foreach { i => cache.get(s"key$i") } } } val printDataLocality = () => { val dm = cache.getAdvancedCache.getDistributionManager val rpcManager = cache.getAdvancedCache.getRpcManager println("All Cluster Members => " + rpcManager.getMembers) nodeNameTo.values.foreach { v => (v to v + increase).foreach { i => val key = s"key$i" println(s"Data Locality: Key[$key] => PrimaryLocation[${dm.getPrimaryLocation(key)}], " + s"Locate:${dm.locate(key)}") } } } (1 to 5).foreach { i => readValues() printDataLocality() readValues() readValues() val waitTime = 10 * 1000L println(s"Sleeping... ${waitTime}sec") Thread.sleep(waitTime) } } finally { cache.stop() manager.stop() } } }
では、まずはサーバを起動。
# Node1 $ sbt "run-main EmbeddedCacheServer node1" # Node2 $ sbt "run-main EmbeddedCacheServer node2"
Node1→Node2の順に起動すると、いきなりInvalidationイベントが発生します。発生するのはNode1側(先に起動した方)です。
[info] Invalidated Event: EventImpl{type=CACHE_ENTRY_INVALIDATED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-48076, key=key14, value=value14-1373107486943, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Invalidated Event: EventImpl{type=CACHE_ENTRY_INVALIDATED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-48076, key=key13, value=value13-1373107486943, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Invalidated Event: EventImpl{type=CACHE_ENTRY_INVALIDATED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-48076, key=key10, value=value10-1373107486943, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false}
まあ、
Node1起動→Node2起動→リハッシュ→Node1からNodeに移ったデータを無効化
というところでしょうね。
では、メインとなるプログラムを実行します。
> run-main InfinispanL1Caching [info] Running InfinispanL1Caching [error] 7 06, 2013 7:46:58 午後 org.infinispan.remoting.transport.jgroups.JGroupsTransport start [error] INFO: ISPN000078: Starting JGroups Channel [info] [info] ------------------------------------------------------------------- [info] GMS: address=ubuntu-33510, cluster=l1-caching-cluster, physical address=fe80:0:0:0:20c:29ff:fe5c:cfec%2:42683 [info] -------------------------------------------------------------------
この時、自分自身は「ubuntu-33510」という名前で起動しています。
途中で出力されるデータの配置場所は、こんな感じになっています。
[info] All Cluster Members => [ubuntu-48076, ubuntu-34567, ubuntu-33510] [info] Data Locality: Key[key10] => PrimaryLocation[ubuntu-34567], Locate:[ubuntu-34567] [info] Data Locality: Key[key11] => PrimaryLocation[ubuntu-48076], Locate:[ubuntu-48076] [info] Data Locality: Key[key12] => PrimaryLocation[ubuntu-48076], Locate:[ubuntu-48076] [info] Data Locality: Key[key13] => PrimaryLocation[ubuntu-34567], Locate:[ubuntu-34567] [info] Data Locality: Key[key14] => PrimaryLocation[ubuntu-34567], Locate:[ubuntu-34567] [info] Data Locality: Key[key15] => PrimaryLocation[ubuntu-48076], Locate:[ubuntu-48076] [info] Data Locality: Key[key20] => PrimaryLocation[ubuntu-33510], Locate:[ubuntu-33510] [info] Data Locality: Key[key21] => PrimaryLocation[ubuntu-48076], Locate:[ubuntu-48076] [info] Data Locality: Key[key22] => PrimaryLocation[ubuntu-34567], Locate:[ubuntu-34567] [info] Data Locality: Key[key23] => PrimaryLocation[ubuntu-34567], Locate:[ubuntu-34567] [info] Data Locality: Key[key24] => PrimaryLocation[ubuntu-48076], Locate:[ubuntu-48076] [info] Data Locality: Key[key25] => PrimaryLocation[ubuntu-33510], Locate:[ubuntu-33510]
L1 Cacheは効いていますが、ここで分かるのはキャッシュされてもデータの配置先としては管理されないということですね。ま、そりゃそうですよね。
こちらでは、連続して3回全データを読み込むのですが、Remoteの各Nodeで出力されるログは、各キーに対して2回(pre/post)ずつになります。
Node1。
[info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-48076, key=key11, value=value11-1373107647714, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=false, cache=Cache 'l1CacheEnabled'@ubuntu-48076, key=key11, value=value11-1373107647714, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-48076, key=key12, value=value12-1373107647714, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=false, cache=Cache 'l1CacheEnabled'@ubuntu-48076, key=key12, value=value12-1373107647714, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-48076, key=key15, value=value15-1373107647714, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=false, cache=Cache 'l1CacheEnabled'@ubuntu-48076, key=key15, value=value15-1373107647714, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-48076, key=key21, value=value21-1373107647706, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=false, cache=Cache 'l1CacheEnabled'@ubuntu-48076, key=key21, value=value21-1373107647706, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-48076, key=key24, value=value24-1373107647706, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=false, cache=Cache 'l1CacheEnabled'@ubuntu-48076, key=key24, value=value24-1373107647706, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false}
Node2。
[info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-34567, key=key10, value=value10-1373107637614, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=false, cache=Cache 'l1CacheEnabled'@ubuntu-34567, key=key10, value=value10-1373107637614, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-34567, key=key13, value=value13-1373107637614, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=false, cache=Cache 'l1CacheEnabled'@ubuntu-34567, key=key13, value=value13-1373107637614, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-34567, key=key14, value=value14-1373107637614, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=false, cache=Cache 'l1CacheEnabled'@ubuntu-34567, key=key14, value=value14-1373107637614, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-34567, key=key22, value=value22-1373107637606, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=false, cache=Cache 'l1CacheEnabled'@ubuntu-34567, key=key22, value=value22-1373107637606, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-34567, key=key23, value=value23-1373107637606, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false} [info] Entry Visited Event: EventImpl{type=CACHE_ENTRY_VISITED, pre=false, cache=Cache 'l1CacheEnabled'@ubuntu-34567, key=key23, value=value23-1373107637606, oldValue=null, transaction=null, originLocal=true, transactionSuccessful=false, entries=null, created=false}
つまり、2回目以降の読み出しではRPCは発生していないということになります。
また、Node1、Node2では、5秒おきにデータの再設定が発生するので、メインとなるプログラムが10秒スリープしている間に、Invalidationイベントが発生します。
[info] Invalidated Event: EventImpl{type=CACHE_ENTRY_INVALIDATED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-33510, key=key10, value=value10-1373107617424, oldValue=null, transaction=null, originLocal=false, transactionSuccessful=false, entries=null, created=false} [info] Invalidated Event: EventImpl{type=CACHE_ENTRY_INVALIDATED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-33510, key=key21, value=value21-1373107617436, oldValue=null, transaction=null, originLocal=false, transactionSuccessful=false, entries=null, created=false} [info] Invalidated Event: EventImpl{type=CACHE_ENTRY_INVALIDATED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-33510, key=key11, value=value11-1373107617424, oldValue=null, transaction=null, originLocal=false, transactionSuccessful=false, entries=null, created=false} [info] Invalidated Event: EventImpl{type=CACHE_ENTRY_INVALIDATED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-33510, key=key22, value=value22-1373107617436, oldValue=null, transaction=null, originLocal=false, transactionSuccessful=false, entries=null, created=false} [info] Invalidated Event: EventImpl{type=CACHE_ENTRY_INVALIDATED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-33510, key=key23, value=value23-1373107617436, oldValue=null, transaction=null, originLocal=false, transactionSuccessful=false, entries=null, created=false} [info] Invalidated Event: EventImpl{type=CACHE_ENTRY_INVALIDATED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-33510, key=key12, value=value12-1373107617424, oldValue=null, transaction=null, originLocal=false, transactionSuccessful=false, entries=null, created=false} [info] Invalidated Event: EventImpl{type=CACHE_ENTRY_INVALIDATED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-33510, key=key13, value=value13-1373107617424, oldValue=null, transaction=null, originLocal=false, transactionSuccessful=false, entries=null, created=false} [info] Invalidated Event: EventImpl{type=CACHE_ENTRY_INVALIDATED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-33510, key=key24, value=value24-1373107617436, oldValue=null, transaction=null, originLocal=false, transactionSuccessful=false, entries=null, created=false} [info] Invalidated Event: EventImpl{type=CACHE_ENTRY_INVALIDATED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-33510, key=key14, value=value14-1373107617424, oldValue=null, transaction=null, originLocal=false, transactionSuccessful=false, entries=null, created=false} [info] Invalidated Event: EventImpl{type=CACHE_ENTRY_INVALIDATED, pre=true, cache=Cache 'l1CacheEnabled'@ubuntu-33510, key=key15, value=value15-1373107617424, oldValue=null, transaction=null, originLocal=false, transactionSuccessful=false, entries=null, created=false}
Node1、Node2が共に全キーを更新するので、全部無効化されています。
なお、このイベントはNode1およびNode2では発生しません。データの読み出しを行っていないので、L1 Cacheに乗らないことが理由だと思いますが。
とりあえず、L1 Cachingを有効にした時にRPCが発生していないこと、そしてデータのオーナーが更新を行った時に、L1 Cacheのエントリが無効化されることは確認できましたね。