2012-11-22 5 views
10

Guavaキャッシュを使用している間に私が実際に理解していない結果が得られました。GuavaキャッシングrefreshAfterWrite混乱

私は非同期にリフレッシュしたい単一のキーキャッシュを実装しています。

私は毎秒キャッシュに達し、refreshAfterWriteを20秒に設定しました。 ロード/リロード機能に5秒かかります。

負荷コールが
リロードコールがで開始午後12時00分00秒で開始:私はこのようないくつかの結果を期待 - 私は、ロード/リロード方法の開始時に現在の時刻をプリントアウトした場合

夜12時〇〇分25秒
リロードコールだから負荷が5秒かかりますし、次の書き込みは、その20秒後(5 + 20 = 25)をトリガーする午後12時00分50秒

で開始しました。その書き込みは、(25 + 5 + 20 = 50)秒その後50秒で起こるであろう。..など

は、代わりに私が手:

負荷コール00:00:00
リロード呼び出しで開始開始夜12時〇〇分25秒で
リロード呼び出しは夜12時〇〇分30秒

で開始これは、第二のリロードが最初のリロードの処理が完了したストレートの後に起こることを示唆しています。

私は未来が処理されているので、次のリロードは、その20秒後に予定されるだろう後に書き込みが起こるだろうと思いましたか?

refreshAfterWriteの動作に関する基本的な誤解がありましたか?

サンプルコード以下の通りです:

private static SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss"); 

    public static void main(String[] args) throws ExecutionException, InterruptedException { 

     final ExecutorService executor = Executors.newFixedThreadPool(3); 

     final LoadingCache<String, Long> cache = CacheBuilder.newBuilder().maximumSize(1) // 
       .refreshAfterWrite(20, TimeUnit.SECONDS)// 
       .build(new CacheLoader<String, Long>() {// 
        public Long load(String key) { 
         return getLongRunningProcess("load", key); 
        } 

        public ListenableFuture<Long> reload(final String key, Long prevGraph) { 
         ListenableFutureTask<Long> task = ListenableFutureTask.create(new Callable<Long>() { 
          public Long call() { 
           return getLongRunningProcess("reload", key); 
          } 
         }); 
         executor.execute(task); 
         return task; 
        } 
       }); 

     while (true) { 
      Thread.sleep(1000L); 
      cache.get(CACHE_KEY); 
     } 
    } 

    private static Long getLongRunningProcess(String callType, String key) { 
     System.out.printf("%s call started at %s\n", callType, format.format(new Date())); 
     try { 
      Thread.sleep(5000L); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     return counter.getAndIncrement(); 
    } 

} 
+0

数週間前、私は忘れられがちな単一要素キャッシュ機能を探していました。残念ながら、これはまだグアバではサポートされていないので、私は自分自身で書きました。 http://codereview.stackexchange.com/questions/18056/ability-to-forget-the-memoized-supplier-valueでこれを行う試みを見て、必要に応じてこのコードを自由に使用してください。 – mindas

答えて

7

私はあなたが合法的なバグを見つけたと思います。 (私はcommon.cacheを維持するのに役立ちます。)

私が正しく物事を以下だ場合、私は次のように一連の出来事があると信じて:

されるのは、Aは、リフレッシュを引き起こす最初のgetで取得しましょう、とBを取得しますそれ以降の最初のget

  • scheduleRefreshを呼び出すと、executorでrefreshタスクが開始されます。エントリ値の参照はLoadingValueReferenceに置き換えられ、loadAsyncはリロードが完了するのを待機するリスナを追加します。
  • Get Aリロードのforkedタスクが完了し、ロックが取得されます。
  • Bコールを受けるscheduleRefreshアクセス時間はまだ更新されていないので、進捗してinsertLoadingValueReferenceに入ります。
  • Get Aリロードのforkedタスクは書き込み時間を更新し、ロードが完了しているために値参照をStrongValueReferenceに置き換えます。ロックが解除されます。
  • Get Bは、値がまだロード中ではないと判断したので、新しいリロードを開始します。

(更新:https://code.google.com/p/guava-libraries/issues/detail?id=1211を提出した。)

+0

返信いただきありがとうございます!私の基本的な理解は、正しい振る舞いが何であるべきかについては健全ですか?すなわち、キャッシュがリフレッシュをプリフォームするのに要する時間よりも短い一定の間隔で要求を受け取る場合、リフレッシュは次のものに対応する一定の時間間隔で開始すると考えられます。(リフレッシュを実行するのにかかる時間)+(リフレッシュ間隔の継続時間)? - 定期的なリフレッシュのスケジューリングがきれいになるので、私はこれをベースにしています。 – plasma147

+0

それは私の仕様の読み方と一貫しています。 –