View Javadoc

1   package com.atlassian.vcache.internal.guava;
2   
3   import com.atlassian.marshalling.api.MarshallingPair;
4   import com.atlassian.vcache.ExternalCacheException;
5   import com.atlassian.vcache.PutPolicy;
6   import com.atlassian.vcache.internal.MetricLabel;
7   import com.atlassian.vcache.internal.RequestContext;
8   import com.atlassian.vcache.internal.core.ExternalCacheKeyGenerator;
9   import com.atlassian.vcache.internal.core.cas.IdentifiedData;
10  import com.atlassian.vcache.internal.core.metrics.CacheType;
11  import com.atlassian.vcache.internal.core.metrics.MetricsRecorder;
12  import com.atlassian.vcache.internal.core.service.AbstractExternalCacheRequestContext;
13  import com.atlassian.vcache.internal.core.service.AbstractStableReadExternalCache;
14  import com.atlassian.vcache.internal.core.service.UnversionedExternalCacheRequestContext;
15  import com.google.common.cache.Cache;
16  import org.slf4j.Logger;
17  import org.slf4j.LoggerFactory;
18  
19  import java.time.Duration;
20  import java.util.Map;
21  import java.util.Optional;
22  import java.util.Set;
23  import java.util.concurrent.ExecutionException;
24  import java.util.function.Supplier;
25  
26  import static com.atlassian.vcache.internal.core.cas.IdentifiedUtils.marshall;
27  import static com.atlassian.vcache.internal.core.cas.IdentifiedUtils.unmarshall;
28  import static java.util.Objects.requireNonNull;
29  
30  /**
31   * Guava implementation of {@link com.atlassian.vcache.StableReadExternalCache}.
32   *
33   * @param <V> the value type
34   * @since 1.0.0
35   */
36  public class GuavaStableReadExternalCache<V>
37          extends AbstractStableReadExternalCache<V> {
38      private static final Logger log = LoggerFactory.getLogger(GuavaStableReadExternalCache.class);
39  
40      private final Cache<String, IdentifiedData> delegate;
41      private final Supplier<RequestContext> contextSupplier;
42      private final ExternalCacheKeyGenerator keyGenerator;
43      private final Optional<MarshallingPair<V>> valueMarshalling;
44  
45      public GuavaStableReadExternalCache(
46              String name,
47              Cache<String, IdentifiedData> delegate,
48              Supplier<RequestContext> contextSupplier,
49              ExternalCacheKeyGenerator keyGenerator,
50              Optional<MarshallingPair<V>> valueMarshalling,
51              MetricsRecorder metricsRecorder,
52              Duration lockTimeout) {
53          super(name, metricsRecorder, lockTimeout, (n, ex) -> {});
54          this.contextSupplier = requireNonNull(contextSupplier);
55          this.keyGenerator = requireNonNull(keyGenerator);
56          this.valueMarshalling = requireNonNull(valueMarshalling);
57          this.delegate = requireNonNull(delegate);
58      }
59  
60      @Override
61      protected boolean internalPut(String internalKey, V value, PutPolicy policy) {
62          final IdentifiedData identifiedData = marshall(value, valueMarshalling);
63          final String externalKey = ensureCacheContext().externalEntryKeyFor(internalKey);
64          return GuavaUtils.directPut(externalKey, identifiedData, policy, delegate);
65      }
66  
67      @Override
68      protected void internalRemove(Iterable<String> internalKeys) {
69          // There is no bulk delete in the api, so need to remove each one
70          final AbstractExternalCacheRequestContext<V> cacheContext = ensureCacheContext();
71          for (String key : internalKeys) {
72              delegate.asMap().remove(cacheContext.externalEntryKeyFor(key));
73              cacheContext.recordValue(key, Optional.empty());
74          }
75      }
76  
77      @Override
78      protected void internalRemoveAll() {
79          delegate.asMap().clear();
80      }
81  
82      @Override
83      protected Logger getLogger() {
84          return log;
85      }
86  
87      @Override
88      protected AbstractExternalCacheRequestContext<V> ensureCacheContext() {
89          final RequestContext requestContext = contextSupplier.get();
90  
91          return requestContext.computeIfAbsent(this, () -> {
92              log.trace("Cache {}: Setting up a new context", getName());
93              return new UnversionedExternalCacheRequestContext<>(
94                      keyGenerator,
95                      getName(),
96                      requestContext::partitionIdentifier,
97                      lockTimeout);
98          });
99      }
100 
101     @Override
102     protected V handleCreation(String internalKey, V candidateValue)
103             throws ExecutionException, InterruptedException {
104         final AbstractExternalCacheRequestContext<V> cacheContext = ensureCacheContext();
105         final IdentifiedData candidateIdentifiedData = marshall(candidateValue, valueMarshalling);
106 
107         final String externalKey = cacheContext.externalEntryKeyFor(internalKey);
108         metricsRecorder.record(name, CacheType.EXTERNAL, MetricLabel.NUMBER_OF_REMOTE_GET, 1);
109         final Optional<V> otherAddedValue =
110                 unmarshall(delegate.asMap().putIfAbsent(externalKey, candidateIdentifiedData), valueMarshalling);
111 
112         if (otherAddedValue.isPresent()) {
113             getLogger().info("Cache {}, unable to add candidate for key {}, use what was added", name, internalKey);
114             // Record as if doing another remote call
115             metricsRecorder.record(name, CacheType.EXTERNAL, MetricLabel.NUMBER_OF_REMOTE_GET, 1);
116             return otherAddedValue.get();
117         }
118 
119         return candidateValue;
120     }
121 
122     @Override
123     protected final ExternalCacheException mapException(Exception ex) {
124         return GuavaUtils.mapException(ex);
125     }
126 
127     @Override
128     protected final Optional<V> directGet(String externalKey) {
129         return unmarshall(delegate.getIfPresent(externalKey), valueMarshalling);
130     }
131 
132     @Override
133     protected final Map<String, Optional<V>> directGetBulk(Set<String> externalKeys) {
134         return GuavaUtils.directGetBulk(externalKeys, delegate, valueMarshalling);
135     }
136 }