View Javadoc

1   package com.atlassian.vcache.internal.memcached;
2   
3   import com.atlassian.marshalling.api.MarshallingPair;
4   import com.atlassian.vcache.CasIdentifier;
5   import com.atlassian.vcache.ExternalCacheException;
6   import com.atlassian.vcache.IdentifiedValue;
7   import com.atlassian.vcache.PutPolicy;
8   import com.atlassian.vcache.internal.core.DefaultIdentifiedValue;
9   import com.atlassian.vcache.internal.core.VCacheCoreUtils;
10  import net.spy.memcached.CASValue;
11  import net.spy.memcached.MemcachedClientIF;
12  import net.spy.memcached.OperationTimeoutException;
13  import org.slf4j.Logger;
14  import org.slf4j.LoggerFactory;
15  
16  import java.util.Map;
17  import java.util.Optional;
18  import java.util.Set;
19  import java.util.concurrent.ExecutionException;
20  import java.util.concurrent.Future;
21  import java.util.function.Supplier;
22  import java.util.stream.Collectors;
23  
24  import static com.atlassian.vcache.ExternalCacheException.Reason.UNCLASSIFIED_FAILURE;
25  import static com.atlassian.vcache.internal.core.VCacheCoreUtils.unmarshall;
26  
27  /**
28   * Common utility methods that are specific for the Memcached implementation.
29   *
30   * @since 1.0.0
31   */
32  class MemcachedUtils {
33      private static final Logger log = LoggerFactory.getLogger(MemcachedUtils.class);
34  
35      /**
36       * The maximum number of seconds offset for a time to live, before TTL must be expressed as offset since 1970.
37       */
38      private static final int MAX_SECONDS_OFFSET = 60 * 60 * 24 * 30;
39  
40      static long safeExtractId(CasIdentifier casId) {
41          if (casId instanceof MemcachedCasIdentifier) {
42              return ((MemcachedCasIdentifier) casId).getId();
43          }
44  
45          log.warn("Passed an unknown CasIdentifier instance of class {}.", casId.getClass().getName());
46          throw new ExternalCacheException(UNCLASSIFIED_FAILURE);
47      }
48  
49      static <V> Optional<IdentifiedValue<V>> identifiedValueFrom(Future<CASValue<Object>> op, MarshallingPair<V> valueMarshalling) {
50          final CASValue<Object> casValue;
51          try {
52              casValue = op.get();
53          } catch (ExecutionException | InterruptedException ex) {
54              throw new ExternalCacheException(UNCLASSIFIED_FAILURE, ex);
55          }
56  
57          if (casValue == null) {
58              return Optional.empty();
59          }
60  
61          final CasIdentifier identifier = new MemcachedCasIdentifier(casValue.getCas());
62          final IdentifiedValue<V> iv = new DefaultIdentifiedValue<>(
63                  identifier, VCacheCoreUtils.unmarshall((byte[]) casValue.getValue(), valueMarshalling).get());
64          return Optional.of(iv);
65      }
66  
67      static Future<Boolean> putOperationForPolicy(
68              PutPolicy policy, MemcachedClientIF client, String externalKey, int defaultTtl, byte[] valueBytes) {
69          final Future<Boolean> putOp;
70          if (policy == PutPolicy.ADD_ONLY) {
71              putOp = client.add(externalKey, defaultTtl, valueBytes);
72          } else if (policy == PutPolicy.PUT_ALWAYS) {
73              putOp = client.set(externalKey, defaultTtl, valueBytes);
74          } else if (policy == PutPolicy.REPLACE_ONLY) {
75              putOp = client.replace(externalKey, defaultTtl, valueBytes);
76          } else {
77              throw new IllegalArgumentException("Unknown put policy: " + policy);
78          }
79  
80          return putOp;
81      }
82  
83      static ExternalCacheException mapException(Exception ex) {
84          if (ex instanceof OperationTimeoutException) {
85              return new ExternalCacheException(ExternalCacheException.Reason.TIMEOUT, ex);
86          }
87          return new ExternalCacheException(ExternalCacheException.Reason.UNCLASSIFIED_FAILURE, ex);
88      }
89  
90      static <V> Map<String, Optional<V>> directGetBulk(
91              Set<String> externalKeys, Supplier<MemcachedClientIF> clientSupplier, MarshallingPair<V> valueMarshalling) {
92          // Returns map of keys that contain values, so need to handle the missing ones
93          final Map<String, Object> haveValues = clientSupplier.get().getBulk(externalKeys);
94  
95          return externalKeys.stream().collect(Collectors.toMap(
96                  k -> k,
97                  k -> unmarshall((byte[]) haveValues.get(k), valueMarshalling)));
98      }
99  
100     static long obtainCacheVersion(Supplier<MemcachedClientIF> clientSupplier, String externalCacheVersionKey) {
101         // Incrementing by 0, to get the current value
102         return clientSupplier.get().incr(externalCacheVersionKey, 0, 1);
103     }
104 
105     static long incrementCacheVersion(Supplier<MemcachedClientIF> clientSupplier, String externalCacheVersionKey) {
106         return clientSupplier.get().incr(externalCacheVersionKey, 1, 1);
107     }
108 
109     /**
110      * Calculates the expiry time for a Memcached entry, taking into account the
111      * logic required to handle when the seconds are longer than 30 days.
112      *
113      * @param seconds the period to expiry, in seconds
114      * @return a Memcached compliant expiry time
115      */
116     static int expiryTime(int seconds) {
117         if (seconds < MAX_SECONDS_OFFSET) {
118             return seconds;
119         }
120 
121         final int currentTime = (int) (System.currentTimeMillis() / 1_000);
122         return currentTime + seconds;
123     }
124 }