web-dev-qa-db-ja.com

値がnullのときにキャッシュを回避するにはどうすればよいですか?

ホットデータのキャッシュにグアバを使用しています。データがキャッシュに存在しない場合、このようなデータベースから取得する必要があります。

public final static LoadingCache<ObjectId, User> UID2UCache = CacheBuilder.newBuilder()
        //.maximumSize(2000)
        .weakKeys()
        .weakValues()
        .expireAfterAccess(10, TimeUnit.MINUTES)
        .build(
        new CacheLoader<ObjectId, User>() {
            @Override
            public User load(ObjectId k) throws Exception {
                User u = DataLoader.datastore.find(User.class).field("_id").equal(k).get();
                return u;
            }
        });

私の問題は、データがデータベースにも存在しない場合、nullを返してキャッシュを行わないことです。しかし、グアバはキャッシュ内のキーでnullを保存し、それを取得すると例外をスローします

com.google.common.cache.CacheLoader $ InvalidCacheLoadException:キーshisoftに対してCacheLoaderがnullを返しました。

それでは、null値のキャッシュを回避する方法は?

48
Shisoft

ユーザーが見つからない場合は例外をスローし、 get(key) メソッドの使用中にクライアントコードでキャッチします。

_new CacheLoader<ObjectId, User>() {
    @Override
    public User load(ObjectId k) throws Exception {
        User u = DataLoader.datastore.find(User.class).field("_id").equal(k).get();
        if (u != null) {
             return u;
        } else {
             throw new UserNotFoundException();
        }
    }
}
_

CacheLoader.load(K) Javadocから:

_Returns:  
  the value associated with key; must not be null  
Throws:  
  Exception - if unable to load the result
_

Null値のキャッシュに関する疑問に答える:

このキャッシュ内のキーに関連付けられた値を返し、必要に応じて最初にその値をロードします。 このキャッシュに関連付けられた監視可能な状態は、ロードが完了するまで変更されません

(from LoadingCache.get(K) Javadoc)から

例外をスローすると、ロードは完了したと見なされないため、新しい値はキャッシュされません。

[〜#〜] edit [〜#〜]

Caffeine は、Guavaキャッシュ2.0の一種であり、「Google Guavaに触発されたAPIを使用してメモリ内キャッシュを提供する」ことに注意してください cannullメソッドからloadを返します

_ Returns:
   the value associated with key or null if not found
_

移行を検討する場合、ユーザーが見つからない場合、データローダーは自由に戻ることができます。

72
Xaerxess

簡単な解決策:値としてUserの代わりにcom.google.common.base.Optional<User>を使用します。

public final static LoadingCache<ObjectId, Optional<User>> UID2UCache = CacheBuilder.newBuilder()
        ...
        .build(
        new CacheLoader<ObjectId, Optional<User>>() {
            @Override
            public Optional<User> load(ObjectId k) throws Exception {
                return Optional.fromNullable(DataLoader.datastore.find(User.class).field("_id").equal(k).get());
            }
        });

編集:@Xaerxessの答えの方が良いと思います。

同じ問題に直面して、ソースの値が欠落していることが通常のワークフローの一部でした。 getIfPresentget、およびputメソッドを使用して自分でコードを記述するよりも良いものは見つかりませんでした。以下のメソッドを参照してください。localCache<Object, Object>

private <K, V> V getFromLocalCache(K key, Supplier<V> fallback) {
    @SuppressWarnings("unchecked")
    V s = (V) local.getIfPresent(key);
    if (s != null) {
        return s;
    } else {
        V value = fallback.get();
        if (value != null) {
            local.put(key, value);
        }
        return value;
    }
}
3

一部のNULL値をキャッシュする場合は、NULLとして動作する他のスタッフを使用できます。

ソリューションを提供する前に、LoadingCacheを外部に公開しないことをお勧めします。代わりに、メソッドを使用してCacheのスコープを制限する必要があります。

たとえば、戻り型としてLoadingCache<ObjectId, List<User>>を使用できます。そして、データベースから値を取得できなかったときに空のリストを返すことができました。整数または長整数として-1を使用できます[〜#〜] null [〜#〜]値、文字列として ""を使用できます[〜#〜] null [〜#〜 ]値など。この後、NULL値を処理するメソッドを提供する必要があります。

when(value equals NULL(-1|"")){
   return null;
}
0
jayxhj