Как я могу кешировать элементы в RxJava и избежать паники?

Допустим, у меня есть следующий код:

Entity getEntity(GUID entityId) {
  Entity entity = entityLRUCache.get(entityId);
  if (entity == null) {
    entity = longLoadFromDatabase(entityId);
    entityLRUCache.put(entityId, entity);
  }
  return entity;
}

Благодаря @BenManes я могу использовать Caffeine для решения проблем с кэшем:

Entity getEntity(GUID entityId) {
  return entityCache.get(entityId, this::longLoadFromDatabase);
}

Но сейчас метод longLoadFromDatabase возвращает Single<Entity> вместо самой сущности (этот второй аргумент для get это картограф из int -> Entity), поэтому предыдущие решения больше не будут работать.

2 ответа

Решение

Вы можете использовать SingleSubject в качестве заполнителя на параллельной карте:

ConcurrentMap<GUID, SingleSubject<Entity>> map = ...

public Single<Entity> getEntity(GUID guid) {
    SingleSubject<Entity> e = map.get(guid);
    if (e == null) {
        e = SingleSubject.create();
        SingleSubject<Entity> f = map.putIfAbsent(guid, e);
        if (f == null) {
            longLoadFromDatabase(guid).subscribe(e);
        } else {
            e = f;
        }
    }
    return e;
}

Комбинируя предложение @akarnokd с асинхронной поддержкой Caffeine, вы можете использовать AsyncLoadingCache с конвертерами Rx.

AsyncLoadingCache<Integer, Entity> cache = Caffeine.newBuilder()
    .buildAsync((key, executor) -> SingleInterop.get()
        .apply(longLoadFromDatabase(key)).toCompletableFuture());
...
return SingleInterop.fromFuture(cache.get(123));

Кэш автоматически удалит запись, если в будущем произойдет сбой с исключением или он будет преобразован в нулевое значение.

Другие вопросы по тегам