|
23 | 23 | import static org.junit.Assert.assertTrue; |
24 | 24 | import static org.junit.Assert.fail; |
25 | 25 |
|
| 26 | +import com.google.api.core.ApiFuture; |
26 | 27 | import com.google.api.core.ApiFutures; |
27 | 28 | import com.google.cloud.firestore.CollectionReference; |
28 | 29 | import com.google.cloud.firestore.DocumentReference; |
|
47 | 48 | import java.util.Iterator; |
48 | 49 | import java.util.List; |
49 | 50 | import java.util.Map; |
| 51 | +import java.util.concurrent.CountDownLatch; |
50 | 52 | import java.util.concurrent.ExecutionException; |
| 53 | +import java.util.concurrent.atomic.AtomicInteger; |
51 | 54 | import org.junit.Before; |
52 | 55 | import org.junit.Rule; |
53 | 56 | import org.junit.Test; |
@@ -425,25 +428,50 @@ public String updateCallback(Transaction transaction) { |
425 | 428 | } |
426 | 429 |
|
427 | 430 | @Test |
428 | | - public void successfulTransaction() throws Exception { |
429 | | - final DocumentReference documentReference = addDocument("foo", 1); |
430 | | - |
431 | | - String result = |
432 | | - firestore |
433 | | - .runTransaction( |
434 | | - new Transaction.Function<String>() { |
435 | | - @Override |
436 | | - public String updateCallback(Transaction transaction) |
437 | | - throws ExecutionException, InterruptedException { |
438 | | - DocumentSnapshot documentSnapshot = documentReference.get().get(); |
439 | | - documentReference.update("foo", documentSnapshot.getLong("foo") + 1); |
440 | | - return "bar"; |
441 | | - } |
442 | | - }) |
443 | | - .get(); |
444 | | - |
445 | | - assertEquals("bar", result); |
446 | | - assertEquals(2L, documentReference.get().get().get("foo")); |
| 431 | + public void successfulTransactionWithContention() throws Exception { |
| 432 | + final DocumentReference documentReference = addDocument("counter", 1); |
| 433 | + |
| 434 | + final CountDownLatch latch = new CountDownLatch(2); |
| 435 | + final AtomicInteger attempts = new AtomicInteger(); |
| 436 | + |
| 437 | + // One of these transaction fails and has to be retried since they both acquire locks on the |
| 438 | + // same document, which they then modify. |
| 439 | + ApiFuture<String> firstTransaction = |
| 440 | + firestore.runTransaction( |
| 441 | + new Transaction.Function<String>() { |
| 442 | + @Override |
| 443 | + public String updateCallback(Transaction transaction) |
| 444 | + throws ExecutionException, InterruptedException { |
| 445 | + attempts.incrementAndGet(); |
| 446 | + DocumentSnapshot documentSnapshot = transaction.get(documentReference).get(); |
| 447 | + latch.countDown(); |
| 448 | + latch.await(); |
| 449 | + transaction.update(documentReference, |
| 450 | + "counter", documentSnapshot.getLong("counter") + 1); |
| 451 | + return "foo"; |
| 452 | + } |
| 453 | + }); |
| 454 | + |
| 455 | + ApiFuture<String> secondTransaction = |
| 456 | + firestore.runTransaction( |
| 457 | + new Function<String>() { |
| 458 | + @Override |
| 459 | + public String updateCallback(Transaction transaction) |
| 460 | + throws ExecutionException, InterruptedException { |
| 461 | + attempts.incrementAndGet(); |
| 462 | + DocumentSnapshot documentSnapshot = transaction.get(documentReference).get(); |
| 463 | + latch.countDown(); |
| 464 | + latch.await(); |
| 465 | + transaction.update(documentReference, |
| 466 | + "counter", documentSnapshot.getLong("counter") + 1); |
| 467 | + return "bar"; |
| 468 | + } |
| 469 | + }); |
| 470 | + |
| 471 | + assertEquals("foo", firstTransaction.get()); |
| 472 | + assertEquals("bar", secondTransaction.get()); |
| 473 | + assertEquals(3, attempts.intValue()); |
| 474 | + assertEquals(3, (long) documentReference.get().get().getLong("counter")); |
447 | 475 | } |
448 | 476 |
|
449 | 477 | @Test |
|
0 commit comments