MCPcopy Create free account
hub / github.com/InterviewReady/Low-Level-Design / Cache

Class Cache

distributed-cache/src/main/java/Cache.java:14–181  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

12import java.util.function.Function;
13
14public class Cache<KEY, VALUE> {
15 private final int maximumSize;
16 private final FetchAlgorithm fetchAlgorithm;
17 private final Duration expiryTime;
18 private final Map<KEY, CompletionStage<Record<KEY, VALUE>>> cache;
19 private final ConcurrentSkipListMap<AccessDetails, List<KEY>> priorityQueue;
20 private final ConcurrentSkipListMap<Long, List<KEY>> expiryQueue;
21 private final DataSource<KEY, VALUE> dataSource;
22 private final List<Event<KEY, VALUE>> eventQueue;
23 private final ExecutorService[] executorPool;
24 private final Timer timer;
25
26 protected Cache(final int maximumSize,
27 final Duration expiryTime,
28 final FetchAlgorithm fetchAlgorithm,
29 final EvictionAlgorithm evictionAlgorithm,
30 final DataSource<KEY, VALUE> dataSource,
31 final Set<KEY> keysToEagerlyLoad,
32 final Timer timer,
33 final int poolSize) {
34 this.expiryTime = expiryTime;
35 this.maximumSize = maximumSize;
36 this.fetchAlgorithm = fetchAlgorithm;
37 this.timer = timer;
38 this.cache = new ConcurrentHashMap<>();
39 this.eventQueue = new CopyOnWriteArrayList<>();
40 this.dataSource = dataSource;
41 this.executorPool = new ExecutorService[poolSize];
42 for (int i = 0; i < poolSize; i++) {
43 executorPool[i] = Executors.newSingleThreadExecutor();
44 }
45 priorityQueue = new ConcurrentSkipListMap<>((first, second) -> {
46 final var accessTimeDifference = (int) (first.getLastAccessTime() - second.getLastAccessTime());
47 if (evictionAlgorithm.equals(EvictionAlgorithm.LRU)) {
48 return accessTimeDifference;
49 } else {
50 final var accessCountDifference = first.getAccessCount() - second.getAccessCount();
51 return accessCountDifference != 0 ? accessCountDifference : accessTimeDifference;
52 }
53 });
54 expiryQueue = new ConcurrentSkipListMap<>();
55 final var eagerLoading = keysToEagerlyLoad.stream()
56 .map(key -> getThreadFor(key, addToCache(key, loadFromDB(dataSource, key))))
57 .toArray(CompletableFuture[]::new);
58 CompletableFuture.allOf(eagerLoading).join();
59 }
60
61 private <U> CompletionStage<U> getThreadFor(KEY key, CompletionStage<U> task) {
62 return CompletableFuture.supplyAsync(() -> task, executorPool[Math.abs(key.hashCode() % executorPool.length)]).thenCompose(Function.identity());
63 }
64
65 public CompletionStage<VALUE> get(KEY key) {
66 return getThreadFor(key, getFromCache(key));
67 }
68
69 public CompletionStage<Void> set(KEY key, VALUE value) {
70 return getThreadFor(key, setInCache(key, value));
71 }

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected