Wicket stuff に DataStores · wicketstuff/core Wiki
いうライブラリがあり、Memcached や、Radis を使うものはあるのですが、
MongoDB を使っているものがなく、Cache の用途だと Memcached や Radis のほうが向いていそうだとは思いながら、
インストールが面倒に思い、MongoDBDatastore作ってみようという気になりました。
その過程で、StoreSettingsついて調べてましたので、
その結果について記載します。1
IDataStore クラスが登場してくる周辺に登場するので、関係があるのかと思って調べていました。


StoreSettings の前に、DataStore について

Apache Wikcet 7.x ユーザガイド8章邦訳+訳注 - Qiita
ページキャッシングの記載がありますが、
Wicket の ステートフルなページは、以下の順番で、アプリケーションにキャッシュされます。

  • 第1レベルのキャッシュ これは、ユーザーセッションに紐づけられたキャッシュです。
    最後のリクエストで使用されたページのみ保存されます。
    wicket/PageStoreManager.java at master · apache/wicket実装を保持しています。

  • 第2レベルのキャッシュ アプリケーションスコープ のキャッシュです。
    デフォルトで、SerializedPagesCache使用されています。
    sessionId pageIdキーとして、アプリケーションのメモリー上にキャッシュされます。
    SerializedPagesCache は、wicket/DefaultPageStore.java at master · apache/wicket
    インナークラスです。

  • 第3レベルのキャッシュ
    このキャッシュが、DataStore の実装でコントロールできます。
    デフォルトだと、DiskDataStore使われます。
    第2レベルキャッシュに存在しないデータも、第3レベルのキャッシュ には存在します。
    実装としては、以下のような作りになっています。

    1. 第2レベルキャッシュにも、第3レベルキャッシュにも、データ保存は試みる。
    2. 第2レベルキャッシュの設定次第で、1.保存時に、設定上限を上回った場合は削除される。
    3. コンポーネントの unbind 時は、第2レベルキャッシュ、第3レベルキャッシュからもデータは削除を試みる。2
      コンポーネントのunbindどのタイミングで走るのかがあまりよくわかっていないです。

この辺りの内容は、25 Wicket Internals 6.x にも記載されています。


DiskDataStore の 実装について

wicket/DiskDataStore.java at master · apache/wicket
コンストラクタ実装は以下の通りです。

  • DiskDataStore.java

        /**
         * Construct.
         * 
         * @param applicationName
         * @param fileStoreFolder
         * @param maxSizePerSession
         */
        public DiskDataStore(final String applicationName, final File fileStoreFolder,
            final Bytes maxSizePerSession)
        {
            this.applicationName = applicationName;
            this.fileStoreFolder = fileStoreFolder;
            maxSizePerPageSession = Args.notNull(maxSizePerSession, "maxSizePerSession");
            sessionEntryMap = new ConcurrentHashMap<>();
    
            try
            {
                if (this.fileStoreFolder.exists() || this.fileStoreFolder.mkdirs())
                {
                    loadIndex();
                }
                else
                {
                    log.warn("Cannot create file store folder for some reason.");
                }
            }
            catch (SecurityException e)
            {
                throw new WicketRuntimeException(
                    "SecurityException occurred while creating DiskDataStore. Consider using a non-disk based IDataStore implementation. "
                        + "See org.apache.wicket.Application.setPageManagerProvider(IPageManagerProvider)",
                    e);
            }
        }
    
    呼び出し先のwicket/DefaultPageManagerProvider.java at master · apache/wicket以下のような実装になっています。

  • DefaultPageManagerProvider.java

        protected IDataStore newDataStore()
        {
            StoreSettings storeSettings = getStoreSettings();
            Bytes maxSizePerSession = storeSettings.getMaxSizePerSession();
            File fileStoreFolder = storeSettings.getFileStoreFolder();
    
            return new DiskDataStore(application.getName(), fileStoreFolder, maxSizePerSession);
        }
    
    DiskDataStore の ファイル保存先は、 storeSettings.getFileStoreFolder()戻り値が使われます。
    デフォルトは、システムプロパティ javax.servlet.context.tempdir指定された値、
    設定がなければ、File.createTempFile("file-prefix", (String)null).getParentFile()得られたディレクトリが使用されます。
    DiskDataStore内の getStoreFolder以下フォルダを指定してますので、
        protected File getStoreFolder()
        {
            return new File(fileStoreFolder, applicationName + "-filestore");
        }
    
    実際には、javax.servlet.context.tempdir指定されたディレクトリの下に アプリケーション名 + "-filestore"
    いうディレクトリ作成され、その配下にページクラスがシリアライズされ、セッションごとに保存されていきます。


StoreSettings の 実装について

上記を踏まえて、StoreSettings の API について記載します。

メソッド名内容
setAsynchronous(boolean async)非同期で使用するか設定します。3
setAsynchronousQueueCapacity(int queueCapacity)非同期でDataStoreを使用する際のキューの容量を設定します。
setFileStoreFolder(File fileStoreFolder)DiskDataStoreが保存するフォルダを設定します。4
setInmemoryCacheSize(int inmemoryCacheSize)第2レベルキャッシュに格納されるページインスタンスの最大数を設定します。
setMaxSizePerSession(Bytes maxSizePerSession)DiskDataStoreに保村するセッションごとのページインスタンスが格納されるFileの最大サイズを設定します。

対になるgetメソッドが存在しますが、
省略します。

3.True を設定すると、IDataStoreには、AsynchronousDataStoreを介してアクセスされます。
4.DiskDataStore でのみ使用します。独自実装する場合は、明示的に使おうとしないと使われない。


DataStore を自前で実装する際の考慮点

調べた結果以下のことが、わかりました。

  • setFileStoreFolder について
    DiskDataStore を使用する場合のみ使用される。DataStore を独自実装する場合は、使用する実装にしないと使われないので注意。

  • setMaxSizePerSession について
    setFileStoreFolder同様に、DiskDataStore を使用する場合のみ使用される。
    意味的には、別のDataStore の場合も、データサイズ超えたら消すなど、使う実装にすることは可能だと思う。
    なので、設定できるようにすると、「格好は良い」かもしれない。

  • setInmemoryCacheSize について
    第2レベルキャッシュ の設定に関する値なので、DataStoreとして何を使ってもアプリケーションには影響する。
    小さい値を設定すれば、DataStore の値が使われる可能性が高まる。
    大きすぎるとアプリケーションのメモリを圧迫する

  • setAsynchronous について
    DataStoreによっては、「DataStoreの保存自体が非同期化されている」ので、その場合は意味がない。
    実装するDataStoreにより、どちらかは使い分けたほうがよさそう。

個人的には、第2レベルキャッシュ、が DataStoreだと思っていたので、
勉強になりました。

以上です。

コメント