I have a cache loader which caches the response( which is in form of a list) from APIs. Now my functions do not need this response data directly, so I made a provider which can process these raw response and act as an oracle which allows me to send the data required instead of whole list.
The issue with my current approach is that for each such response the provider has to iterate over the whole list even if the list was the same as before; this makes the complexity of each call O(n) instead of O(1) I wanted.
Potential solutions I can think of
- Cache the processed data instead. Now issue with this is that I would need two different caches in this case, and would be unnecessarily fetching from API same results in both caches per refresh.
- Iterate in provider only if cache value has changed I want do this but I can't find a way to know if cache has changed the values, all my ways would need to do a pass on the data which will again take O(n) time.
Can anyone suggest a good way to solve this issue?
Rule Info Cache Loader Class
import com.google.common.cache.CacheLoader;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.ParametersAreNonnullByDefault;
final class RuleInfoLoader extends CacheLoader<RuleType, List<RuleInfo>> {
private final ConfigServiceGrpc.ConfigServiceBlockingStub configServiceBlockingStub;
private final long callTimeout;
RuleInfoLoader(ConfigServiceClientConfig clientConfig) {
callTimeout = clientConfig.getCallTimeout();
configServiceBlockingStub = ConfigServiceGrpc.newBlockingStub();
}
@Override
@ParametersAreNonnullByDefault
public List<RuleInfo> load(RuleType ruleType) {
return fetchRuleInfo(ruleType);
}
private List<RuleInfo> fetchRuleInfo(RuleType ruleType) {
return configServiceBlockingStub
.withDeadlineAfter(callTimeout, TimeUnit.MILLISECONDS)
.getAnomalyRuleInfos(
GetRuleInfosRequest.newBuilder().setRuleType(ruleType).build())
.getRuleInfosList();
}
}
Rule Info Provider
import com.google.common.cache.CacheLoader;
import com.google.common.collect.ImmutableMap;
import com.typesafe.config.Config;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
public class RuleInfoProvider {
private LoadingCache<RuleType, List<RuleInfo>> ruleInfoCache;
public RuleInfoProvider(Config config) {
initCache(config);
}
public Map<String, RuleInfo> getRuleMapping() {
ImmutableMap.Builder<String, RuleInfo> mapBuilder = new ImmutableMap.Builder<>();
getRuleInfo(RuleType.a)
.forEach(rule -> mapBuilder.put(rule.getRuleId(), rule));
getRuleInfo(RuleType.b)
.forEach(rule -> mapBuilder.put(rule.getRuleId(), rule));
getRuleInfo(RuleType.c)
.forEach(rule -> mapBuilder.put(rule.getRuleId(), rule));
return mapBuilder.build();
}
public Map<Class<? extends RuleInfo>, List<String>> getPathsMapping() {
return new ImmutableMap.Builder<Class<? extends RuleInfo>, List<String>>()
.put(
Class_A.class,
getRuleInfo(RuleType.a).stream()
.map(RuleInfo::getPath)
.collect(Collectors.toList()))
.put(
Class_B.class,
getRuleInfo(RuleType.b).stream()
.map(RuleInfo::getPath)
.collect(Collectors.toList()))
.put(
Class_c.class,
getRuleInfo(RuleType.c).stream()
.map(RuleInfo::getPath)
.collect(Collectors.toList()))
.build();
}
private List<RuleInfo> getRuleInfo(RuleType ruleType) {
try {
return ruleInfoCache.get(ruleType);
} catch (Exception e) {
return Collections.emptyList();
}
}
private void initCache(Config config) {
ruleInfoCache =
CacheBuilder.newBuilder()
.refreshAfterWrite(config.getCacheConfig().getRefreshMs())
.maximumSize(config.getCacheConfig().getMaxSize())
.build(
CacheLoader.asyncReloading(
new RuleInfoLoader(config.getConfigServiceClientConfig()),
Executors.newSingleThreadScheduledExecutor()));
}
}