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.forEach((key, value) -> internalKeyToValueMap.put(key, Optional.of(value)));
108 }
109
110 public void forgetValue(String internalKey) {
111 log.trace("Cache {}, forgetting value for {}", name, internalKey);
112 internalKeyToValueMap.remove(internalKey);
113 }
114
115 public void forgetAllValues() {
116 log.trace("Cache {}, forgetting all values", name);
117 internalKeyToValueMap.clear();
118 }
119
120
121
122
123
124
125
126
127 public void recordPut(String internalKey, V value, PutPolicy policy) {
128 recordValue(internalKey, Optional.of(value));
129 recordPutPolicy(internalKey, value, policy);
130 }
131
132
133
134
135
136
137
138
139 public void recordPutPolicy(String internalKey, V value, PutPolicy policy) {
140 keyedOperationMap.put(requireNonNull(internalKey), DeferredOperation.putOperation(value, policy));
141 }
142
143 public void recordRemove(Iterable<String> internalKeys) {
144 for (String internalKey : internalKeys) {
145 recordValue(internalKey, Optional.empty());
146 keyedOperationMap.put(requireNonNull(internalKey), DeferredOperation.removeOperation());
147 }
148 }
149
150 public void recordRemoveAll() {
151 forgetAllValues();
152 hasRemoveAll = true;
153 keyedOperationMap.clear();
154 }
155
156 public boolean hasRemoveAll() {
157 return hasRemoveAll;
158 }
159
160 public void forgetAll() {
161 forgetAllValues();
162 hasRemoveAll = false;
163 keyedOperationMap.clear();
164 }
165
166 public Set<Map.Entry<String, DeferredOperation<V>>> getKeyedOperations() {
167 return keyedOperationMap.entrySet();
168 }
169
170 public boolean hasPendingOperations() {
171 return hasRemoveAll || !keyedOperationMap.isEmpty();
172 }
173
174
175
176
177
178
179 public static class DeferredOperation<V> {
180 private final boolean remove;
181 private final CompletionStage<Optional<V>> value;
182 private final Optional<PutPolicy> policy;
183
184 private DeferredOperation() {
185 this.remove = true;
186 this.value = CompletableFuture.completedFuture(Optional.empty());
187 this.policy = Optional.empty();
188 }
189
190 private DeferredOperation(V value, PutPolicy policy) {
191 this(CompletableFuture.completedFuture(Optional.of(value)), policy);
192 }
193
194 private DeferredOperation(CompletionStage<Optional<V>> value, PutPolicy policy) {
195 this.remove = false;
196 this.value = requireNonNull(value);
197 this.policy = Optional.of(policy);
198 }
199
200 public boolean isRemove() {
201 return remove;
202 }
203
204 public boolean isPut() {
205 return !remove;
206 }
207
208 public V getValue() {
209 return unsafeJoin(value).get();
210 }
211
212 public PutPolicy getPolicy() {
213 return policy.get();
214 }
215
216 public static <V> DeferredOperation<V> removeOperation() {
217 return new DeferredOperation<>();
218 }
219
220 public static <V> DeferredOperation<V> putOperation(V value, PutPolicy policy) {
221 return new DeferredOperation<>(value, policy);
222 }
223 }
224 }