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                  () -> memClient,
79                  () -> requestContext,
80                  new PlainExternalCacheKeyGenerator("prodid"),
81                  "mocked",
82                  StringMarshalling.pair(),
83                  settings,
84                  new DefaultTransactionControlManager(metricsCollector, begunTransactionalActivityHandler),
85                  metricsCollector,
86                  Duration.ofSeconds(1));
87      }
88  
89      @Test
90      public void get_missing() throws Exception {
91          when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
92          when(memClient.get(eq("prodid::tenant-id::mocked::3::missing"))).thenReturn(null);
93  
94          final CompletionStage<Optional<String>> f1 = cache.get("missing");
95  
96          assertThat(f1, successfulWith(is(Optional.empty())));
97  
98          verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
99          verify(memClient).get(eq("prodid::tenant-id::mocked::3::missing"));
100         verifyNoMoreInteractions(memClient);
101         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
102     }
103 
104     @Test
105     public void removeAll_get() {
106         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
107 
108         cache.removeAll();
109 
110         verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
111         verifyNoMoreInteractions(memClient);
112 
113         final CompletionStage<Optional<String>> f1 = cache.get("missing");
114 
115         assertThat(f1, successfulWith(is(Optional.empty())));
116 
117         verifyNoMoreInteractions(memClient);
118         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
119     }
120 
121     @Test
122     public void removeAll_put_get() {
123         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
124 
125         cache.removeAll();
126 
127         verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
128         verifyNoMoreInteractions(memClient);
129 
130         cache.put("mary", "lamb", PutPolicy.PUT_ALWAYS);
131 
132         final CompletionStage<Optional<String>> f1 = cache.get("mary");
133         assertThat(f1, successfulWith(is(Optional.of("lamb"))));
134 
135         verifyNoMoreInteractions(memClient);
136 
137         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
138     }
139 
140     @Test
141     public void removeAll_getBulk() throws Exception {
142         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
143 
144         cache.removeAll();
145 
146         verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
147         verifyNoMoreInteractions(memClient);
148 
149         final CompletionStage<Map<String, Optional<String>>> get1 = cache.getBulk("key-1", "key-2");
150 
151         assertThat(get1, successful());
152         assertThat(unsafeJoin(get1).keySet(), containsInAnyOrder("key-1", "key-2"));
153         //noinspection unchecked
154         assertThat(unsafeJoin(get1).values(), containsInAnyOrder(Optional.empty(), Optional.empty()));
155 
156         verifyNoMoreInteractions(memClient);
157         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
158     }
159 
160     @Test
161     public void removeAll_getBulkFactory() throws Exception {
162         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
163 
164         cache.removeAll();
165 
166         verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
167         verifyNoMoreInteractions(memClient);
168 
169         final CompletionStage<Map<String, String>> get1 = cache.getBulk(
170                 keys -> Maps.asMap(keys, k -> k + "-1"), "key-1", "key-2");
171 
172         assertThat(get1, successful());
173         assertThat(unsafeJoin(get1).keySet(), containsInAnyOrder("key-1", "key-2"));
174         assertThat(unsafeJoin(get1).values(), containsInAnyOrder("key-1-1", "key-2-1"));
175 
176         verifyNoMoreInteractions(memClient);
177         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
178     }
179 
180     @Test
181     public void remove_get() {
182         cache.remove("missing");
183 
184         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
185 
186         final CompletionStage<Optional<String>> f1 = cache.get("missing");
187 
188         assertThat(f1, successfulWith(is(Optional.empty())));
189 
190         verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
191         verifyNoMoreInteractions(memClient);
192         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
193     }
194 
195     @Test
196     public void put_get() {
197         cache.put("lockwood", "brand", PutPolicy.PUT_ALWAYS);
198 
199         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
200 
201         final CompletionStage<Optional<String>> f1 = cache.get("lockwood");
202 
203         assertThat(f1, successfulWith(is(Optional.of("brand"))));
204 
205         verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
206         verifyNoMoreInteractions(memClient);
207         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
208     }
209 
210     @Test
211     public void put_removeAll_getSupplier() {
212         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
213 
214         cache.put("six", "pack", PutPolicy.ADD_ONLY);
215 
216         verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
217         verifyNoMoreInteractions(memClient);
218 
219         final CompletionStage<Optional<String>> get1 = cache.get("six");
220 
221         assertThat(get1, successfulWith(is(Optional.of("pack"))));
222 
223         verifyNoMoreInteractions(memClient);
224 
225         cache.removeAll();
226 
227         verifyNoMoreInteractions(memClient);
228 
229         final CompletionStage<String> get2 = cache.get("six", () -> "times");
230 
231         assertThat(get2, successfulWith(is("times")));
232         verifyNoMoreInteractions(memClient);
233     }
234 
235     @Test
236     public void removeAll_put_getSupplier() {
237         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
238 
239         cache.put("six", "pack", PutPolicy.ADD_ONLY);
240 
241         verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
242         verifyNoMoreInteractions(memClient);
243 
244         final CompletionStage<Optional<String>> get1 = cache.get("six");
245 
246         assertThat(get1, successfulWith(is(Optional.of("pack"))));
247 
248         verifyNoMoreInteractions(memClient);
249 
250         cache.removeAll();
251 
252         verifyNoMoreInteractions(memClient);
253 
254         cache.put("mary", "lamb", PutPolicy.PUT_ALWAYS);
255 
256         final CompletionStage<String> get2 = cache.get("six", () -> "times");
257 
258         assertThat(get2, successfulWith(is("times")));
259         verifyNoMoreInteractions(memClient);
260     }
261 
262 
263     @Test
264     public void put_sync_sync() throws Exception {
265         when(memClient.incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L))).thenReturn(3L);
266 
267         cache.put("aws", "monopoly", PutPolicy.ADD_ONLY);
268 
269         verify(memClient).incr(eq("prodid::tenant-id::mocked::cache-version"), eq(0), eq(1L));
270         verifyNoMoreInteractions(memClient);
271 
272         when(booleanFuture.get()).thenReturn(true);
273         when(memClient.add(
274                 eq("prodid::tenant-id::mocked::3::aws"), eq(DEFAULT_TTL),
275                 eq(new StringMarshalling().marshallToBytes("monopoly"))))
276                 .thenReturn(booleanFuture);
277 
278         cache.transactionSync();
279 
280         verify(memClient).add(
281                 eq("prodid::tenant-id::mocked::3::aws"), eq(DEFAULT_TTL),
282                 eq(new StringMarshalling().marshallToBytes("monopoly")));
283         verifyNoMoreInteractions(memClient);
284 
285         cache.transactionSync();
286         verifyNoMoreInteractions(memClient);
287         verify(begunTransactionalActivityHandler, times(1)).onRequest(requestContext);
288     }
289 }