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