Skip to content

Instantly share code, notes, and snippets.

@mhewedy
Created September 28, 2023 04:12
Show Gist options
  • Save mhewedy/519b0441da587eae6efff81c1cb5e68e to your computer and use it in GitHub Desktop.
Save mhewedy/519b0441da587eae6efff81c1cb5e68e to your computer and use it in GitHub Desktop.
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisKeyValueTemplate;
import org.springframework.stereotype.Service;
import javax.persistence.Id;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import java.util.function.Supplier;
@Slf4j
@Service
@RequiredArgsConstructor
public class CacheService {
private final RedisKeyValueTemplate template;
/**
* A cache-safe method, that will always fall back to cache if unable to get data from the source.
* <p>
* Use with critical data that should be always present, regardless of the remote data source availability.
*/
public <T extends AbstractCache> Optional<T> getOrElseFromCache(Object id, Class<T> clazz, Duration cacheTtl,
Supplier<T> getFromSourceFn) {
var objectFromCache = template.findById(id, clazz).orElse(null);
if (objectFromCache != null &&
objectFromCache.createdDate.plus(cacheTtl).isAfter(Instant.now())) {
log.debug("get {} with id: {} from cache, value: {}", clazz.getSimpleName(), id, objectFromCache);
return Optional.of(objectFromCache);
}
log.debug("{} for id : {} either ttl exceed or not found in cache: {}, getting from source",
clazz.getSimpleName(), id, objectFromCache);
try {
log.debug("try getting value of {} for id: {} from source", clazz.getSimpleName(), id);
var objectFromSource = getFromSourceFn.get();
if (objectFromSource != null) {
log.debug("found {} for id: {} in source, caching...", clazz.getSimpleName(), id);
objectFromSource.id = id;
objectFromSource.createdDate = Instant.now();
template.update(objectFromSource);
return Optional.of(objectFromSource);
}
} catch (Exception ex) {
log.warn("cannot get {} for id : {} from source, cause: {}", clazz.getSimpleName(), id, ex.getMessage());
}
if (objectFromCache != null) {
log.warn("{} expired, but cannot get from source, get stale value from cache for: {}, value: {}",
clazz.getSimpleName(), id, objectFromCache);
return Optional.of(objectFromCache);
}
log.error("{} not found in cache and cannot loaded from source, for id: {}, return as empty",
clazz.getSimpleName(), id);
return Optional.empty();
}
public static abstract class AbstractCache {
@Id
public Object id;
public Instant createdDate;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment