View Javadoc

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   * Base implementation of a service.
53   *
54   * @since 1.0.0
55   */
56  public abstract class AbstractVCacheService implements VCacheFactory, VCacheManagement, VCacheLifecycleManager {
57      // List of Marshaller class names that support Serializable values. This is a slightly
58      // dodgy approach, but the rationale is to not be forced to make implementations
59      // visible, or to make it an attribute of a Marshaller. The fact is, this is a temporary
60      // hack for internal use only.
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      * Returns the logger for the implementation service.
109      *
110      * @return the logger for the implementation service.
111      */
112     protected abstract Logger log();
113 
114     /**
115      * Creates a {@link JvmCache} with the supplied parameters.
116      *
117      * @param name     the name of the cache
118      * @param settings the setting for the cache
119      * @param <K>      the key type
120      * @param <V>      the value type
121      * @return a freshly minted {@link JvmCache}
122      */
123     protected abstract <K, V> JvmCache<K, V> createJvmCache(String name, JvmCacheSettings settings);
124 
125     /**
126      * Creates a {@link TransactionalExternalCache} with the supplied parameters.
127      *
128      * @param name              the name of the cache
129      * @param settings          the settings for the cache
130      * @param valueMarshalling  the marshalling pair for values
131      * @param valueSerializable whether the values are {@link java.io.Serializable}
132      * @param <V>               the value type
133      * @return a freshly minted {@link TransactionalExternalCache}
134      */
135     protected abstract <V> TransactionalExternalCache<V> createTransactionalExternalCache(
136             String name, ExternalCacheSettings settings, MarshallingPair<V> valueMarshalling, boolean valueSerializable);
137 
138     /**
139      * Creates a {@link StableReadExternalCache} with the supplied parameters.
140      *
141      * @param name              the name of the cache
142      * @param settings          the settings for the cache
143      * @param valueMarshalling  the marshalling pair for values
144      * @param valueSerializable whether the values are {@link java.io.Serializable}
145      * @param <V>               the value type
146      * @return a freshly minted {@link StableReadExternalCache}
147      */
148     protected abstract <V> StableReadExternalCache<V> createStableReadExternalCache(
149             String name, ExternalCacheSettings settings, MarshallingPair<V> valueMarshalling, boolean valueSerializable);
150 
151     /**
152      * Creates a {@link DirectExternalCache} with the supplied parameters.
153      *
154      * @param name              the name of the cache
155      * @param settings          the settings for the cache
156      * @param valueMarshalling  the marshalling pair for values
157      * @param valueSerializable whether the values are {@link java.io.Serializable}
158      * @param <V>               the value type
159      * @return a freshly minted {@link DirectExternalCache}
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                     // Check if attempting to change the policy, a no no
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                     // Now always create a new cache wrapper, regardless of whether one existed before.
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 }