|
1 | 1 | package com.scalar.db.transaction.consensuscommit; |
2 | 2 |
|
3 | 3 | import static com.scalar.db.api.ConditionBuilder.column; |
| 4 | +import static com.scalar.db.api.ConditionBuilder.updateIf; |
4 | 5 | import static org.assertj.core.api.Assertions.assertThat; |
5 | 6 | import static org.assertj.core.api.Assertions.assertThatCode; |
6 | 7 | import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|
45 | 46 | import com.scalar.db.exception.transaction.CrudConflictException; |
46 | 47 | import com.scalar.db.exception.transaction.CrudException; |
47 | 48 | import com.scalar.db.exception.transaction.PreparationConflictException; |
| 49 | +import com.scalar.db.exception.transaction.RollbackException; |
48 | 50 | import com.scalar.db.exception.transaction.TransactionException; |
49 | 51 | import com.scalar.db.exception.transaction.UnknownTransactionStatusException; |
50 | 52 | import com.scalar.db.io.DataType; |
@@ -5927,6 +5929,91 @@ public void getScanner_InReadOnlyMode_WithSerializable_ShouldNotThrowAnyExceptio |
5927 | 5929 | assertThat(results.get(1).getInt(BALANCE)).isEqualTo(INITIAL_BALANCE); |
5928 | 5930 | } |
5929 | 5931 |
|
| 5932 | + @Test |
| 5933 | + public void |
| 5934 | + commit_ConflictingExternalUpdate_DifferentGetButSameRecordReturned_ShouldThrowCommitConflictExceptionAndPreserveExternalChanges() |
| 5935 | + throws UnknownTransactionStatusException, CrudException, RollbackException { |
| 5936 | + // Arrange |
| 5937 | + manager.insert( |
| 5938 | + Insert.newBuilder() |
| 5939 | + .namespace(namespace1) |
| 5940 | + .table(TABLE_1) |
| 5941 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 5942 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 5943 | + .intValue(BALANCE, INITIAL_BALANCE) |
| 5944 | + .build()); |
| 5945 | + |
| 5946 | + // Act Assert |
| 5947 | + DistributedTransaction transaction = manager.begin(); |
| 5948 | + |
| 5949 | + // Retrieve the record |
| 5950 | + Optional<Result> result = |
| 5951 | + transaction.get( |
| 5952 | + Get.newBuilder() |
| 5953 | + .namespace(namespace1) |
| 5954 | + .table(TABLE_1) |
| 5955 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 5956 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 5957 | + .build()); |
| 5958 | + |
| 5959 | + assertThat(result).isPresent(); |
| 5960 | + assertThat(result.get().getInt(ACCOUNT_ID)).isEqualTo(0); |
| 5961 | + assertThat(result.get().getInt(ACCOUNT_TYPE)).isEqualTo(0); |
| 5962 | + assertThat(result.get().getInt(BALANCE)).isEqualTo(INITIAL_BALANCE); |
| 5963 | + |
| 5964 | + // Update the balance of the record |
| 5965 | + transaction.update( |
| 5966 | + Update.newBuilder() |
| 5967 | + .namespace(namespace1) |
| 5968 | + .table(TABLE_1) |
| 5969 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 5970 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 5971 | + .condition(updateIf(column(BALANCE).isEqualToInt(INITIAL_BALANCE)).build()) |
| 5972 | + .intValue(BALANCE, 100) |
| 5973 | + .build()); |
| 5974 | + |
| 5975 | + // Update the balance of the record by another transaction |
| 5976 | + manager.update( |
| 5977 | + Update.newBuilder() |
| 5978 | + .namespace(namespace1) |
| 5979 | + .table(TABLE_1) |
| 5980 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 5981 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 5982 | + .intValue(BALANCE, 200) |
| 5983 | + .build()); |
| 5984 | + |
| 5985 | + // Retrieve the record again, but use a different Get object (with a where clause) |
| 5986 | + result = |
| 5987 | + transaction.get( |
| 5988 | + Get.newBuilder() |
| 5989 | + .namespace(namespace1) |
| 5990 | + .table(TABLE_1) |
| 5991 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 5992 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 5993 | + .where(column(BALANCE).isEqualToInt(200)) |
| 5994 | + .build()); |
| 5995 | + |
| 5996 | + assertThat(result).isNotPresent(); |
| 5997 | + |
| 5998 | + assertThatThrownBy(transaction::commit).isInstanceOf(CommitConflictException.class); |
| 5999 | + transaction.rollback(); |
| 6000 | + |
| 6001 | + // Assert |
| 6002 | + result = |
| 6003 | + manager.get( |
| 6004 | + Get.newBuilder() |
| 6005 | + .namespace(namespace1) |
| 6006 | + .table(TABLE_1) |
| 6007 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 6008 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 6009 | + .build()); |
| 6010 | + |
| 6011 | + assertThat(result).isPresent(); |
| 6012 | + assertThat(result.get().getInt(ACCOUNT_ID)).isEqualTo(0); |
| 6013 | + assertThat(result.get().getInt(ACCOUNT_TYPE)).isEqualTo(0); |
| 6014 | + assertThat(result.get().getInt(BALANCE)).isEqualTo(200); |
| 6015 | + } |
| 6016 | + |
5930 | 6017 | @Test |
5931 | 6018 | public void manager_get_GetGivenForCommittedRecord_WithSerializable_ShouldReturnRecord() |
5932 | 6019 | throws TransactionException { |
|
0 commit comments