View Javadoc

1   package com.atlassian.vcache.internal.memcached;
2   
3   import com.atlassian.marshalling.jdk.StringMarshalling;
4   import com.atlassian.vcache.ChangeRate;
5   import com.atlassian.vcache.ExternalCacheSettings;
6   import com.atlassian.vcache.ExternalCacheSettingsBuilder;
7   import com.atlassian.vcache.PutPolicy;
8   import com.atlassian.vcache.internal.BegunTransactionalActivityHandler;
9   import com.atlassian.vcache.internal.RequestContext;
10  import com.atlassian.vcache.internal.core.DefaultRequestContext;
11  import com.atlassian.vcache.internal.core.DefaultTransactionControlManager;
12  import com.atlassian.vcache.internal.core.PlainExternalCacheKeyGenerator;
13  import com.atlassian.vcache.internal.core.metrics.DefaultMetricsCollector;
14  import com.atlassian.vcache.internal.core.metrics.MetricsCollector;
15  import com.atlassian.vcache.internal.test.LoggingTestWatcher;
16  import com.google.common.collect.Maps;
17  import net.spy.memcached.MemcachedClientIF;
18  import org.junit.Before;
19  import org.junit.Rule;
20  import org.junit.Test;
21  import org.junit.runner.RunWith;
22  import org.mockito.Mock;
23  import org.mockito.runners.MockitoJUnitRunner;
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  
27  import java.time.Duration;
28  import java.util.Map;
29  import java.util.Optional;
30  import java.util.concurrent.CompletionStage;
31  import java.util.concurrent.Future;
32  
33  import static com.atlassian.vcache.VCacheUtils.unsafeJoin;
34  import static com.atlassian.vcache.internal.test.CompletionStageSuccessful.successful;
35  import static com.atlassian.vcache.internal.test.CompletionStageSuccessful.successfulWith;
36  import static org.hamcrest.MatcherAssert.assertThat;
37  import static org.hamcrest.Matchers.containsInAnyOrder;
38  import static org.hamcrest.Matchers.is;
39  import static org.mockito.Matchers.eq;
40  import static org.mockito.Mockito.times;
41  import static org.mockito.Mockito.verify;
42  import static org.mockito.Mockito.verifyNoMoreInteractions;
43  import static org.mockito.Mockito.when;
44  
45  @RunWith(MockitoJUnitRunner.class)
46  public class MemcachedTransactionalExternalCacheTest {
47      private static final Logger log = LoggerFactory.getLogger(MemcachedTransactionalExternalCacheTest.class);
48  
49      private static final int DEFAULT_TTL = 11;
50  
51      @Rule
52      public LoggingTestWatcher watcher = new LoggingTestWatcher(log);
53  
54      @Mock
55      private MemcachedClientIF memClient;
56  
57      @Mock
58      private BegunTransactionalActivityHandler begunTransactionalActivityHandler;
59  
60      private RequestContext requestContext = new DefaultRequestContext(() -> "tenant-id");
61      private MetricsCollector metricsCollector = new DefaultMetricsCollector(() -> requestContext);
62  
63      @Mock
64      private Future<Boolean> booleanFuture;
65  
66      private MemcachedTransactionalExternalCache<String> cache;
67  
68      @Before
69      public void init() {
70          final ExternalCacheSettings settings = new ExternalCacheSettingsBuilder()
71                  .entryGrowthRateHint(ChangeRate.LOW_CHANGE)
72                  .entryCountHint(5)
73                  .defaultTtl(Duration.ofSeconds(DEFAULT_TTL))
74                  .dataChangeRateHint(ChangeRate.HIGH_CHANGE)
75                  .build();
76  
77          cache = new MemcachedTransactionalExternalCache<>(
78                  Utils.defaultServiceSettingsBuilder(() -> memClient, Duration.ofSeconds(1)).build(),
79                  () -> requestContext,
80                  new PlainExternalCacheKeyGenerator("prodid"),
81                  "mocked",
82                  StringMarshalling.pair(),
83                  settings,
84                  new DefaultTransactionControlManager(metricsCollector, begunTransactionalActivityHandler),
85                  metricsCollector);
86      }
87  
88      @Test
89      public void get_missing() throws Exception {
90          when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
91          when(memClient.get(eq("prodid::tenant-id::mocked::3::missing"))).thenReturn(null);
92  
93          final CompletionStage<Optional<String>> f1 = cache.get("missing");
94  
95          assertThat(f1, successfulWith(is(Optional.empty())));
96  
97          verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
98          verify(memClient).get(eq("prodid::tenant-id::mocked::3::missing"));
99          verifyNoMoreInteractions(memClient);
100         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
101     }
102 
103     @Test
104     public void removeAll_get() {
105         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
106 
107         cache.removeAll();
108 
109         verifyNoMoreInteractions(memClient);
110 
111         final CompletionStage<Optional<String>> f1 = cache.get("missing");
112 
113         assertThat(f1, successfulWith(is(Optional.empty())));
114 
115         verifyNoMoreInteractions(memClient);
116         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
117     }
118 
119     @Test
120     public void removeAll_put_get() {
121         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
122 
123         cache.removeAll();
124 
125         verifyNoMoreInteractions(memClient);
126 
127         cache.put("mary", "lamb", PutPolicy.PUT_ALWAYS);
128 
129         final CompletionStage<Optional<String>> f1 = cache.get("mary");
130         assertThat(f1, successfulWith(is(Optional.of("lamb"))));
131 
132         verifyNoMoreInteractions(memClient);
133 
134         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
135     }
136 
137     @Test
138     public void removeAll_getBulk() throws Exception {
139         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
140 
141         cache.removeAll();
142 
143         verifyNoMoreInteractions(memClient);
144 
145         final CompletionStage<Map<String, Optional<String>>> get1 = cache.getBulk("key-1", "key-2");
146 
147         assertThat(get1, successful());
148         assertThat(unsafeJoin(get1).keySet(), containsInAnyOrder("key-1", "key-2"));
149         //noinspection unchecked
150         assertThat(unsafeJoin(get1).values(), containsInAnyOrder(Optional.empty(), Optional.empty()));
151 
152         verifyNoMoreInteractions(memClient);
153         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
154     }
155 
156     @Test
157     public void removeAll_getBulkFactory() throws Exception {
158         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
159 
160         cache.removeAll();
161 
162         verifyNoMoreInteractions(memClient);
163 
164         final CompletionStage<Map<String, String>> get1 = cache.getBulk(
165                 keys -> Maps.asMap(keys, k -> k + "-1"), "key-1", "key-2");
166 
167         assertThat(get1, successful());
168         assertThat(unsafeJoin(get1).keySet(), containsInAnyOrder("key-1", "key-2"));
169         assertThat(unsafeJoin(get1).values(), containsInAnyOrder("key-1-1", "key-2-1"));
170 
171         verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
172         verifyNoMoreInteractions(memClient);
173         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
174     }
175 
176     @Test
177     public void remove_get() {
178         cache.remove("missing");
179 
180         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
181 
182         final CompletionStage<Optional<String>> f1 = cache.get("missing");
183 
184         assertThat(f1, successfulWith(is(Optional.empty())));
185 
186         verifyNoMoreInteractions(memClient);
187         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
188     }
189 
190     @Test
191     public void put_get() {
192         cache.put("lockwood", "brand", PutPolicy.PUT_ALWAYS);
193 
194         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
195 
196         final CompletionStage<Optional<String>> f1 = cache.get("lockwood");
197 
198         assertThat(f1, successfulWith(is(Optional.of("brand"))));
199 
200         verifyNoMoreInteractions(memClient);
201         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
202     }
203 
204     @Test
205     public void put_removeAll_getSupplier() {
206         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
207 
208         cache.put("six", "pack", PutPolicy.ADD_ONLY);
209 
210         verifyNoMoreInteractions(memClient);
211 
212         final CompletionStage<Optional<String>> get1 = cache.get("six");
213 
214         assertThat(get1, successfulWith(is(Optional.of("pack"))));
215 
216         verifyNoMoreInteractions(memClient);
217 
218         cache.removeAll();
219 
220         verifyNoMoreInteractions(memClient);
221 
222         final CompletionStage<String> get2 = cache.get("six", () -> "times");
223 
224         assertThat(get2, successfulWith(is("times")));
225         verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
226         verifyNoMoreInteractions(memClient);
227     }
228 
229     @Test
230     public void removeAll_put_getSupplier() {
231         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
232 
233         cache.put("six", "pack", PutPolicy.ADD_ONLY);
234 
235         verifyNoMoreInteractions(memClient);
236 
237         final CompletionStage<Optional<String>> get1 = cache.get("six");
238 
239         assertThat(get1, successfulWith(is(Optional.of("pack"))));
240 
241         verifyNoMoreInteractions(memClient);
242 
243         cache.removeAll();
244 
245         verifyNoMoreInteractions(memClient);
246 
247         cache.put("mary", "lamb", PutPolicy.PUT_ALWAYS);
248 
249         final CompletionStage<String> get2 = cache.get("six", () -> "times");
250 
251         assertThat(get2, successfulWith(is("times")));
252         verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
253         verifyNoMoreInteractions(memClient);
254     }
255 
256 
257     @Test
258     public void put_sync_sync() throws Exception {
259         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
260 
261         cache.put("aws", "monopoly", PutPolicy.ADD_ONLY);
262 
263         verifyNoMoreInteractions(memClient);
264 
265         when(booleanFuture.get()).thenReturn(true);
266         when(memClient.add(
267                 eq("prodid::tenant-id::mocked::3::aws"), eq(DEFAULT_TTL),
268                 eq(new StringMarshalling().marshallToBytes("monopoly"))))
269                 .thenReturn(booleanFuture);
270 
271         cache.transactionSync();
272 
273         verify(memClient).add(
274                 eq("prodid::tenant-id::mocked::3::aws"), eq(DEFAULT_TTL),
275                 eq(new StringMarshalling().marshallToBytes("monopoly")));
276         verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
277         verifyNoMoreInteractions(memClient);
278 
279         cache.transactionSync();
280         verifyNoMoreInteractions(memClient);
281         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
282     }
283 }