|
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; |
@@ -5893,6 +5895,91 @@ public void getScanner_InReadOnlyMode_WithSerializable_ShouldNotThrowAnyExceptio |
5893 | 5895 | assertThat(results.get(1).getInt(BALANCE)).isEqualTo(INITIAL_BALANCE); |
5894 | 5896 | } |
5895 | 5897 |
|
| 5898 | + @Test |
| 5899 | + public void |
| 5900 | + commit_ConflictingExternalUpdate_DifferentGetButSameRecordReturned_ShouldThrowCommitConflictExceptionAndPreserveExternalChanges() |
| 5901 | + throws UnknownTransactionStatusException, CrudException, RollbackException { |
| 5902 | + // Arrange |
| 5903 | + manager.insert( |
| 5904 | + Insert.newBuilder() |
| 5905 | + .namespace(namespace1) |
| 5906 | + .table(TABLE_1) |
| 5907 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 5908 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 5909 | + .intValue(BALANCE, INITIAL_BALANCE) |
| 5910 | + .build()); |
| 5911 | + |
| 5912 | + // Act Assert |
| 5913 | + DistributedTransaction transaction = manager.begin(); |
| 5914 | + |
| 5915 | + // Retrieve the record |
| 5916 | + Optional<Result> result = |
| 5917 | + transaction.get( |
| 5918 | + Get.newBuilder() |
| 5919 | + .namespace(namespace1) |
| 5920 | + .table(TABLE_1) |
| 5921 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 5922 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 5923 | + .build()); |
| 5924 | + |
| 5925 | + assertThat(result).isPresent(); |
| 5926 | + assertThat(result.get().getInt(ACCOUNT_ID)).isEqualTo(0); |
| 5927 | + assertThat(result.get().getInt(ACCOUNT_TYPE)).isEqualTo(0); |
| 5928 | + assertThat(result.get().getInt(BALANCE)).isEqualTo(INITIAL_BALANCE); |
| 5929 | + |
| 5930 | + // Update the balance of the record |
| 5931 | + transaction.update( |
| 5932 | + Update.newBuilder() |
| 5933 | + .namespace(namespace1) |
| 5934 | + .table(TABLE_1) |
| 5935 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 5936 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 5937 | + .condition(updateIf(column(BALANCE).isEqualToInt(INITIAL_BALANCE)).build()) |
| 5938 | + .intValue(BALANCE, 100) |
| 5939 | + .build()); |
| 5940 | + |
| 5941 | + // Update the balance of the record by another transaction |
| 5942 | + manager.update( |
| 5943 | + Update.newBuilder() |
| 5944 | + .namespace(namespace1) |
| 5945 | + .table(TABLE_1) |
| 5946 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 5947 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 5948 | + .intValue(BALANCE, 200) |
| 5949 | + .build()); |
| 5950 | + |
| 5951 | + // Retrieve the record again, but use a different Get object (with a where clause) |
| 5952 | + result = |
| 5953 | + transaction.get( |
| 5954 | + Get.newBuilder() |
| 5955 | + .namespace(namespace1) |
| 5956 | + .table(TABLE_1) |
| 5957 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 5958 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 5959 | + .where(column(BALANCE).isEqualToInt(200)) |
| 5960 | + .build()); |
| 5961 | + |
| 5962 | + assertThat(result).isNotPresent(); |
| 5963 | + |
| 5964 | + assertThatThrownBy(transaction::commit).isInstanceOf(CommitConflictException.class); |
| 5965 | + transaction.rollback(); |
| 5966 | + |
| 5967 | + // Assert |
| 5968 | + result = |
| 5969 | + manager.get( |
| 5970 | + Get.newBuilder() |
| 5971 | + .namespace(namespace1) |
| 5972 | + .table(TABLE_1) |
| 5973 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 5974 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 5975 | + .build()); |
| 5976 | + |
| 5977 | + assertThat(result).isPresent(); |
| 5978 | + assertThat(result.get().getInt(ACCOUNT_ID)).isEqualTo(0); |
| 5979 | + assertThat(result.get().getInt(ACCOUNT_TYPE)).isEqualTo(0); |
| 5980 | + assertThat(result.get().getInt(BALANCE)).isEqualTo(200); |
| 5981 | + } |
| 5982 | + |
5896 | 5983 | @Test |
5897 | 5984 | public void manager_get_GetGivenForCommittedRecord_WithSerializable_ShouldReturnRecord() |
5898 | 5985 | throws TransactionException { |
|
0 commit comments