CLOVER🍀

That was when it all began.

Ehcache 3のUserManagedCacheを試す

Ehcache 3のドキュメントを見ていて、新しいキーワードが増えているのに気付きまして。

User managed caches

何これ?という感じですが、要はCacheManagerに管理されないCacheのようです(まんま)。Ehcache 3で導入されたコンセプトの模様。

Ehcache 3では、CacheManagerとCacheManagerから取得できるCacheをAPIの中心として使いますが、こちらはUserManagedCacheというインターフェースを使用します。まあ、UserManagedCacheもCacheインターフェースを実装していますが…。

CacheManagerにより管理されるCacheはCacheManagerによりライフサイクルを管理されますが、UserManagedCacheの場合はUserManagedCacheに対してcloseを呼び出すことになります。

このあたりが、「UserManaged」なんでしょうねぇ…。

使いどころですが、ドキュメントを見ると

Ideas are: method local caches, thread local caches or any other place where the lifecycle of the cache is shorter than the application lifecycle.

http://www.ehcache.org/documentation/3.0/usermanaged.html#what-are-user-managed-caches-and-what-do-they-offer

メソッドローカルキャッシュ、スレッドローカルキャッシュ、その他のアプリケーションよりライフサイクルが短いCacheとして使えばいいのでは?という感じで書かれています。

制限事項(?)として挙がっているのは、こちら。

As there is no longer a cache manager offering up services, the main limitation of user managed caches is that the user has to configure all required services by hand.
Of course, if you find yourself requiring plenty of services, maybe the cache manager is a better option!

http://www.ehcache.org/documentation/3.0/usermanaged.html#limitations

CacheManagerが提供するようなサービスがないよ、という話。CacheManager単位で設定していたような機能は、書く場所がないですからね…。また後で出てきますが、CacheManagerがないので設定はAPIを使ってプログラムとして記述する必要があります。設定ファイルがないよ、と。

とりあえず、使ってみましょう。

準備

Maven依存関係は、このように定義。

        <dependency>
            <groupId>org.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>3.0.0.m4</version>
        </dependency>

Ehcache 3、現在は3.0.0.m4です。まだまだ正式版が出てくるのは遠そうですね。

あとは、テストコードのためにJUnitとAssertJを加えておきます。

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.3.0</version>
            <scope>test</scope>
        </dependency>

テストコードでは、以下のimport文があることを前提にします。

import java.util.concurrent.TimeUnit;

import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.CacheManagerBuilder;
import org.ehcache.Ehcache;
import org.ehcache.UserManagedCache;
import org.ehcache.UserManagedCacheBuilder;
import org.ehcache.config.CacheConfigurationBuilder;
import org.ehcache.config.Eviction;
import org.ehcache.config.ResourcePoolsBuilder;
import org.ehcache.config.units.EntryUnit;
import org.ehcache.expiry.Duration;
import org.ehcache.expiry.Expirations;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

あとは、ドキュメントに沿って進めてみましょう。

User managed caches

使ってみる

UserManagedCacheを使うには、UserManagedCacheBuilderでUserManagedCacheを構成します。その後、取得したUserManagedCacheの初期化、利用、終了という順で操作していきます。

というのをまとめると、こんな感じになります。

    @Test
    public void simpleUsage() {
        UserManagedCache<String, Integer> cache =
                UserManagedCacheBuilder
                        .newUserManagedCacheBuilder(String.class, Integer.class)
                        .build(false);
        cache.init();

        cache.put("key1", 1);

        assertThat(cache.get("key1"))
                .isEqualTo(1);

        cache.remove("key1");

        assertThat(cache.get("key1"))
                .isNull();

        cache.close();
    }

UserManagedCacheを作成して

        UserManagedCache<String, Integer> cache =
                UserManagedCacheBuilder
                        .newUserManagedCacheBuilder(String.class, Integer.class)
                        .build(false);

initして

        cache.init();

使って

        cache.put("key1", 1);

        assertThat(cache.get("key1"))
                .isEqualTo(1);

        cache.remove("key1");

        assertThat(cache.get("key1"))
                .isNull();

close。

        cache.close();

initを省略する

先ほどの例では、UserManagedCacheを使う前にinitを呼び出す必要がありました。

        cache.init();

ですが、これが嫌だという場合は、UserManagedCacheBuilder#build時にtrueを渡すとinitされた状態のUserManagedCacheを取得することができます。

    @Test
    public void lessInit() {
        UserManagedCache<String, Integer> cache =
                UserManagedCacheBuilder
                        .newUserManagedCacheBuilder(String.class, Integer.class)
                        .build(true);
        cache.put("key1", 1);

        assertThat(cache.get("key1"))
                .isEqualTo(1);

        cache.remove("key1");

        assertThat(cache.get("key1"))
                .isNull();

        cache.close();
    }

