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
32
33
34
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
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
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 }