1 package com.atlassian.vcache.internal.core.service;
2
3 import com.atlassian.marshalling.api.MarshallingPair;
4 import com.atlassian.vcache.ChangeRate;
5 import com.atlassian.vcache.DirectExternalCache;
6 import com.atlassian.vcache.ExternalCache;
7 import com.atlassian.vcache.ExternalCacheException;
8 import com.atlassian.vcache.ExternalCacheSettings;
9 import com.atlassian.vcache.JvmCache;
10 import com.atlassian.vcache.JvmCacheSettings;
11 import com.atlassian.vcache.Marshaller;
12 import com.atlassian.vcache.RequestCache;
13 import com.atlassian.vcache.RequestCacheSettings;
14 import com.atlassian.vcache.StableReadExternalCache;
15 import com.atlassian.vcache.TransactionalExternalCache;
16 import com.atlassian.vcache.VCacheFactory;
17 import com.atlassian.vcache.internal.BegunTransactionalActivityHandler;
18 import com.atlassian.vcache.internal.ExternalCacheDetails;
19 import com.atlassian.vcache.internal.JvmCacheDetails;
20 import com.atlassian.vcache.internal.RequestCacheDetails;
21 import com.atlassian.vcache.internal.RequestContext;
22 import com.atlassian.vcache.internal.RequestMetrics;
23 import com.atlassian.vcache.internal.VCacheCreationHandler;
24 import com.atlassian.vcache.internal.VCacheLifecycleManager;
25 import com.atlassian.vcache.internal.VCacheManagement;
26 import com.atlassian.vcache.internal.VCacheSettingsDefaultsProvider;
27 import com.atlassian.vcache.internal.core.DefaultExternalCacheDetails;
28 import com.atlassian.vcache.internal.core.DefaultJvmCacheDetails;
29 import com.atlassian.vcache.internal.core.DefaultRequestCacheDetails;
30 import com.atlassian.vcache.internal.core.DefaultTransactionControlManager;
31 import com.atlassian.vcache.internal.core.ExternalCacheKeyGenerator;
32 import com.atlassian.vcache.internal.core.TransactionControlManager;
33 import com.atlassian.vcache.internal.core.metrics.MetricsCollector;
34 import com.google.common.annotations.VisibleForTesting;
35 import org.slf4j.Logger;
36
37 import java.time.Duration;
38 import java.util.Arrays;
39 import java.util.Collections;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.concurrent.ConcurrentHashMap;
45 import java.util.function.Function;
46 import java.util.function.Supplier;
47
48 import static com.atlassian.vcache.internal.NameValidator.requireValidCacheName;
49 import static java.util.Objects.requireNonNull;
50
51
52
53
54
55
56 public abstract class AbstractVCacheService implements VCacheFactory, VCacheManagement, VCacheLifecycleManager {
57
58
59
60
61 private static final Set<String> SERIALIZABLE_MARSHALLER_CLASS_NAMES =
62 Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
63 "com.atlassian.vcache.marshallers.StringMarshaller",
64 "com.atlassian.vcache.marshallers.JavaSerializationMarshaller")));
65 private static final Set<String> SERIALIZABLE_MARSHALLING_CLASS_NAMES =
66 Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
67 "com.atlassian.marshalling.jdk.JavaSerializationMarshalling",
68 "com.atlassian.marshalling.jdk.StringMarshalling")));
69
70 protected final Supplier<RequestContext> workContextContextSupplier;
71 protected final Supplier<RequestContext> threadLocalContextSupplier;
72 protected final TransactionControlManager transactionControlManager;
73 protected final MetricsCollector metricsCollector;
74
75 protected final ExternalCacheKeyGenerator externalCacheKeyGenerator;
76 protected final Duration lockTimeout;
77 private final VCacheSettingsDefaultsProvider defaultsProvider;
78 private final VCacheCreationHandler creationHandler;
79
80 private final ConcurrentHashMap<String, JvmCache> jvmCacheInstancesMap = new ConcurrentHashMap<>();
81 private final ConcurrentHashMap<String, JvmCacheDetails> jvmCacheDetailsMap = new ConcurrentHashMap<>();
82
83 private final ConcurrentHashMap<String, RequestCache> requestCacheInstancesMap = new ConcurrentHashMap<>();
84 private final ConcurrentHashMap<String, RequestCacheDetails> requestCacheDetailsMap = new ConcurrentHashMap<>();
85
86 private final ConcurrentHashMap<String, ExternalCache> externalCacheInstancesMap = new ConcurrentHashMap<>();
87 private final ConcurrentHashMap<String, ExternalCacheDetails> externalCacheDetailsMap = new ConcurrentHashMap<>();
88
89 public AbstractVCacheService(Supplier<RequestContext> threadLocalContextSupplier,
90 Supplier<RequestContext> workContextContextSupplier,
91 VCacheSettingsDefaultsProvider defaultsProvider,
92 VCacheCreationHandler creationHandler,
93 MetricsCollector metricsCollector,
94 ExternalCacheKeyGenerator externalCacheKeyGenerator,
95 BegunTransactionalActivityHandler begunTransactionalActivityHandler,
96 Duration lockTimeout) {
97 this.threadLocalContextSupplier = requireNonNull(threadLocalContextSupplier);
98 this.workContextContextSupplier = requireNonNull(workContextContextSupplier);
99 this.defaultsProvider = requireNonNull(defaultsProvider);
100 this.creationHandler = requireNonNull(creationHandler);
101 this.metricsCollector = requireNonNull(metricsCollector);
102 this.transactionControlManager = new DefaultTransactionControlManager(metricsCollector, begunTransactionalActivityHandler);
103 this.externalCacheKeyGenerator = requireNonNull(externalCacheKeyGenerator);
104 this.lockTimeout = requireNonNull(lockTimeout);
105 }
106
107
108
109
110
111
112 protected abstract Logger log();
113
114
115
116
117
118
119
120
121
122
123 protected abstract <K, V> JvmCache<K, V> createJvmCache(String name, JvmCacheSettings settings);
124
125
126
127
128
129
130
131
132
133
134
135 protected abstract <V> TransactionalExternalCache<V> createTransactionalExternalCache(
136 String name, ExternalCacheSettings settings, MarshallingPair<V> valueMarshalling, boolean valueSerializable);
137
138
139
140
141
142
143
144
145
146
147
148 protected abstract <V> StableReadExternalCache<V> createStableReadExternalCache(
149 String name, ExternalCacheSettings settings, MarshallingPair<V> valueMarshalling, boolean valueSerializable);
150
151
152
153
154
155
156
157
158
159
160
161 protected abstract <V> DirectExternalCache<V> createDirectExternalCache(
162 String name, ExternalCacheSettings settings, MarshallingPair<V> valueMarshalling, boolean valueSerializable);
163
164 @Override
165 @SuppressWarnings("unchecked")
166 public <K, V> JvmCache<K, V> getJvmCache(String name, JvmCacheSettings settings) {
167 return jvmCacheInstancesMap.computeIfAbsent(requireValidCacheName(name), s -> {
168 log().trace("Cache {}: creating the instance", name);
169 final JvmCacheSettings candidateSettings = defaultsProvider.getJvmDefaults(name).override(settings);
170 final JvmCacheSettings finalSettings =
171 creationHandler.jvmCacheCreation(new DefaultJvmCacheDetails(name, candidateSettings));
172 jvmCacheDetailsMap.put(name, new DefaultJvmCacheDetails(name, finalSettings));
173
174 return metricsCollector.wrap(createJvmCache(name, finalSettings));
175 });
176 }
177
178 @SuppressWarnings("unchecked")
179 public <K, V> RequestCache<K, V> getRequestCache(String name, RequestCacheSettings settings) {
180 return requestCacheInstancesMap.computeIfAbsent(requireValidCacheName(name), s -> {
181 log().trace("DefaultRequestCache {}: creating the instance", name);
182 creationHandler.requestCacheCreation(name);
183 requestCacheDetailsMap.put(name, new DefaultRequestCacheDetails(name));
184 if (settings.getChangeRate().equals(ChangeRate.NONE)) {
185 return metricsCollector.wrap(new ReadOptimisedRequestCache(name, workContextContextSupplier, lockTimeout));
186 } else {
187 return metricsCollector.wrap(new DefaultRequestCache(name, workContextContextSupplier, lockTimeout));
188 }
189 });
190 }
191
192 @Override
193 public <V> TransactionalExternalCache<V> getTransactionalExternalCache(String name, MarshallingPair<V> valueMarshalling, ExternalCacheSettings settings) {
194 final MarshallingPair<V> wrappedMarshalling = metricsCollector.wrap(valueMarshalling, name);
195
196 final TransactionalExternalCache<V> cache = obtainCache(
197 name, ExternalCacheDetails.BufferPolicy.FULLY, settings, finalSettings ->
198 createTransactionalExternalCache(
199 name, finalSettings, wrappedMarshalling, isValueSerializable(valueMarshalling)));
200 return metricsCollector.wrap(cache);
201 }
202
203 @Override
204 public <V> TransactionalExternalCache<V> getTransactionalExternalCache(
205 String name, @SuppressWarnings("deprecation") Marshaller<V> valueMarshaller, ExternalCacheSettings settings) {
206 final MarshallingPair<V> marshallingPair =
207 new MarshallingPair<>(valueMarshaller, valueMarshaller);
208 final MarshallingPair<V> wrappedMarshalling = metricsCollector.wrap(marshallingPair, name);
209
210 final TransactionalExternalCache<V> cache = obtainCache(
211 name, ExternalCacheDetails.BufferPolicy.FULLY, settings, finalSettings ->
212 createTransactionalExternalCache(
213 name, finalSettings, wrappedMarshalling, isValueSerializable(valueMarshaller)));
214 return metricsCollector.wrap(cache);
215 }
216
217 @Override
218 public <V> StableReadExternalCache<V> getStableReadExternalCache(String name, MarshallingPair<V> valueMarshalling, ExternalCacheSettings settings) {
219 final MarshallingPair<V> wrappedMarshalling = metricsCollector.wrap(valueMarshalling, name);
220 final StableReadExternalCache<V> cache = obtainCache(
221 name, ExternalCacheDetails.BufferPolicy.READ_ONLY, settings, finalSettings ->
222 createStableReadExternalCache(
223 name, finalSettings, wrappedMarshalling, isValueSerializable(valueMarshalling)));
224 return metricsCollector.wrap(cache);
225 }
226
227 @Override
228 public <V> StableReadExternalCache<V> getStableReadExternalCache(
229 String name, @SuppressWarnings("deprecation") Marshaller<V> valueMarshaller, ExternalCacheSettings settings) {
230 final MarshallingPair<V> marshallingPair =
231 new MarshallingPair<>(valueMarshaller, valueMarshaller);
232 final MarshallingPair<V> wrappedMarshalling = metricsCollector.wrap(marshallingPair, name);
233
234 final StableReadExternalCache<V> cache = obtainCache(
235 name, ExternalCacheDetails.BufferPolicy.READ_ONLY, settings, finalSettings ->
236 createStableReadExternalCache(
237 name, finalSettings, wrappedMarshalling, isValueSerializable(valueMarshaller)));
238 return metricsCollector.wrap(cache);
239 }
240
241 @Override
242 public <V> DirectExternalCache<V> getDirectExternalCache(String name, MarshallingPair<V> valueMarshalling, ExternalCacheSettings settings) {
243 final MarshallingPair<V> wrappedMarshalling = metricsCollector.wrap(valueMarshalling, name);
244 final DirectExternalCache<V> cache = obtainCache(
245 name, ExternalCacheDetails.BufferPolicy.NEVER, settings, finalSettings ->
246 createDirectExternalCache(
247 name, finalSettings, wrappedMarshalling, isValueSerializable(valueMarshalling)));
248 return metricsCollector.wrap(cache);
249 }
250
251 @Override
252 public <V> DirectExternalCache<V> getDirectExternalCache(
253 String name, @SuppressWarnings("deprecation") Marshaller<V> valueMarshaller, ExternalCacheSettings settings) {
254 final MarshallingPair<V> marshallingPair =
255 new MarshallingPair<>(valueMarshaller, valueMarshaller);
256 final MarshallingPair<V> wrappedMarshalling = metricsCollector.wrap(marshallingPair, name);
257
258 final DirectExternalCache<V> cache = obtainCache(
259 name, ExternalCacheDetails.BufferPolicy.NEVER, settings, finalSettings ->
260 createDirectExternalCache(
261 name, finalSettings, wrappedMarshalling, isValueSerializable(valueMarshaller)));
262 return metricsCollector.wrap(cache);
263 }
264
265 @Override
266 public void transactionSync(RequestContext context) {
267 transactionControlManager.syncAll(context);
268 }
269
270 @Override
271 public Set<String> transactionDiscard(RequestContext context) {
272 return transactionControlManager.discardAll(context);
273 }
274
275 @Override
276 public RequestMetrics metrics(RequestContext context) {
277 return metricsCollector.obtainRequestMetrics(context);
278 }
279
280 @Override
281 public Map<String, JvmCacheDetails> allJvmCacheDetails() {
282 final Map<String, JvmCacheDetails> result = new HashMap<>();
283 result.putAll(jvmCacheDetailsMap);
284 return result;
285 }
286
287 @Override
288 public Map<String, RequestCacheDetails> allRequestCacheDetails() {
289 final Map<String, RequestCacheDetails> result = new HashMap<>();
290 result.putAll(requestCacheDetailsMap);
291 return result;
292 }
293
294 @Override
295 public Map<String, ExternalCacheDetails> allExternalCacheDetails() {
296 final Map<String, ExternalCacheDetails> result = new HashMap<>();
297 result.putAll(externalCacheDetailsMap);
298 return result;
299 }
300
301 @VisibleForTesting
302 static <V> boolean isValueSerializable(@SuppressWarnings("deprecation") Marshaller<V> valueMarshaller) {
303 return SERIALIZABLE_MARSHALLER_CLASS_NAMES.contains(valueMarshaller.getClass().getName());
304 }
305
306 @VisibleForTesting
307 static <V> boolean isValueSerializable(MarshallingPair<V> valueMarshalling) {
308 return SERIALIZABLE_MARSHALLING_CLASS_NAMES.contains(valueMarshalling.getMarshaller().getClass().getName())
309 && SERIALIZABLE_MARSHALLING_CLASS_NAMES.contains(valueMarshalling.getUnmarshaller().getClass().getName());
310 }
311
312 @SuppressWarnings("unchecked")
313 private <C extends ExternalCache> C obtainCache(
314 String name,
315 ExternalCacheDetails.BufferPolicy policy,
316 ExternalCacheSettings settings,
317 Function<ExternalCacheSettings, C> factory) {
318 final ExternalCache candidateCache = externalCacheInstancesMap.compute(
319 requireValidCacheName(name),
320 (key, existing) -> {
321
322 if ((existing != null) && (externalCacheDetailsMap.get(name).getPolicy() != policy)) {
323 log().warn("Cache {}: unable to create cache with policy {}, as one already configured with policy {}",
324 name, ExternalCacheDetails.BufferPolicy.READ_ONLY, externalCacheDetailsMap.get(name).getPolicy());
325 throw new ExternalCacheException(ExternalCacheException.Reason.CREATION_FAILURE);
326 }
327
328
329 log().trace("Cache {}: creating the instance with policy {}", name, policy);
330 final ExternalCacheSettings candidateSettings =
331 defaultsProvider.getExternalDefaults(name).override(settings);
332 final ExternalCacheSettings finalSettings = creationHandler.externalCacheCreation(
333 new DefaultExternalCacheDetails(name, policy, candidateSettings));
334 externalCacheDetailsMap.put(
335 name, new DefaultExternalCacheDetails(name, policy, finalSettings));
336 return factory.apply(finalSettings);
337 });
338
339 return (C) candidateCache;
340 }
341 }