UserManagedCacheとCacheとEhcache

UserManagedCacheインターフェースですが、Cacheインターフェースを拡張したインターフェースになります。
https://github.com/ehcache/ehcache3/blob/v3.0.0.m4/api/src/main/java/org/ehcache/UserManagedCache.java#L28

さらに、CacheおよびUserManagedCacheの実体は、ともにEhcacheクラスです。
https://github.com/ehcache/ehcache3/blob/v3.0.0.m4/core/src/main/java/org/ehcache/Ehcache.java#L84

UserManagedCacheと、CacheManagerから取得したCacheのClassクラスを確認してみます。

    @Test
    public void userManagedCacheIsEhcache() {
        UserManagedCache<String, Integer> cache =
                UserManagedCacheBuilder
                        .newUserManagedCacheBuilder(String.class, Integer.class)
                        .build(true);
        assertThat(cache)
                .isExactlyInstanceOf(Ehcache.class);

        CacheManager cacheManager
                = CacheManagerBuilder.newCacheManagerBuilder()
                .withCache("myCache",
                        CacheConfigurationBuilder.newCacheConfigurationBuilder()
                                .buildConfig(String.class, Integer.class))
                .build(true);
        Cache<String, Integer> normalCache = cacheManager.getCache("myCache", String.class, Integer.class);

        assertThat(normalCache)
                .isExactlyInstanceOf(Ehcache.class);

        assertThat(cache.getClass())
                .isEqualTo(normalCache.getClass())
                .isEqualTo(Ehcache.class);

        cacheManager.close();
        cache.close();
    }

というわけで、同じEhcacheクラスが実体であることがわかります。

このことから、ホントにCacheManagerで管理されていない「だけ」のCacheなんだなぁというのがわかりますね…。

なお、Ehcacheクラスは内部でJCache向けの実装体まで作成していたりするのでいろいろフルスペック(?)です。
https://github.com/ehcache/ehcache3/blob/v3.0.0.m4/core/src/main/java/org/ehcache/Ehcache.java#L156

こう見ると、先の使用例で上げられていたような、メソッドのローカルキャッシュとして使用…というのはちょっとオーバースペックな気もするのですが…。もうちょっと、長い生存期間のキャッシュとして使う方がいいような気がしないでもないです。

ExpireやEvictionを設定する

Cacheの実体がCacheManagerで作成された時と同じということもあり、その他の設定もできます。

例えば、Expire(有効期限)。

    @Test
    public void configureExpiry() throws InterruptedException {
        UserManagedCache<String, Integer> cache =
                UserManagedCacheBuilder
                        .newUserManagedCacheBuilder(String.class, Integer.class)
                        .withExpiry(Expirations.timeToIdleExpiration(new Duration(3L, TimeUnit.SECONDS)))
                        .build(true);
        cache.put("key1", 1);
        assertThat(cache.get("key1"))
                .isEqualTo(1);

        TimeUnit.SECONDS.sleep(5L);

        assertThat(cache.get("key1"))
                .isNull();

        cache.close();
    }

Eviction(指定した要素数などを超えた場合に、Cacheから追い出し)。

    @Test
    public void configureEviction() throws InterruptedException {
        UserManagedCache<String, Integer> cache =
                UserManagedCacheBuilder
                        .newUserManagedCacheBuilder(String.class, Integer.class)
                        .prioritizeEviction(Eviction.Prioritizer.FIFO)
                        .withResourcePools(ResourcePoolsBuilder.newResourcePoolsBuilder()
                                .heap(3L, EntryUnit.ENTRIES))
                        .build(true);
        cache.put("key1", 1);
        cache.put("key2", 2);
        cache.put("key3", 3);
        cache.put("key4", 4);
        cache.put("key5", 5);

        TimeUnit.SECONDS.sleep(1L);

        assertThat(cache.get("key1"))
                .isNull();
        assertThat(cache.get("key2"))
                .isNull();
        assertThat(cache.get("key3"))
                .isEqualTo(3);
        assertThat(cache.get("key4"))
                .isEqualTo(4);
        assertThat(cache.get("key5"))
                .isEqualTo(5);

        cache.close();
    }

などなど。

まとめ

というわけで、Ehcache 3の新しいコンセプト、UserManagedCacheを試してみました。

CacheManagerに管理されず、ライフサイクル自体をユーザーコードで管理することになるのでこの名前なのでしょうけど、正直なところ、どうなのかな?そんなに必要とされてたのかな?という印象です。

まあ、コンセプトとしてこういうのがあることについては、覚えておきましょう。

あと、現在のEcache 3はバージョンが3.0.0.m4なので、今後APIが変わることも十分ありえるというのには注意です。