1 package com.atlassian.vcache.internal.core.service;
2
3 import com.atlassian.vcache.PutPolicy;
4 import com.atlassian.vcache.internal.core.ExternalCacheKeyGenerator;
5 import com.atlassian.vcache.internal.core.RecursionDetector;
6 import com.google.common.collect.BiMap;
7 import com.google.common.collect.HashBiMap;
8 import com.google.common.collect.Maps;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11
12 import java.util.Collections;
13 import java.util.HashMap;
14 import java.util.Map;
15 import java.util.Optional;
16 import java.util.Set;
17 import java.util.concurrent.CompletableFuture;
18 import java.util.concurrent.CompletionStage;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.function.BiFunction;
21 import java.util.function.Supplier;
22
23 import static java.util.Objects.requireNonNull;
24
25
26
27
28
29
30
31 public abstract class AbstractExternalCacheRequestContext<V> {
32 private static final Logger log = LoggerFactory.getLogger(AbstractExternalCacheRequestContext.class);
33
34 protected final String name;
35
36
37
38
39
40 private final RecursionDetector<String> recursionDetector = new RecursionDetector<>();
41 private final ExternalCacheKeyGenerator keyGenerator;
42 private final Supplier<String> partitionSupplier;
43
44
45 private final BiMap<String, String> internalToExternalKeyMap = Maps.synchronizedBiMap(HashBiMap.create());
46
47 private final Map<String, String> externalToInternalKeyMap = Collections.unmodifiableMap(
48 internalToExternalKeyMap.inverse());
49
50 private final Map<String, CompletionStage<Optional<V>>> internalKeyToValueMap = new ConcurrentHashMap<>();
51 private boolean hasRemoveAll;
52 private final Map<String, DeferredOperation<V>> keyedOperationMap = new HashMap<>();
53
54 protected AbstractExternalCacheRequestContext(ExternalCacheKeyGenerator keyGenerator,
55 String name,
56 Supplier<String> partitionSupplier) {
57 this.keyGenerator = requireNonNull(keyGenerator);
58 this.name = requireNonNull(name);
59 this.partitionSupplier = requireNonNull(partitionSupplier);
60 }
61
62 protected abstract long cacheVersion();
63
64 protected void clearKeyMaps() {
65 internalToExternalKeyMap.clear();
66 }
67
68 public String externalEntryKeyFor(String internalKey) {
69 final String cached = internalToExternalKeyMap.get(requireNonNull(internalKey));
70 if (cached != null) {
71 return cached;
72 }
73
74 final String result = keyGenerator.entryKey(
75 partitionSupplier.get(), name, cacheVersion(), internalKey);
76
77
78 internalToExternalKeyMap.forcePut(internalKey, result);
79 return result;
80 }
81
82
83
84
85
86
87
88
89 public String internalEntryKeyFor(String externalKey) {
90 return requireNonNull(externalToInternalKeyMap.get(externalKey));
91 }
92
93 public Optional<Optional<V>> getValueRecorded(String internalKey) {
94 final CompletionStage<Optional<V>> optionalCompletionStage = internalKeyToValueMap.get(requireNonNull(internalKey));
95 if (optionalCompletionStage == null || optionalCompletionStage.toCompletableFuture().isCompletedExceptionally()) {
96 return Optional.empty();
97 }
98 return Optional.ofNullable(optionalCompletionStage.toCompletableFuture().join());
99 }
100
101 public void recordValue(String internalKey, Optional<V> outcome) {
102 recordValue(requireNonNull(internalKey), CompletableFuture.completedFuture(outcome));
103 }
104
105 public void recordValue(String internalKey, CompletionStage<Optional<V>> outcome) {
106 log.trace("Cache {}, recording value for {}", name, internalKey);
107 internalKeyToValueMap.put(requireNonNull(internalKey), outcome);
108 }
109
110 public void recordValues(Map<String, V> knownValues) {
111 log.trace("Cache {}, recording {} known values", name, knownValues.size());
112 knownValues.entrySet().stream().collect(
113 () -> internalKeyToValueMap,
114 (m, e) -> m.put(e.getKey(), CompletableFuture.completedFuture(Optional.of(e.getValue()))),
115 Map::putAll);
116 }
117
118 public CompletionStage<Optional<V>> computeValue(
119 String key,
120 BiFunction<String, CompletionStage<Optional<V>>, CompletionStage<Optional<V>>> doCompute) {
121 try (RecursionDetector.Guard ignored = recursionDetector.guardOn(key)) {
122 return internalKeyToValueMap.compute(requireNonNull(key), doCompute);
123 }
124 }
125
126 public void forgetValue(String internalKey) {
127 log.trace("Cache {}, forgetting value for {}", name, internalKey);
128 internalKeyToValueMap.remove(internalKey);
129 }
130
131 public void forgetAllValues() {
132 log.trace("Cache {}, forgetting all values", name);
133 internalKeyToValueMap.clear();
134 }
135
136
137
138
139
140
141
142
143 public void recordPut(String internalKey, V value, PutPolicy policy) {
144 recordValue(internalKey, Optional.of(value));
145 recordPutPolicy(internalKey, value, policy);
146 }
147
148
149
150
151
152
153
154
155 public void recordPutPolicy(String internalKey, V value, PutPolicy policy) {
156 keyedOperationMap.put(requireNonNull(internalKey), DeferredOperation.putOperation(value, policy));
157 }
158
159 public void recordRemove(Iterable<String> internalKeys) {
160 for (String internalKey : internalKeys) {
161 recordValue(internalKey, Optional.empty());
162 keyedOperationMap.put(requireNonNull(internalKey), DeferredOperation.removeOperation());
163 }
164 }
165
166 public void recordRemoveAll() {
167 forgetAllValues();
168 hasRemoveAll = true;
169 keyedOperationMap.clear();
170 }
171
172 public boolean hasRemoveAll() {
173 return hasRemoveAll;
174 }
175
176 public void forgetAll() {
177 forgetAllValues();
178 hasRemoveAll = false;
179 keyedOperationMap.clear();
180 }
181
182 public Set<Map.Entry<String, DeferredOperation<V>>> getKeyedOperations() {
183 return keyedOperationMap.entrySet();
184 }
185
186 public boolean hasPendingOperations() {
187 return hasRemoveAll || !keyedOperationMap.isEmpty();
188 }
189
190
191
192
193
194
195 public static class DeferredOperation<V> {
196 private final boolean remove;
197 private final CompletionStage<Optional<V>> value;
198 private final Optional<PutPolicy> policy;
199
200 private DeferredOperation() {
201 this.remove = true;
202 this.value = CompletableFuture.completedFuture(Optional.empty());
203 this.policy = Optional.empty();
204 }
205
206 private DeferredOperation(V value, PutPolicy policy) {
207 this(CompletableFuture.completedFuture(Optional.of(value)), policy);
208 }
209
210 private DeferredOperation(CompletionStage<Optional<V>> value, PutPolicy policy) {
211 this.remove = false;
212 this.value = requireNonNull(value);
213 this.policy = Optional.of(policy);
214 }
215
216 public boolean isRemove() {
217 return remove;
218 }
219
220 public boolean isPut() {
221 return !remove;
222 }
223
224 public V getValue() {
225 return value.toCompletableFuture().join().get();
226 }
227
228 public PutPolicy getPolicy() {
229 return policy.get();
230 }
231
232 public static <V> DeferredOperation<V> removeOperation() {
233 return new DeferredOperation<>();
234 }
235
236 public static <V> DeferredOperation<V> putOperation(V value, PutPolicy policy) {
237 return new DeferredOperation<>(value, policy);
238 }
239 }
240 }