blob: 23f9a1f160ecfeaadc3539f66ed7b54dc7cb6fb1 [file] [log] [blame]
Madan Jampani5e5b3d62016-02-01 16:03:33 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Madan Jampani5e5b3d62016-02-01 16:03:33 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.store.primitives.resources.impl;
17
Aaron Kruglikovc0c27c02016-06-07 16:05:00 -070018import com.google.common.collect.Sets;
Jordan Halterman2bf177c2017-06-29 01:49:08 -070019import io.atomix.protocols.raft.proxy.RaftProxy;
20import io.atomix.protocols.raft.service.RaftService;
Madan Jampani5e5b3d62016-02-01 16:03:33 -080021import org.junit.Test;
22import org.onlab.util.Tools;
Madan Jampani74da78b2016-02-09 21:18:36 -080023import org.onosproject.store.primitives.MapUpdate;
Madan Jampanicadd70b2016-02-08 13:45:43 -080024import org.onosproject.store.primitives.TransactionId;
Madan Jampani5e5b3d62016-02-01 16:03:33 -080025import org.onosproject.store.service.MapEvent;
26import org.onosproject.store.service.MapEventListener;
Jordan Halterman948d6592017-04-20 17:18:24 -070027import org.onosproject.store.service.TransactionLog;
Jordan Halterman5f97a302017-04-26 23:41:31 -070028import org.onosproject.store.service.Version;
Madan Jampani5e5b3d62016-02-01 16:03:33 -080029import org.onosproject.store.service.Versioned;
30
Ray Milkey6a51cb92018-03-06 09:03:03 -080031import java.util.Arrays;
32import java.util.ConcurrentModificationException;
33import java.util.List;
34import java.util.concurrent.ArrayBlockingQueue;
35import java.util.concurrent.BlockingQueue;
36import java.util.concurrent.CompletionException;
37import java.util.stream.Collectors;
38
Aaron Kruglikovc0c27c02016-06-07 16:05:00 -070039import static org.hamcrest.Matchers.is;
40import static org.junit.Assert.assertArrayEquals;
41import static org.junit.Assert.assertEquals;
42import static org.junit.Assert.assertFalse;
43import static org.junit.Assert.assertNotNull;
44import static org.junit.Assert.assertNull;
45import static org.junit.Assert.assertThat;
46import static org.junit.Assert.assertTrue;
47import static org.junit.Assert.fail;
Madan Jampani5e5b3d62016-02-01 16:03:33 -080048
49/**
50 * Unit tests for {@link AtomixConsistentMap}.
51 */
Jordan Halterman2bf177c2017-06-29 01:49:08 -070052public class AtomixConsistentMapTest extends AtomixTestBase<AtomixConsistentMap> {
Madan Jampani5e5b3d62016-02-01 16:03:33 -080053
54 @Override
Jordan Halterman2bf177c2017-06-29 01:49:08 -070055 protected RaftService createService() {
56 return new AtomixConsistentMapService();
57 }
58
59 @Override
60 protected AtomixConsistentMap createPrimitive(RaftProxy proxy) {
61 return new AtomixConsistentMap(proxy);
Madan Jampani5e5b3d62016-02-01 16:03:33 -080062 }
63
64 /**
65 * Tests various basic map operations.
66 */
67 @Test
68 public void testBasicMapOperations() throws Throwable {
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -070069 basicMapOperationTests();
Madan Jampani5e5b3d62016-02-01 16:03:33 -080070 }
71
72 /**
73 * Tests various map compute* operations on different cluster sizes.
74 */
75 @Test
76 public void testMapComputeOperations() throws Throwable {
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -070077 mapComputeOperationTests();
Madan Jampani5e5b3d62016-02-01 16:03:33 -080078 }
79
80 /**
Jordan Halterman4922a062017-07-31 15:55:36 -070081 * Tests null values.
82 */
83 @Test
84 public void testNullValues() throws Throwable {
85 final byte[] rawFooValue = Tools.getBytesUtf8("Hello foo!");
86 final byte[] rawBarValue = Tools.getBytesUtf8("Hello bar!");
87
88 AtomixConsistentMap map = newPrimitive("testNullValues");
89
90 map.get("foo")
91 .thenAccept(v -> assertNull(v)).join();
92 map.put("foo", null)
93 .thenAccept(v -> assertNull(v)).join();
94 map.put("foo", rawFooValue).thenAccept(v -> {
95 assertNotNull(v);
96 assertNull(v.value());
97 }).join();
98 map.get("foo").thenAccept(v -> {
99 assertNotNull(v);
100 assertTrue(Arrays.equals(v.value(), rawFooValue));
101 }).join();
102 map.replace("foo", rawFooValue, null)
103 .thenAccept(replaced -> assertTrue(replaced)).join();
104 map.get("foo").thenAccept(v -> {
105 assertNotNull(v);
106 assertNull(v.value());
107 }).join();
108 map.replace("foo", rawFooValue, rawBarValue)
109 .thenAccept(replaced -> assertFalse(replaced)).join();
110 map.replace("foo", null, rawBarValue)
111 .thenAccept(replaced -> assertTrue(replaced)).join();
112 map.get("foo").thenAccept(v -> {
113 assertNotNull(v);
114 assertTrue(Arrays.equals(v.value(), rawBarValue));
115 }).join();
116 }
117
118 /**
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800119 * Tests map event notifications.
120 */
121 @Test
122 public void testMapListeners() throws Throwable {
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700123 mapListenerTests();
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800124 }
125
126 /**
Jordan Halterman5f97a302017-04-26 23:41:31 -0700127 * Tests map transaction prepare.
128 */
129 @Test
130 public void testTransactionPrepare() throws Throwable {
131 transactionPrepareTests();
132 }
133
134 /**
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800135 * Tests map transaction commit.
136 */
137 @Test
138 public void testTransactionCommit() throws Throwable {
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700139 transactionCommitTests();
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800140 }
141
142 /**
143 * Tests map transaction rollback.
144 */
145 @Test
146 public void testTransactionRollback() throws Throwable {
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700147 transactionRollbackTests();
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800148 }
149
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700150 protected void basicMapOperationTests() throws Throwable {
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800151 final byte[] rawFooValue = Tools.getBytesUtf8("Hello foo!");
152 final byte[] rawBarValue = Tools.getBytesUtf8("Hello bar!");
153
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700154 AtomixConsistentMap map = newPrimitive("testBasicMapOperationMap");
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800155
156 map.isEmpty().thenAccept(result -> {
157 assertTrue(result);
158 }).join();
159
160 map.put("foo", rawFooValue).thenAccept(result -> {
161 assertNull(result);
162 }).join();
163
164 map.size().thenAccept(result -> {
165 assertTrue(result == 1);
166 }).join();
167
168 map.isEmpty().thenAccept(result -> {
169 assertFalse(result);
170 }).join();
171
172 map.putIfAbsent("foo", "Hello foo again!".getBytes()).thenAccept(result -> {
173 assertNotNull(result);
174 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), rawFooValue));
175 }).join();
176
177 map.putIfAbsent("bar", rawBarValue).thenAccept(result -> {
178 assertNull(result);
179 }).join();
180
181 map.size().thenAccept(result -> {
182 assertTrue(result == 2);
183 }).join();
184
185 map.keySet().thenAccept(result -> {
186 assertTrue(result.size() == 2);
187 assertTrue(result.containsAll(Sets.newHashSet("foo", "bar")));
188 }).join();
189
190 map.values().thenAccept(result -> {
191 assertTrue(result.size() == 2);
192 List<String> rawValues =
193 result.stream().map(v -> Tools.toStringUtf8(v.value())).collect(Collectors.toList());
194 assertTrue(rawValues.contains("Hello foo!"));
195 assertTrue(rawValues.contains("Hello bar!"));
196 }).join();
197
198 map.entrySet().thenAccept(result -> {
199 assertTrue(result.size() == 2);
200 // TODO: check entries
201 }).join();
202
203 map.get("foo").thenAccept(result -> {
204 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), rawFooValue));
205 }).join();
206
207 map.remove("foo").thenAccept(result -> {
208 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), rawFooValue));
209 }).join();
210
211 map.containsKey("foo").thenAccept(result -> {
212 assertFalse(result);
213 }).join();
214
215 map.get("foo").thenAccept(result -> {
216 assertNull(result);
217 }).join();
218
219 map.get("bar").thenAccept(result -> {
220 assertNotNull(result);
221 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), rawBarValue));
222 }).join();
223
224 map.containsKey("bar").thenAccept(result -> {
225 assertTrue(result);
226 }).join();
227
228 map.size().thenAccept(result -> {
229 assertTrue(result == 1);
230 }).join();
231
232 map.containsValue(rawBarValue).thenAccept(result -> {
233 assertTrue(result);
234 }).join();
235
236 map.containsValue(rawFooValue).thenAccept(result -> {
237 assertFalse(result);
238 }).join();
239
240 map.replace("bar", "Goodbye bar!".getBytes()).thenAccept(result -> {
241 assertNotNull(result);
242 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), rawBarValue));
243 }).join();
244
245 map.replace("foo", "Goodbye foo!".getBytes()).thenAccept(result -> {
246 assertNull(result);
247 }).join();
248
249 // try replace_if_value_match for a non-existent key
250 map.replace("foo", "Goodbye foo!".getBytes(), rawFooValue).thenAccept(result -> {
251 assertFalse(result);
252 }).join();
253
254 map.replace("bar", "Goodbye bar!".getBytes(), rawBarValue).thenAccept(result -> {
255 assertTrue(result);
256 }).join();
257
258 map.replace("bar", "Goodbye bar!".getBytes(), rawBarValue).thenAccept(result -> {
259 assertFalse(result);
260 }).join();
261
262 Versioned<byte[]> barValue = map.get("bar").join();
263 map.replace("bar", barValue.version(), "Goodbye bar!".getBytes()).thenAccept(result -> {
264 assertTrue(result);
265 }).join();
266
267 map.replace("bar", barValue.version(), rawBarValue).thenAccept(result -> {
268 assertFalse(result);
269 }).join();
270
271 map.clear().join();
272
273 map.size().thenAccept(result -> {
274 assertTrue(result == 0);
275 }).join();
276 }
277
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700278 public void mapComputeOperationTests() throws Throwable {
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800279 final byte[] value1 = Tools.getBytesUtf8("value1");
280 final byte[] value2 = Tools.getBytesUtf8("value2");
281 final byte[] value3 = Tools.getBytesUtf8("value3");
282
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700283 AtomixConsistentMap map = newPrimitive("testMapComputeOperationsMap");
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800284
285 map.computeIfAbsent("foo", k -> value1).thenAccept(result -> {
286 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value1));
287 }).join();
288
289 map.computeIfAbsent("foo", k -> value2).thenAccept(result -> {
290 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value1));
291 }).join();
292
293 map.computeIfPresent("bar", (k, v) -> value2).thenAccept(result -> {
294 assertNull(result);
Jordan Halterman71635ae2017-07-28 10:35:43 -0700295 }).join();
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800296
297 map.computeIfPresent("foo", (k, v) -> value3).thenAccept(result -> {
298 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value3));
299 }).join();
300
301 map.computeIfPresent("foo", (k, v) -> null).thenAccept(result -> {
302 assertNull(result);
303 }).join();
304
305 map.computeIf("foo", v -> v == null, (k, v) -> value1).thenAccept(result -> {
306 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value1));
307 }).join();
308
309 map.compute("foo", (k, v) -> value2).thenAccept(result -> {
310 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value2));
311 }).join();
312 }
313
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700314 protected void mapListenerTests() throws Throwable {
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800315 final byte[] value1 = Tools.getBytesUtf8("value1");
316 final byte[] value2 = Tools.getBytesUtf8("value2");
317 final byte[] value3 = Tools.getBytesUtf8("value3");
318
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700319 AtomixConsistentMap map = newPrimitive("testMapListenerMap");
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800320 TestMapEventListener listener = new TestMapEventListener();
321
322 // add listener; insert new value into map and verify an INSERT event is received.
Madan Jampani40f022e2016-03-02 21:35:14 -0800323 map.addListener(listener).thenCompose(v -> map.put("foo", value1)).join();
324 MapEvent<String, byte[]> event = listener.event();
325 assertNotNull(event);
326 assertEquals(MapEvent.Type.INSERT, event.type());
327 assertTrue(Arrays.equals(value1, event.newValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800328
329 // remove listener and verify listener is not notified.
Madan Jampani40f022e2016-03-02 21:35:14 -0800330 map.removeListener(listener).thenCompose(v -> map.put("foo", value2)).join();
331 assertFalse(listener.eventReceived());
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800332
333 // add the listener back and verify UPDATE events are received correctly
Madan Jampani40f022e2016-03-02 21:35:14 -0800334 map.addListener(listener).thenCompose(v -> map.put("foo", value3)).join();
335 event = listener.event();
336 assertNotNull(event);
337 assertEquals(MapEvent.Type.UPDATE, event.type());
338 assertTrue(Arrays.equals(value3, event.newValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800339
340 // perform a non-state changing operation and verify no events are received.
341 map.putIfAbsent("foo", value1).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800342 assertFalse(listener.eventReceived());
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800343
344 // verify REMOVE events are received correctly.
345 map.remove("foo").join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800346 event = listener.event();
347 assertNotNull(event);
348 assertEquals(MapEvent.Type.REMOVE, event.type());
349 assertTrue(Arrays.equals(value3, event.oldValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800350
351 // verify compute methods also generate events.
352 map.computeIf("foo", v -> v == null, (k, v) -> value1).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800353 event = listener.event();
354 assertNotNull(event);
355 assertEquals(MapEvent.Type.INSERT, event.type());
356 assertTrue(Arrays.equals(value1, event.newValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800357
358 map.compute("foo", (k, v) -> value2).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800359 event = listener.event();
360 assertNotNull(event);
361 assertEquals(MapEvent.Type.UPDATE, event.type());
362 assertTrue(Arrays.equals(value2, event.newValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800363
364 map.computeIf("foo", v -> Arrays.equals(v, value2), (k, v) -> null).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800365 event = listener.event();
366 assertNotNull(event);
367 assertEquals(MapEvent.Type.REMOVE, event.type());
368 assertTrue(Arrays.equals(value2, event.oldValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800369
370 map.removeListener(listener).join();
371 }
372
Jordan Halterman5f97a302017-04-26 23:41:31 -0700373 protected void transactionPrepareTests() throws Throwable {
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700374 AtomixConsistentMap map = newPrimitive("testPrepareTestsMap");
Jordan Halterman5f97a302017-04-26 23:41:31 -0700375
376 TransactionId transactionId1 = TransactionId.from("tx1");
377 TransactionId transactionId2 = TransactionId.from("tx2");
378 TransactionId transactionId3 = TransactionId.from("tx3");
379 TransactionId transactionId4 = TransactionId.from("tx4");
380
381 Version lock1 = map.begin(transactionId1).join();
382
383 MapUpdate<String, byte[]> update1 =
384 MapUpdate.<String, byte[]>newBuilder()
385 .withType(MapUpdate.Type.LOCK)
386 .withKey("foo")
387 .withVersion(lock1.value())
388 .build();
389 MapUpdate<String, byte[]> update2 =
390 MapUpdate.<String, byte[]>newBuilder()
391 .withType(MapUpdate.Type.LOCK)
392 .withKey("bar")
393 .withVersion(lock1.value())
394 .build();
395
396 map.prepare(new TransactionLog<>(transactionId1, lock1.value(), Arrays.asList(update1, update2)))
397 .thenAccept(result -> {
398 assertTrue(result);
399 }).join();
400
401 Version lock2 = map.begin(transactionId2).join();
402
403 MapUpdate<String, byte[]> update3 =
404 MapUpdate.<String, byte[]>newBuilder()
405 .withType(MapUpdate.Type.LOCK)
406 .withKey("foo")
407 .withVersion(lock2.value())
408 .build();
409
410 map.prepare(new TransactionLog<>(transactionId2, lock2.value(), Arrays.asList(update3)))
411 .thenAccept(result -> {
412 assertFalse(result);
413 }).join();
414 map.rollback(transactionId2).join();
415
416 Version lock3 = map.begin(transactionId3).join();
417
418 MapUpdate<String, byte[]> update4 =
419 MapUpdate.<String, byte[]>newBuilder()
420 .withType(MapUpdate.Type.LOCK)
421 .withKey("baz")
422 .withVersion(0)
423 .build();
424
425 map.prepare(new TransactionLog<>(transactionId3, lock3.value(), Arrays.asList(update4)))
426 .thenAccept(result -> {
427 assertFalse(result);
428 }).join();
429 map.rollback(transactionId3).join();
430
431 Version lock4 = map.begin(transactionId4).join();
432
433 MapUpdate<String, byte[]> update5 =
434 MapUpdate.<String, byte[]>newBuilder()
435 .withType(MapUpdate.Type.LOCK)
436 .withKey("baz")
437 .withVersion(lock4.value())
438 .build();
439
440 map.prepare(new TransactionLog<>(transactionId4, lock4.value(), Arrays.asList(update5)))
441 .thenAccept(result -> {
442 assertTrue(result);
443 }).join();
444 }
445
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700446 protected void transactionCommitTests() throws Throwable {
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800447 final byte[] value1 = Tools.getBytesUtf8("value1");
448 final byte[] value2 = Tools.getBytesUtf8("value2");
449
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700450 AtomixConsistentMap map = newPrimitive("testCommitTestsMap");
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800451 TestMapEventListener listener = new TestMapEventListener();
452
453 map.addListener(listener).join();
454
Jordan Halterman5f97a302017-04-26 23:41:31 -0700455 TransactionId transactionId = TransactionId.from("tx1");
456
457 // Begin the transaction.
458 Version lock = map.begin(transactionId).join();
459
460 // PUT_IF_VERSION_MATCH
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800461 MapUpdate<String, byte[]> update1 =
Jordan Halterman5f97a302017-04-26 23:41:31 -0700462 MapUpdate.<String, byte[]>newBuilder().withType(MapUpdate.Type.PUT_IF_VERSION_MATCH)
463 .withKey("foo")
464 .withValue(value1)
465 .withVersion(lock.value())
466 .build();
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800467
Jordan Halterman5f97a302017-04-26 23:41:31 -0700468 map.prepare(new TransactionLog<>(transactionId, lock.value(), Arrays.asList(update1))).thenAccept(result -> {
Madan Jampani74da78b2016-02-09 21:18:36 -0800469 assertEquals(true, result);
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800470 }).join();
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700471 // verify changes in Tx is not visible yet until commit
Madan Jampani40f022e2016-03-02 21:35:14 -0800472 assertFalse(listener.eventReceived());
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800473
474 map.size().thenAccept(result -> {
475 assertTrue(result == 0);
476 }).join();
477
478 map.get("foo").thenAccept(result -> {
479 assertNull(result);
480 }).join();
481
482 try {
483 map.put("foo", value2).join();
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700484 fail("update to map entry in open tx should fail with Exception");
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800485 } catch (CompletionException e) {
486 assertEquals(ConcurrentModificationException.class, e.getCause().getClass());
487 }
488
Madan Jampani40f022e2016-03-02 21:35:14 -0800489 assertFalse(listener.eventReceived());
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800490
Jordan Halterman5f97a302017-04-26 23:41:31 -0700491 map.commit(transactionId).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800492 MapEvent<String, byte[]> event = listener.event();
493 assertNotNull(event);
494 assertEquals(MapEvent.Type.INSERT, event.type());
495 assertTrue(Arrays.equals(value1, event.newValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800496
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700497 // map should be update-able after commit
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800498 map.put("foo", value2).thenAccept(result -> {
499 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value1));
500 }).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800501 event = listener.event();
502 assertNotNull(event);
503 assertEquals(MapEvent.Type.UPDATE, event.type());
504 assertTrue(Arrays.equals(value2, event.newValue().value()));
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700505
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700506 // REMOVE_IF_VERSION_MATCH
507 byte[] currFoo = map.get("foo").get().value();
508 long currFooVersion = map.get("foo").get().version();
509 MapUpdate<String, byte[]> remove1 =
510 MapUpdate.<String, byte[]>newBuilder().withType(MapUpdate.Type.REMOVE_IF_VERSION_MATCH)
Jordan Halterman5f97a302017-04-26 23:41:31 -0700511 .withKey("foo")
512 .withVersion(currFooVersion)
513 .build();
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700514
Jordan Halterman5f97a302017-04-26 23:41:31 -0700515 transactionId = TransactionId.from("tx2");
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700516
Jordan Halterman5f97a302017-04-26 23:41:31 -0700517 // Begin the transaction.
518 map.begin(transactionId).join();
519
520 map.prepare(new TransactionLog<>(transactionId, lock.value(), Arrays.asList(remove1))).thenAccept(result -> {
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700521 assertTrue("prepare should succeed", result);
522 }).join();
523 // verify changes in Tx is not visible yet until commit
524 assertFalse(listener.eventReceived());
525
526 map.size().thenAccept(size -> {
527 assertThat(size, is(1));
528 }).join();
529
530 map.get("foo").thenAccept(result -> {
531 assertThat(result.value(), is(currFoo));
532 }).join();
533
Jordan Halterman5f97a302017-04-26 23:41:31 -0700534 map.commit(transactionId).join();
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700535 event = listener.event();
536 assertNotNull(event);
537 assertEquals(MapEvent.Type.REMOVE, event.type());
538 assertArrayEquals(currFoo, event.oldValue().value());
539
540 map.size().thenAccept(size -> {
541 assertThat(size, is(0));
542 }).join();
543
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800544 }
545
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700546 protected void transactionRollbackTests() throws Throwable {
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800547 final byte[] value1 = Tools.getBytesUtf8("value1");
548 final byte[] value2 = Tools.getBytesUtf8("value2");
549
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700550 AtomixConsistentMap map = newPrimitive("testTransactionRollbackTestsMap");
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800551 TestMapEventListener listener = new TestMapEventListener();
552
553 map.addListener(listener).join();
554
Jordan Halterman5f97a302017-04-26 23:41:31 -0700555 TransactionId transactionId = TransactionId.from("tx1");
556
557 Version lock = map.begin(transactionId).join();
558
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800559 MapUpdate<String, byte[]> update1 =
Jordan Halterman5f97a302017-04-26 23:41:31 -0700560 MapUpdate.<String, byte[]>newBuilder().withType(MapUpdate.Type.PUT_IF_VERSION_MATCH)
561 .withKey("foo")
562 .withValue(value1)
563 .withVersion(lock.value())
564 .build();
565
566 map.prepare(new TransactionLog<>(transactionId, lock.value(), Arrays.asList(update1))).thenAccept(result -> {
Madan Jampani74da78b2016-02-09 21:18:36 -0800567 assertEquals(true, result);
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800568 }).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800569 assertFalse(listener.eventReceived());
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800570
Jordan Halterman5f97a302017-04-26 23:41:31 -0700571 map.rollback(transactionId).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800572 assertFalse(listener.eventReceived());
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800573
574 map.get("foo").thenAccept(result -> {
575 assertNull(result);
576 }).join();
577
578 map.put("foo", value2).thenAccept(result -> {
579 assertNull(result);
580 }).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800581 MapEvent<String, byte[]> event = listener.event();
582 assertNotNull(event);
583 assertEquals(MapEvent.Type.INSERT, event.type());
584 assertTrue(Arrays.equals(value2, event.newValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800585 }
586
587 private static class TestMapEventListener implements MapEventListener<String, byte[]> {
588
Madan Jampani40f022e2016-03-02 21:35:14 -0800589 private final BlockingQueue<MapEvent<String, byte[]>> queue = new ArrayBlockingQueue<>(1);
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800590
591 @Override
592 public void event(MapEvent<String, byte[]> event) {
Madan Jampani40f022e2016-03-02 21:35:14 -0800593 try {
594 queue.put(event);
595 } catch (InterruptedException e) {
Ray Milkey6a51cb92018-03-06 09:03:03 -0800596 Thread.currentThread().interrupt();
597 throw new IllegalStateException(e);
Madan Jampani40f022e2016-03-02 21:35:14 -0800598 }
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800599 }
600
Madan Jampani40f022e2016-03-02 21:35:14 -0800601 public boolean eventReceived() {
602 return !queue.isEmpty();
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800603 }
604
Madan Jampani40f022e2016-03-02 21:35:14 -0800605 public MapEvent<String, byte[]> event() throws InterruptedException {
606 return queue.take();
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800607 }
608 }
609}