簡體   English   中英

除非不滿足條件,否則 Spring 緩存值不會被更新

[英]Spring cache value not being renewed when unless condition is not met

我對 Cacheable 注釋的除非條件有疑問。

從文檔中,我了解到,在調用被注釋的方法之后驗證了除非條件,並且僅在不滿足條件時才緩存(並實際返回)方法返回的值。 否則,應該返回緩存的值。

首先,這個假設是真的嗎?

編輯:

[來自 Spring 文檔] 顧名思義,@Cacheable 用於划分可緩存的方法 - 即,在后續調用(使用相同的參數)時將結果存儲到緩存中的方法,在無需實際執行該方法即可返回緩存。

【我的理解】所以對於給定的key,方法會一直執行,直到unless條件不滿足一次。 然后將為所有后續調用該方法返回緩存的值。

為了說明我的問題,我嘗試將我的代碼分解為四個簡單的類:

1) DummyObject,表示要緩存和檢索的實例。 它是一個時間戳包裝器,用於顯示緩存的最后一個值。 toBeCached boolean 是一個標志,應該在除非條件中檢查以了解返回的實例是否應該被緩存。

2) DummyDAO 根據提供的鍵返回 DummyObject 實例。 在檢索實例時,DAO 檢查最后一次檢索的值是什么時候,並驗證它是否應該被緩存(與提供的鍵無關。這個邏輯是否“損壞”無關緊要,因為我總是使用對於我的示例,相同的鍵)。 然后 DAO 用 toBeCached 標志標記返回的實例。 如果該值被標記為緩存,則 DAO 實際上會更新其 lastRetrieved 時間戳,因為實例最終應由 CachedDAO 緩存(因為不會滿足除非條件)。

3) DummyCachedDao 調用 DummyDAO 來檢索 DummyObject 的實例。 如果實例被標記為BeCached,它應該緩存新返回的值。 否則它應該返回先前緩存的值。

4) 應用程序檢索一個值(將被緩存),休眠一小段時間(不足以讓緩存持續時間通過),檢索一個值(應該是緩存的值),再次休眠(足夠長要傳遞的緩存持續時間),再次檢索一個值(應該是要緩存的新值)。

不幸的是,此代碼無法按預期工作,因為檢索到的值始終是已緩存的原始值。 為了確保邏輯按預期工作,我通過在應用程序 class 中將 retrieveTimestamp 替換為 retrieveTimestampBypass 來檢查是否滿足條件。 由於內部調用繞過 Spring 代理,retrieveTimestamp 方法和我輸入的任何斷點或日志實際上都被捕獲/顯示。

什么會導致該值不再被緩存? 緩存是否需要先從以前的值中清除?

public class DummyObject
{
  private long timestamp;
  private boolean toBeCached;

  public DummyObject(long timestamp, boolean toBeCached)
  {
    this.timestamp = timestamp;
    this.toBeCached = toBeCached;
  }

  public long getTimestamp()
  {
    return timestamp;
  }

  public boolean isToBeCached()
  {
    return toBeCached;
  }
}
@Service
public class DummyDAO
{
  private long cacheDuration = 3000;
  private long lastRetrieved;

  public DummyObject retrieveTimestamp(String key)
  {
    long renewalTime = lastRetrieved + cacheDuration;
    long time = System.currentTimeMillis();
    boolean markedToBeCached = renewalTime < time;

    System.out.println(renewalTime + " < " + time + " = " + markedToBeCached);

    if(markedToBeCached)
    {
      lastRetrieved = time;
    }

    return new DummyObject(time, markedToBeCached);
  }
}
@Service
public class DummyCachedDAO
{
    @Autowired
    private DummyDAO dao;

    // to check the flow.
    public DummyObject retrieveTimestampBypass(String key)
    {
        return retrieveTimestamp(key);
    }

    @Cacheable(cacheNames = "timestamps", unless = "#result.isToBeCached() != true")
    public DummyObject retrieveTimestamp(String key)
    {
      return dao.retrieveTimestamp(key); 
    }
}
@SpringBootApplication
@EnableCaching
public class Application
{
  public final static String KEY = "cache";
  public final static String MESSAGE = "Cached timestamp is: %s [%s]";

  public static void main(String[] args) throws InterruptedException
  {
    SpringApplication app = new SpringApplication(Application.class);
    ApplicationContext context = app.run(args);
    DummyCachedDAO cache = (DummyCachedDAO) context.getBean(DummyCachedDAO.class);

    // new value
    long value = cache.retrieveTimestamp(KEY).getTimestamp();
    System.out.println(String.format(MESSAGE, value, new Date(value)));

    Thread.sleep(1000);

    // expecting same value
    value = cache.retrieveTimestamp(KEY).getTimestamp();
    System.out.println(String.format(MESSAGE, value, new Date(value));

    Thread.sleep(5000);

    // expecting new value
    value = cache.retrieveTimestamp(KEY).getTimestamp();
    System.out.println(String.format(MESSAGE, value, new Date(value));

    SpringApplication.exit(context, () -> 0);
  }
}

這里有很多細節,可能還有問題,但首先你應該刪除

private long lastRetrieved;

來自 DummyDao class。 DummyDao 是 singleton 實例 lastRetrieved 字段不是線程安全的。

正如您在第一次緩存項目后從日志中看到的那樣,它將始終從那里檢索,因為它已在第一次調用中緩存。

否則你應該看到以下日志

3000 < 1590064933733 = true

這個問題其實很簡單。 我的問題沒有解決方案,這是理所當然的。

我最初的假設是“每次調用被注釋的方法后都會驗證除非條件,並且只有在不滿足條件時才緩存(並實際返回)方法返回的值。否則,緩存的值應該被退回。

但是,這不是實際行為,因為正如文檔所述,“@Cacheable 用於划分可緩存的方法 - 即將結果存儲到緩存中的方法等后續調用(使用相同的參數),無需實際執行該方法即可返回緩存中的值。”

因此,對於給定的鍵,該方法將始終執行,直到一次不滿足除非條件。 然后將為所有后續調用該方法返回緩存的值。

因此,我嘗試通過嘗試使用注釋組合(@Caching 與@Cacheable 和@CachePut,盡管文檔建議不要這樣做),以不同的方式為我的實驗解決問題。 我檢索的值始終是新值,而緩存中的值始終是預期值。 (*)


那是當我傾斜我無法根據內部時間戳上傳緩存中的值否則。

每次執行該方法以計算最新值但返回緩存的值有什么意義(因為我設置了除非條件)? 無關緊要...

如果可緩存的條件是指定何時檢索緩存版本或檢索/生成新版本,我想要實現的目標(如果期限已過則更新緩存)是可能的。 據我所知,可緩存的只是指定何時需要首先緩存方法。


我的實驗到此結束。 當我遇到一個實際生產項目的問題時,需要對此進行測試,該項目使用內部時間戳,除非條件。 僅供參考,這個問題最明顯的解決方案是使用實際提供 TTL 功能的緩存提供程序。

(*) PS:我還嘗試了@CacheEvict(條件=“#root.target.myNewExpiringCheckMethod()==true”)和@Cacheable 的幾個@caching 組合,但它失敗了,並且 CacheEvict 強制執行注釋方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM