blob: d08495aa7b6ae22a0d7a321bd05297072f780053 [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
Jordan Halterman2bf177c2017-06-29 01:49:08 -070018import java.util.Arrays;
19import java.util.ConcurrentModificationException;
20import java.util.List;
21import java.util.concurrent.ArrayBlockingQueue;
22import java.util.concurrent.BlockingQueue;
23import java.util.concurrent.CompletionException;
24import java.util.stream.Collectors;
25
Aaron Kruglikovc0c27c02016-06-07 16:05:00 -070026import com.google.common.base.Throwables;
27import com.google.common.collect.Sets;
Jordan Halterman2bf177c2017-06-29 01:49:08 -070028import io.atomix.protocols.raft.proxy.RaftProxy;
29import io.atomix.protocols.raft.service.RaftService;
Madan Jampani5e5b3d62016-02-01 16:03:33 -080030import org.junit.Test;
31import org.onlab.util.Tools;
Madan Jampani74da78b2016-02-09 21:18:36 -080032import org.onosproject.store.primitives.MapUpdate;
Madan Jampanicadd70b2016-02-08 13:45:43 -080033import org.onosproject.store.primitives.TransactionId;
Madan Jampani5e5b3d62016-02-01 16:03:33 -080034import org.onosproject.store.service.MapEvent;
35import org.onosproject.store.service.MapEventListener;
Jordan Halterman948d6592017-04-20 17:18:24 -070036import org.onosproject.store.service.TransactionLog;
Jordan Halterman5f97a302017-04-26 23:41:31 -070037import org.onosproject.store.service.Version;
Madan Jampani5e5b3d62016-02-01 16:03:33 -080038import org.onosproject.store.service.Versioned;
39
Aaron Kruglikovc0c27c02016-06-07 16:05:00 -070040import static org.hamcrest.Matchers.is;
41import static org.junit.Assert.assertArrayEquals;
42import static org.junit.Assert.assertEquals;
43import static org.junit.Assert.assertFalse;
44import static org.junit.Assert.assertNotNull;
45import static org.junit.Assert.assertNull;
46import static org.junit.Assert.assertThat;
47import static org.junit.Assert.assertTrue;
48import static org.junit.Assert.fail;
Madan Jampani5e5b3d62016-02-01 16:03:33 -080049
50/**
51 * Unit tests for {@link AtomixConsistentMap}.
52 */
Jordan Halterman2bf177c2017-06-29 01:49:08 -070053public class AtomixConsistentMapTest extends AtomixTestBase<AtomixConsistentMap> {
Madan Jampani5e5b3d62016-02-01 16:03:33 -080054
55 @Override
Jordan Halterman2bf177c2017-06-29 01:49:08 -070056 protected RaftService createService() {
57 return new AtomixConsistentMapService();
58 }
59
60 @Override
61 protected AtomixConsistentMap createPrimitive(RaftProxy proxy) {
62 return new AtomixConsistentMap(proxy);
Madan Jampani5e5b3d62016-02-01 16:03:33 -080063 }
64
65 /**
66 * Tests various basic map operations.
67 */
68 @Test
69 public void testBasicMapOperations() throws Throwable {
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -070070 basicMapOperationTests();
Madan Jampani5e5b3d62016-02-01 16:03:33 -080071 }
72
73 /**
74 * Tests various map compute* operations on different cluster sizes.
75 */
76 @Test
77 public void testMapComputeOperations() throws Throwable {
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -070078 mapComputeOperationTests();
Madan Jampani5e5b3d62016-02-01 16:03:33 -080079 }
80
81 /**
Jordan Halterman4922a062017-07-31 15:55:36 -070082 * Tests null values.
83 */
84 @Test
85 public void testNullValues() throws Throwable {
86 final byte[] rawFooValue = Tools.getBytesUtf8("Hello foo!");
87 final byte[] rawBarValue = Tools.getBytesUtf8("Hello bar!");
88
89 AtomixConsistentMap map = newPrimitive("testNullValues");
90
91 map.get("foo")
92 .thenAccept(v -> assertNull(v)).join();
93 map.put("foo", null)
94 .thenAccept(v -> assertNull(v)).join();
95 map.put("foo", rawFooValue).thenAccept(v -> {
96 assertNotNull(v);
97 assertNull(v.value());
98 }).join();
99 map.get("foo").thenAccept(v -> {
100 assertNotNull(v);
101 assertTrue(Arrays.equals(v.value(), rawFooValue));
102 }).join();
103 map.replace("foo", rawFooValue, null)
104 .thenAccept(replaced -> assertTrue(replaced)).join();
105 map.get("foo").thenAccept(v -> {
106 assertNotNull(v);
107 assertNull(v.value());
108 }).join();
109 map.replace("foo", rawFooValue, rawBarValue)
110 .thenAccept(replaced -> assertFalse(replaced)).join();
111 map.replace("foo", null, rawBarValue)
112 .thenAccept(replaced -> assertTrue(replaced)).join();
113 map.get("foo").thenAccept(v -> {
114 assertNotNull(v);
115 assertTrue(Arrays.equals(v.value(), rawBarValue));
116 }).join();
117 }
118
119 /**
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800120 * Tests map event notifications.
121 */
122 @Test
123 public void testMapListeners() throws Throwable {
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700124 mapListenerTests();
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800125 }
126
127 /**
Jordan Halterman5f97a302017-04-26 23:41:31 -0700128 * Tests map transaction prepare.
129 */
130 @Test
131 public void testTransactionPrepare() throws Throwable {
132 transactionPrepareTests();
133 }
134
135 /**
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800136 * Tests map transaction commit.
137 */
138 @Test
139 public void testTransactionCommit() throws Throwable {
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700140 transactionCommitTests();
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800141 }
142
143 /**
144 * Tests map transaction rollback.
145 */
146 @Test
147 public void testTransactionRollback() throws Throwable {
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700148 transactionRollbackTests();
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800149 }
150
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700151 protected void basicMapOperationTests() throws Throwable {
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800152 final byte[] rawFooValue = Tools.getBytesUtf8("Hello foo!");
153 final byte[] rawBarValue = Tools.getBytesUtf8("Hello bar!");
154
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700155 AtomixConsistentMap map = newPrimitive("testBasicMapOperationMap");
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800156
157 map.isEmpty().thenAccept(result -> {
158 assertTrue(result);
159 }).join();
160
161 map.put("foo", rawFooValue).thenAccept(result -> {
162 assertNull(result);
163 }).join();
164
165 map.size().thenAccept(result -> {
166 assertTrue(result == 1);
167 }).join();
168
169 map.isEmpty().thenAccept(result -> {
170 assertFalse(result);
171 }).join();
172
173 map.putIfAbsent("foo", "Hello foo again!".getBytes()).thenAccept(result -> {
174 assertNotNull(result);
175 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), rawFooValue));
176 }).join();
177
178 map.putIfAbsent("bar", rawBarValue).thenAccept(result -> {
179 assertNull(result);
180 }).join();
181
182 map.size().thenAccept(result -> {
183 assertTrue(result == 2);
184 }).join();
185
186 map.keySet().thenAccept(result -> {
187 assertTrue(result.size() == 2);
188 assertTrue(result.containsAll(Sets.newHashSet("foo", "bar")));
189 }).join();
190
191 map.values().thenAccept(result -> {
192 assertTrue(result.size() == 2);
193 List<String> rawValues =
194 result.stream().map(v -> Tools.toStringUtf8(v.value())).collect(Collectors.toList());
195 assertTrue(rawValues.contains("Hello foo!"));
196 assertTrue(rawValues.contains("Hello bar!"));
197 }).join();
198
199 map.entrySet().thenAccept(result -> {
200 assertTrue(result.size() == 2);
201 // TODO: check entries
202 }).join();
203
204 map.get("foo").thenAccept(result -> {
205 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), rawFooValue));
206 }).join();
207
208 map.remove("foo").thenAccept(result -> {
209 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), rawFooValue));
210 }).join();
211
212 map.containsKey("foo").thenAccept(result -> {
213 assertFalse(result);
214 }).join();
215
216 map.get("foo").thenAccept(result -> {
217 assertNull(result);
218 }).join();
219
220 map.get("bar").thenAccept(result -> {
221 assertNotNull(result);
222 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), rawBarValue));
223 }).join();
224
225 map.containsKey("bar").thenAccept(result -> {
226 assertTrue(result);
227 }).join();
228
229 map.size().thenAccept(result -> {
230 assertTrue(result == 1);
231 }).join();
232
233 map.containsValue(rawBarValue).thenAccept(result -> {
234 assertTrue(result);
235 }).join();
236
237 map.containsValue(rawFooValue).thenAccept(result -> {
238 assertFalse(result);
239 }).join();
240
241 map.replace("bar", "Goodbye bar!".getBytes()).thenAccept(result -> {
242 assertNotNull(result);
243 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), rawBarValue));
244 }).join();
245
246 map.replace("foo", "Goodbye foo!".getBytes()).thenAccept(result -> {
247 assertNull(result);
248 }).join();
249
250 // try replace_if_value_match for a non-existent key
251 map.replace("foo", "Goodbye foo!".getBytes(), rawFooValue).thenAccept(result -> {
252 assertFalse(result);
253 }).join();
254
255 map.replace("bar", "Goodbye bar!".getBytes(), rawBarValue).thenAccept(result -> {
256 assertTrue(result);
257 }).join();
258
259 map.replace("bar", "Goodbye bar!".getBytes(), rawBarValue).thenAccept(result -> {
260 assertFalse(result);
261 }).join();
262
263 Versioned<byte[]> barValue = map.get("bar").join();
264 map.replace("bar", barValue.version(), "Goodbye bar!".getBytes()).thenAccept(result -> {
265 assertTrue(result);
266 }).join();
267
268 map.replace("bar", barValue.version(), rawBarValue).thenAccept(result -> {
269 assertFalse(result);
270 }).join();
271
272 map.clear().join();
273
274 map.size().thenAccept(result -> {
275 assertTrue(result == 0);
276 }).join();
277 }
278
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700279 public void mapComputeOperationTests() throws Throwable {
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800280 final byte[] value1 = Tools.getBytesUtf8("value1");
281 final byte[] value2 = Tools.getBytesUtf8("value2");
282 final byte[] value3 = Tools.getBytesUtf8("value3");
283
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700284 AtomixConsistentMap map = newPrimitive("testMapComputeOperationsMap");
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800285
286 map.computeIfAbsent("foo", k -> value1).thenAccept(result -> {
287 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value1));
288 }).join();
289
290 map.computeIfAbsent("foo", k -> value2).thenAccept(result -> {
291 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value1));
292 }).join();
293
294 map.computeIfPresent("bar", (k, v) -> value2).thenAccept(result -> {
295 assertNull(result);
Jordan Halterman71635ae2017-07-28 10:35:43 -0700296 }).join();
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800297
298 map.computeIfPresent("foo", (k, v) -> value3).thenAccept(result -> {
299 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value3));
300 }).join();
301
302 map.computeIfPresent("foo", (k, v) -> null).thenAccept(result -> {
303 assertNull(result);
304 }).join();
305
306 map.computeIf("foo", v -> v == null, (k, v) -> value1).thenAccept(result -> {
307 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value1));
308 }).join();
309
310 map.compute("foo", (k, v) -> value2).thenAccept(result -> {
311 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value2));
312 }).join();
313 }
314
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700315 protected void mapListenerTests() throws Throwable {
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800316 final byte[] value1 = Tools.getBytesUtf8("value1");
317 final byte[] value2 = Tools.getBytesUtf8("value2");
318 final byte[] value3 = Tools.getBytesUtf8("value3");
319
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700320 AtomixConsistentMap map = newPrimitive("testMapListenerMap");
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800321 TestMapEventListener listener = new TestMapEventListener();
322
323 // add listener; insert new value into map and verify an INSERT event is received.
Madan Jampani40f022e2016-03-02 21:35:14 -0800324 map.addListener(listener).thenCompose(v -> map.put("foo", value1)).join();
325 MapEvent<String, byte[]> event = listener.event();
326 assertNotNull(event);
327 assertEquals(MapEvent.Type.INSERT, event.type());
328 assertTrue(Arrays.equals(value1, event.newValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800329
330 // remove listener and verify listener is not notified.
Madan Jampani40f022e2016-03-02 21:35:14 -0800331 map.removeListener(listener).thenCompose(v -> map.put("foo", value2)).join();
332 assertFalse(listener.eventReceived());
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800333
334 // add the listener back and verify UPDATE events are received correctly
Madan Jampani40f022e2016-03-02 21:35:14 -0800335 map.addListener(listener).thenCompose(v -> map.put("foo", value3)).join();
336 event = listener.event();
337 assertNotNull(event);
338 assertEquals(MapEvent.Type.UPDATE, event.type());
339 assertTrue(Arrays.equals(value3, event.newValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800340
341 // perform a non-state changing operation and verify no events are received.
342 map.putIfAbsent("foo", value1).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800343 assertFalse(listener.eventReceived());
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800344
345 // verify REMOVE events are received correctly.
346 map.remove("foo").join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800347 event = listener.event();
348 assertNotNull(event);
349 assertEquals(MapEvent.Type.REMOVE, event.type());
350 assertTrue(Arrays.equals(value3, event.oldValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800351
352 // verify compute methods also generate events.
353 map.computeIf("foo", v -> v == null, (k, v) -> value1).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800354 event = listener.event();
355 assertNotNull(event);
356 assertEquals(MapEvent.Type.INSERT, event.type());
357 assertTrue(Arrays.equals(value1, event.newValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800358
359 map.compute("foo", (k, v) -> value2).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800360 event = listener.event();
361 assertNotNull(event);
362 assertEquals(MapEvent.Type.UPDATE, event.type());
363 assertTrue(Arrays.equals(value2, event.newValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800364
365 map.computeIf("foo", v -> Arrays.equals(v, value2), (k, v) -> null).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800366 event = listener.event();
367 assertNotNull(event);
368 assertEquals(MapEvent.Type.REMOVE, event.type());
369 assertTrue(Arrays.equals(value2, event.oldValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800370
371 map.removeListener(listener).join();
372 }
373
Jordan Halterman5f97a302017-04-26 23:41:31 -0700374 protected void transactionPrepareTests() throws Throwable {
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700375 AtomixConsistentMap map = newPrimitive("testPrepareTestsMap");
Jordan Halterman5f97a302017-04-26 23:41:31 -0700376
377 TransactionId transactionId1 = TransactionId.from("tx1");
378 TransactionId transactionId2 = TransactionId.from("tx2");
379 TransactionId transactionId3 = TransactionId.from("tx3");
380 TransactionId transactionId4 = TransactionId.from("tx4");
381
382 Version lock1 = map.begin(transactionId1).join();
383
384 MapUpdate<String, byte[]> update1 =
385 MapUpdate.<String, byte[]>newBuilder()
386 .withType(MapUpdate.Type.LOCK)
387 .withKey("foo")
388 .withVersion(lock1.value())
389 .build();
390 MapUpdate<String, byte[]> update2 =
391 MapUpdate.<String, byte[]>newBuilder()
392 .withType(MapUpdate.Type.LOCK)
393 .withKey("bar")
394 .withVersion(lock1.value())
395 .build();
396
397 map.prepare(new TransactionLog<>(transactionId1, lock1.value(), Arrays.asList(update1, update2)))
398 .thenAccept(result -> {
399 assertTrue(result);
400 }).join();
401
402 Version lock2 = map.begin(transactionId2).join();
403
404 MapUpdate<String, byte[]> update3 =
405 MapUpdate.<String, byte[]>newBuilder()
406 .withType(MapUpdate.Type.LOCK)
407 .withKey("foo")
408 .withVersion(lock2.value())
409 .build();
410
411 map.prepare(new TransactionLog<>(transactionId2, lock2.value(), Arrays.asList(update3)))
412 .thenAccept(result -> {
413 assertFalse(result);
414 }).join();
415 map.rollback(transactionId2).join();
416
417 Version lock3 = map.begin(transactionId3).join();
418
419 MapUpdate<String, byte[]> update4 =
420 MapUpdate.<String, byte[]>newBuilder()
421 .withType(MapUpdate.Type.LOCK)
422 .withKey("baz")
423 .withVersion(0)
424 .build();
425
426 map.prepare(new TransactionLog<>(transactionId3, lock3.value(), Arrays.asList(update4)))
427 .thenAccept(result -> {
428 assertFalse(result);
429 }).join();
430 map.rollback(transactionId3).join();
431
432 Version lock4 = map.begin(transactionId4).join();
433
434 MapUpdate<String, byte[]> update5 =
435 MapUpdate.<String, byte[]>newBuilder()
436 .withType(MapUpdate.Type.LOCK)
437 .withKey("baz")
438 .withVersion(lock4.value())
439 .build();
440
441 map.prepare(new TransactionLog<>(transactionId4, lock4.value(), Arrays.asList(update5)))
442 .thenAccept(result -> {
443 assertTrue(result);
444 }).join();
445 }
446
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700447 protected void transactionCommitTests() throws Throwable {
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800448 final byte[] value1 = Tools.getBytesUtf8("value1");
449 final byte[] value2 = Tools.getBytesUtf8("value2");
450
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700451 AtomixConsistentMap map = newPrimitive("testCommitTestsMap");
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800452 TestMapEventListener listener = new TestMapEventListener();
453
454 map.addListener(listener).join();
455
Jordan Halterman5f97a302017-04-26 23:41:31 -0700456 TransactionId transactionId = TransactionId.from("tx1");
457
458 // Begin the transaction.
459 Version lock = map.begin(transactionId).join();
460
461 // PUT_IF_VERSION_MATCH
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800462 MapUpdate<String, byte[]> update1 =
Jordan Halterman5f97a302017-04-26 23:41:31 -0700463 MapUpdate.<String, byte[]>newBuilder().withType(MapUpdate.Type.PUT_IF_VERSION_MATCH)
464 .withKey("foo")
465 .withValue(value1)
466 .withVersion(lock.value())
467 .build();
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800468
Jordan Halterman5f97a302017-04-26 23:41:31 -0700469 map.prepare(new TransactionLog<>(transactionId, lock.value(), Arrays.asList(update1))).thenAccept(result -> {
Madan Jampani74da78b2016-02-09 21:18:36 -0800470 assertEquals(true, result);
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800471 }).join();
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700472 // verify changes in Tx is not visible yet until commit
Madan Jampani40f022e2016-03-02 21:35:14 -0800473 assertFalse(listener.eventReceived());
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800474
475 map.size().thenAccept(result -> {
476 assertTrue(result == 0);
477 }).join();
478
479 map.get("foo").thenAccept(result -> {
480 assertNull(result);
481 }).join();
482
483 try {
484 map.put("foo", value2).join();
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700485 fail("update to map entry in open tx should fail with Exception");
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800486 } catch (CompletionException e) {
487 assertEquals(ConcurrentModificationException.class, e.getCause().getClass());
488 }
489
Madan Jampani40f022e2016-03-02 21:35:14 -0800490 assertFalse(listener.eventReceived());
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800491
Jordan Halterman5f97a302017-04-26 23:41:31 -0700492 map.commit(transactionId).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800493 MapEvent<String, byte[]> event = listener.event();
494 assertNotNull(event);
495 assertEquals(MapEvent.Type.INSERT, event.type());
496 assertTrue(Arrays.equals(value1, event.newValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800497
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700498 // map should be update-able after commit
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800499 map.put("foo", value2).thenAccept(result -> {
500 assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value1));
501 }).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800502 event = listener.event();
503 assertNotNull(event);
504 assertEquals(MapEvent.Type.UPDATE, event.type());
505 assertTrue(Arrays.equals(value2, event.newValue().value()));
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700506
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700507 // REMOVE_IF_VERSION_MATCH
508 byte[] currFoo = map.get("foo").get().value();
509 long currFooVersion = map.get("foo").get().version();
510 MapUpdate<String, byte[]> remove1 =
511 MapUpdate.<String, byte[]>newBuilder().withType(MapUpdate.Type.REMOVE_IF_VERSION_MATCH)
Jordan Halterman5f97a302017-04-26 23:41:31 -0700512 .withKey("foo")
513 .withVersion(currFooVersion)
514 .build();
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700515
Jordan Halterman5f97a302017-04-26 23:41:31 -0700516 transactionId = TransactionId.from("tx2");
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700517
Jordan Halterman5f97a302017-04-26 23:41:31 -0700518 // Begin the transaction.
519 map.begin(transactionId).join();
520
521 map.prepare(new TransactionLog<>(transactionId, lock.value(), Arrays.asList(remove1))).thenAccept(result -> {
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700522 assertTrue("prepare should succeed", result);
523 }).join();
524 // verify changes in Tx is not visible yet until commit
525 assertFalse(listener.eventReceived());
526
527 map.size().thenAccept(size -> {
528 assertThat(size, is(1));
529 }).join();
530
531 map.get("foo").thenAccept(result -> {
532 assertThat(result.value(), is(currFoo));
533 }).join();
534
Jordan Halterman5f97a302017-04-26 23:41:31 -0700535 map.commit(transactionId).join();
HIGUCHI Yutacaad26b2016-04-16 16:06:11 -0700536 event = listener.event();
537 assertNotNull(event);
538 assertEquals(MapEvent.Type.REMOVE, event.type());
539 assertArrayEquals(currFoo, event.oldValue().value());
540
541 map.size().thenAccept(size -> {
542 assertThat(size, is(0));
543 }).join();
544
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800545 }
546
Aaron Kruglikovc38dc7f2016-07-19 10:00:11 -0700547 protected void transactionRollbackTests() throws Throwable {
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800548 final byte[] value1 = Tools.getBytesUtf8("value1");
549 final byte[] value2 = Tools.getBytesUtf8("value2");
550
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700551 AtomixConsistentMap map = newPrimitive("testTransactionRollbackTestsMap");
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800552 TestMapEventListener listener = new TestMapEventListener();
553
554 map.addListener(listener).join();
555
Jordan Halterman5f97a302017-04-26 23:41:31 -0700556 TransactionId transactionId = TransactionId.from("tx1");
557
558 Version lock = map.begin(transactionId).join();
559
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800560 MapUpdate<String, byte[]> update1 =
Jordan Halterman5f97a302017-04-26 23:41:31 -0700561 MapUpdate.<String, byte[]>newBuilder().withType(MapUpdate.Type.PUT_IF_VERSION_MATCH)
562 .withKey("foo")
563 .withValue(value1)
564 .withVersion(lock.value())
565 .build();
566
567 map.prepare(new TransactionLog<>(transactionId, lock.value(), Arrays.asList(update1))).thenAccept(result -> {
Madan Jampani74da78b2016-02-09 21:18:36 -0800568 assertEquals(true, result);
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800569 }).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800570 assertFalse(listener.eventReceived());
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800571
Jordan Halterman5f97a302017-04-26 23:41:31 -0700572 map.rollback(transactionId).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800573 assertFalse(listener.eventReceived());
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800574
575 map.get("foo").thenAccept(result -> {
576 assertNull(result);
577 }).join();
578
579 map.put("foo", value2).thenAccept(result -> {
580 assertNull(result);
581 }).join();
Madan Jampani40f022e2016-03-02 21:35:14 -0800582 MapEvent<String, byte[]> event = listener.event();
583 assertNotNull(event);
584 assertEquals(MapEvent.Type.INSERT, event.type());
585 assertTrue(Arrays.equals(value2, event.newValue().value()));
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800586 }
587
588 private static class TestMapEventListener implements MapEventListener<String, byte[]> {
589
Madan Jampani40f022e2016-03-02 21:35:14 -0800590 private final BlockingQueue<MapEvent<String, byte[]>> queue = new ArrayBlockingQueue<>(1);
Madan Jampani5e5b3d62016-02-01 16:03:33 -0800591
592 @Override
593 public void event(MapEvent<String, byte[]> event) {
Madan Jampani40f022e2016-03-02 21:35:14 -0800594 try {
595 queue.put(event);
596 } catch (InterruptedException e) {
597 Throwables.propagate(e);
598 }
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}