blog

SpringBootでは、空の@Cacheableキャッシュオブジェクトの使い方に落とし穴があった。

今日、本番環境では、@Cacheableの問題が発生し、レコード1は、問題のインターフェイスが突然リクエストの失敗を発見し、ログをクエリすると、次のエラーログメッセージは非常に明確に言う、キャッシュは...

Jul 16, 2020 · 3 min. read
シェア

今日、本番環境で@Cacheableの問題に遭遇したので記録しておきます。

1.問題の特定

インターフェイスは突然リクエストに失敗し、ログを照会すると次のようなエラー・レポートが表示されます。

Cache 'cache:getCustRange' does not allow 'null' values. Avoid storing null via '@Cacheable(unless="#result == null")' or configure RedisCache to allow 'null' via RedisCacheConfiguration. java.lang.IllegalArgumentException: Cache 'cache:getCustRange' does not allow 'null' values. Avoid storing null via '@Cacheable(unless="#result == null")' or configure RedisCache to allow 'null' via RedisCacheConfiguration

ロギングのヒント・メッセージには、NULL値をキャッシュに保存することは許可されていないとはっきりと書かれています。

または、RedisCacheConfiguration を設定して、NULL 値をキャッシュに保存できるようにします。

問題のコードを見てみましょう。

@Cacheable(cacheNames = "cache:getCustRange", key = "#root.args[0]['custId'] + ''")
 public CustRangeVo getCustRange(Map<String, Object> map) {
 return custMapper.getCustRange(map);
 }

明らかに、ここでの CustRangeVo オブジェクトは空であり、キャッシュに保存するとエラーが発生します。

2.問題解決

この問題を解決する2つの方法

  • 結果をnullに設定してもキャッシュされません
  • 許容キャッシュをヌル値に設定します。

結果がnullの場合はキャッシュしないように設定します。unless="#result == null "を直接追加するだけです。

@Cacheable(cacheNames = "cache:getCustRange", key = "#root.args[0]['custId'] + ''", unless="#result == null")
 public CustRangeVo getCustRange(Map<String, Object> map) {
 return custMapper.getCustRange(map);
 }

ここでの私のシナリオは、99.9%はデータがクエリされ、極端な場合にのみオブジェクトがNULLになるということです。

シナリオのクエリのほとんどが null で、null をキャッシュしない場合、リクエストのほとんどがデータベースにヒットすることになり、キャッシュを追加する意味がなくなります。

許容キャッシュを null 値に設定するには、RedisCacheConfiguration を設定する必要があります。

public void put(Object key, @Nullable Object value) {
		Object cacheValue = preProcessCacheValue(value);
		//このエラーが報告される場所には条件判定があり、値がnullであることが許されない場合にのみ報告されることがわかる。
		if (!isAllowNullValues() && cacheValue == null) {
			throw new IllegalArgumentException(String.format(
					"Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless="#result == null")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.",
					name));
		}
		cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
	}

では、このisAllowNullValues() を見てみましょう。

protected RedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) {
		//ここでわかるように、allowCacheNummValues は結局 RedisCacheConfiguration のcacheNullValues
		super(cacheConfig.getAllowCacheNullValues());
		Assert.notNull(name, "Name must not be null!");
		Assert.notNull(cacheWriter, "CacheWriter must not be null!");
		Assert.notNull(cacheConfig, "CacheConfig must not be null!");
		this.name = name;
		this.cacheWriter = cacheWriter;
		this.cacheConfig = cacheConfig;
		this.conversionService = cacheConfig.getConversionService();
	}

RedisCacheConfigurationのソリューションはテストしていません。

Read next

Vue2.x-ソース学習 - 仮想DOM - 考える質問

A. 仮想 DOM を使うことで、DOM を手動で操作する必要がなくなります。 B. 仮想 DOM を使用すると、手動で DOM を操作する必要がないため、開発効率が大幅に向上します。 C. 仮想DOMはアプリケーションの状態を保持し、2つの状態の違いを比較することで実DOMを更新します。 D. 仮想DOMは本質的にJavaScri...

Jul 16, 2020 · 2 min read