|
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; |
@@ -6073,6 +6075,91 @@ public void getScanner_InReadOnlyMode_WithSerializable_ShouldNotThrowAnyExceptio |
6073 | 6075 | assertThat(results.get(1).getInt(BALANCE)).isEqualTo(INITIAL_BALANCE); |
6074 | 6076 | } |
6075 | 6077 |
|
| 6078 | + @Test |
| 6079 | + public void |
| 6080 | + commit_ConflictingExternalUpdate_DifferentGetButSameRecordReturned_ShouldThrowCommitConflictExceptionAndPreserveExternalChanges() |
| 6081 | + throws UnknownTransactionStatusException, CrudException, RollbackException { |
| 6082 | + // Arrange |
| 6083 | + manager.insert( |
| 6084 | + Insert.newBuilder() |
| 6085 | + .namespace(namespace1) |
| 6086 | + .table(TABLE_1) |
| 6087 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 6088 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 6089 | + .intValue(BALANCE, INITIAL_BALANCE) |
| 6090 | + .build()); |
| 6091 | + |
| 6092 | + // Act Assert |
| 6093 | + DistributedTransaction transaction = manager.begin(); |
| 6094 | + |
| 6095 | + // Retrieve the record |
| 6096 | + Optional<Result> result = |
| 6097 | + transaction.get( |
| 6098 | + Get.newBuilder() |
| 6099 | + .namespace(namespace1) |
| 6100 | + .table(TABLE_1) |
| 6101 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 6102 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 6103 | + .build()); |
| 6104 | + |
| 6105 | + assertThat(result).isPresent(); |
| 6106 | + assertThat(result.get().getInt(ACCOUNT_ID)).isEqualTo(0); |
| 6107 | + assertThat(result.get().getInt(ACCOUNT_TYPE)).isEqualTo(0); |
| 6108 | + assertThat(result.get().getInt(BALANCE)).isEqualTo(INITIAL_BALANCE); |
| 6109 | + |
| 6110 | + // Update the balance of the record |
| 6111 | + transaction.update( |
| 6112 | + Update.newBuilder() |
| 6113 | + .namespace(namespace1) |
| 6114 | + .table(TABLE_1) |
| 6115 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 6116 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 6117 | + .condition(updateIf(column(BALANCE).isEqualToInt(INITIAL_BALANCE)).build()) |
| 6118 | + .intValue(BALANCE, 100) |
| 6119 | + .build()); |
| 6120 | + |
| 6121 | + // Update the balance of the record by another transaction |
| 6122 | + manager.update( |
| 6123 | + Update.newBuilder() |
| 6124 | + .namespace(namespace1) |
| 6125 | + .table(TABLE_1) |
| 6126 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 6127 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 6128 | + .intValue(BALANCE, 200) |
| 6129 | + .build()); |
| 6130 | + |
| 6131 | + // Retrieve the record again, but use a different Get object (with a where clause) |
| 6132 | + result = |
| 6133 | + transaction.get( |
| 6134 | + Get.newBuilder() |
| 6135 | + .namespace(namespace1) |
| 6136 | + .table(TABLE_1) |
| 6137 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 6138 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 6139 | + .where(column(BALANCE).isEqualToInt(200)) |
| 6140 | + .build()); |
| 6141 | + |
| 6142 | + assertThat(result).isNotPresent(); |
| 6143 | + |
| 6144 | + assertThatThrownBy(transaction::commit).isInstanceOf(CommitConflictException.class); |
| 6145 | + transaction.rollback(); |
| 6146 | + |
| 6147 | + // Assert |
| 6148 | + result = |
| 6149 | + manager.get( |
| 6150 | + Get.newBuilder() |
| 6151 | + .namespace(namespace1) |
| 6152 | + .table(TABLE_1) |
| 6153 | + .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) |
| 6154 | + .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) |
| 6155 | + .build()); |
| 6156 | + |
| 6157 | + assertThat(result).isPresent(); |
| 6158 | + assertThat(result.get().getInt(ACCOUNT_ID)).isEqualTo(0); |
| 6159 | + assertThat(result.get().getInt(ACCOUNT_TYPE)).isEqualTo(0); |
| 6160 | + assertThat(result.get().getInt(BALANCE)).isEqualTo(200); |
| 6161 | + } |
| 6162 | + |
6076 | 6163 | @Test |
6077 | 6164 | public void manager_get_GetGivenForCommittedRecord_WithSerializable_ShouldReturnRecord() |
6078 | 6165 | throws TransactionException { |
|
0 commit comments