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.internal.RequestContext;
6   import com.atlassian.vcache.internal.core.ExternalCacheKeyGenerator;
7   import com.atlassian.vcache.internal.core.TransactionControlManager;
8   import com.atlassian.vcache.internal.core.cas.IdentifiedData;
9   import com.atlassian.vcache.internal.core.metrics.MetricsRecorder;
10  import com.atlassian.vcache.internal.core.service.AbstractExternalCacheRequestContext;
11  import com.atlassian.vcache.internal.core.service.AbstractTransactionalExternalCache;
12  import com.atlassian.vcache.internal.core.service.UnversionedExternalCacheRequestContext;
13  import com.google.common.cache.Cache;
14  import org.slf4j.Logger;
15  import org.slf4j.LoggerFactory;
16  
17  import java.util.Map;
18  import java.util.Optional;
19  import java.util.Set;
20  import java.util.function.Supplier;
21  
22  import static com.atlassian.vcache.internal.core.cas.IdentifiedUtils.marshall;
23  import static com.atlassian.vcache.internal.core.cas.IdentifiedUtils.unmarshall;
24  import static java.util.Objects.requireNonNull;
25  
26  /**
27   * Guava implementation of a {@link com.atlassian.vcache.TransactionalExternalCache}.
28   *
29   * @param <V> the value type.
30   * @since 1.0.0
31   */
32  public class GuavaTransactionalExternalCache<V>
33          extends AbstractTransactionalExternalCache<V> {
34      private static final Logger log = LoggerFactory.getLogger(GuavaTransactionalExternalCache.class);
35  
36      private final Cache<String, IdentifiedData> delegate;
37      private final ExternalCacheKeyGenerator keyGenerator;
38      private final Optional<MarshallingPair<V>> valueMarshalling;
39      private final TransactionControlManager transactionControlManager;
40  
41      public GuavaTransactionalExternalCache(
42              String name,
43              Cache<String, IdentifiedData> delegate,
44              Supplier<RequestContext> contextSupplier,
45              ExternalCacheKeyGenerator keyGenerator,
46              Optional<MarshallingPair<V>> valueMarshalling,
47              TransactionControlManager transactionControlManager,
48              MetricsRecorder metricsRecorder) {
49          super(name, contextSupplier, metricsRecorder);
50          this.delegate = requireNonNull(delegate);
51          this.keyGenerator = requireNonNull(keyGenerator);
52          this.valueMarshalling = requireNonNull(valueMarshalling);
53          this.transactionControlManager = requireNonNull(transactionControlManager);
54      }
55  
56      @Override
57      public void transactionSync() {
58          log.trace("Cache {}: synchronising operations", name);
59          final AbstractExternalCacheRequestContext<V> cacheContext = ensureCacheContext();
60  
61          if (cacheContext.hasRemoveAll()) {
62              delegate.asMap().clear();
63          }
64  
65          performKeyedOperations(cacheContext);
66          cacheContext.forgetAll();
67      }
68  
69      @Override
70      protected AbstractExternalCacheRequestContext<V> ensureCacheContext() {
71          final RequestContext requestContext = contextSupplier.get();
72  
73          transactionControlManager.registerTransactionalExternalCache(requestContext, name, this);
74  
75          return requestContext.computeIfAbsent(this, () -> {
76              // Need to build a new context, which involves getting the current cache version, or setting it if it does
77              // not exist.
78              log.trace("Cache {}: Setting up a new context", name);
79              return new UnversionedExternalCacheRequestContext<>(
80                      keyGenerator, getName(), requestContext::partitionIdentifier);
81          });
82      }
83  
84      @Override
85      protected Logger getLogger() {
86          return log;
87      }
88  
89      @Override
90      protected final ExternalCacheException mapException(Exception ex) {
91          return GuavaUtils.mapException(ex);
92      }
93  
94      @Override
95      protected final Optional<V> directGet(String externalKey) {
96          return unmarshall(delegate.getIfPresent(externalKey), valueMarshalling);
97      }
98  
99      @Override
100     protected final Map<String, Optional<V>> directGetBulk(Set<String> externalKeys) {
101         return GuavaUtils.directGetBulk(externalKeys, delegate, valueMarshalling);
102     }
103 
104     private void performKeyedOperations(AbstractExternalCacheRequestContext<V> cacheContext) {
105         try {
106             for (Map.Entry<String, AbstractExternalCacheRequestContext.DeferredOperation<V>> entry
107                     : cacheContext.getKeyedOperations()) {
108                 final String externalKey = cacheContext.externalEntryKeyFor(entry.getKey());
109 
110                 if (entry.getValue().isRemove()) {
111                     log.trace("Cache {}: performing remove on entry {}", name, entry.getKey());
112                     delegate.asMap().remove(externalKey);
113                 } else {
114                     log.trace("Cache {}: performing {} on entry {}", name, entry.getValue().getPolicy(), entry.getKey());
115                     final IdentifiedData identifiedData = marshall(entry.getValue().getValue(), valueMarshalling);
116 
117                     final boolean putOutcome =
118                             GuavaUtils.directPut(
119                                     externalKey,
120                                     identifiedData,
121                                     entry.getValue().getPolicy(),
122                                     delegate);
123 
124                     if (!putOutcome) {
125                         log.debug("Cache {}: Unable to perform put() operation {} on entry {}, clearing cache",
126                                 name, entry.getValue().getPolicy(), entry.getKey());
127                         delegate.asMap().clear();
128                         break;
129                     }
130                 }
131             }
132         } catch (ExternalCacheException bugger) {
133             log.error("Cache {}: an operation failed in transaction sync, so clearing the cache", name);
134             delegate.asMap().clear();
135         }
136     }
137 
138 }