-
Notifications
You must be signed in to change notification settings - Fork 40
Handle Get with index correctly in CrudHandler #2700
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ | |
| import java.util.List; | ||
| import java.util.Optional; | ||
| import java.util.stream.Collectors; | ||
| import javax.annotation.Nullable; | ||
| import javax.annotation.concurrent.NotThreadSafe; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
@@ -76,17 +77,27 @@ public CrudHandler( | |
| public Optional<Result> get(Get originalGet) throws CrudException { | ||
| List<String> originalProjections = new ArrayList<>(originalGet.getProjections()); | ||
| Get get = (Get) prepareStorageSelection(originalGet); | ||
| Snapshot.Key key = new Snapshot.Key(get); | ||
| readUnread(key, get); | ||
|
|
||
| TableMetadata metadata = getTableMetadata(get); | ||
|
|
||
| Snapshot.Key key; | ||
| if (ScalarDbUtils.isSecondaryIndexSpecified(get, metadata)) { | ||
| // In case of a Get with index, we don't know the key until we read the record | ||
| key = null; | ||
| } else { | ||
| key = new Snapshot.Key(get); | ||
brfrn169 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| readUnread(key, get); | ||
|
|
||
| return snapshot | ||
| .getResult(key, get) | ||
| .map(r -> new FilteredResult(r, originalProjections, metadata, isIncludeMetadataEnabled)); | ||
| } | ||
|
|
||
| // Only for a Get with index, the argument `key` is null | ||
| @VisibleForTesting | ||
| void readUnread(Snapshot.Key key, Get get) throws CrudException { | ||
| void readUnread(@Nullable Snapshot.Key key, Get get) throws CrudException { | ||
| if (!snapshot.containsKeyInGetSet(get)) { | ||
| read(key, get); | ||
| } | ||
|
|
@@ -95,7 +106,7 @@ void readUnread(Snapshot.Key key, Get get) throws CrudException { | |
| // Although this class is not thread-safe, this method is actually thread-safe, so we call it | ||
| // concurrently in the implicit pre-read | ||
| @VisibleForTesting | ||
| void read(Snapshot.Key key, Get get) throws CrudException { | ||
| void read(@Nullable Snapshot.Key key, Get get) throws CrudException { | ||
| Optional<TransactionResult> result = getFromStorage(get); | ||
| if (!result.isPresent() || result.get().isCommitted()) { | ||
| if (result.isPresent() || get.getConjunctions().isEmpty()) { | ||
|
|
@@ -104,7 +115,18 @@ void read(Snapshot.Key key, Get get) throws CrudException { | |
| // transaction read it first. However, we update it only if a get operation has no | ||
| // conjunction or the result exists. This is because we don’t know whether the record | ||
| // actually exists or not due to the conjunction. | ||
| snapshot.putIntoReadSet(key, result); | ||
| if (key != null) { | ||
| snapshot.putIntoReadSet(key, result); | ||
| } else { | ||
| // Only for a Get with index, the argument `key` is null | ||
|
|
||
| if (result.isPresent()) { | ||
| // Only when we can get the record with the Get with index, we can put it into the read | ||
| // set | ||
| key = new Snapshot.Key(get, result.get()); | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Create a |
||
| snapshot.putIntoReadSet(key, result); | ||
| } | ||
| } | ||
| } | ||
| snapshot.putIntoGetSet(get, result); // for re-read and validation | ||
| return; | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -720,6 +720,13 @@ public Key(Get get) { | |||||||||||||||||||||||||||||
| this((Operation) get); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
| /** | |
| * Constructs a snapshot key from an index-based {@link Get} operation and its corresponding {@link Result}. | |
| * | |
| * <p>The following fields are used: | |
| * <ul> | |
| * <li>{@code namespace} and {@code table} are extracted from the {@link Get} operation.</li> | |
| * <li>{@code partitionKey} and {@code clusteringKey} are extracted from the {@link Result}.</li> | |
| * </ul> | |
| * | |
| * @param get the {@link Get} operation representing the index-based query | |
| * @param result the {@link Result} containing the actual data for the key | |
| */ |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -4351,6 +4351,149 @@ public void get_GetWithIndexGiven_WithSerializable_ShouldNotThrowAnyException() | |||||
| assertThatThrownBy(transaction::commit).isInstanceOf(CommitConflictException.class); | ||||||
| } | ||||||
|
|
||||||
| @Test | ||||||
| public void getAndUpdate_GetWithIndexGiven_ShouldUpdate() throws TransactionException { | ||||||
| // Arrange | ||||||
| manager.insert( | ||||||
| Insert.newBuilder() | ||||||
| .namespace(namespace1) | ||||||
| .table(TABLE_1) | ||||||
| .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) | ||||||
| .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) | ||||||
| .intValue(BALANCE, INITIAL_BALANCE) | ||||||
| .build()); | ||||||
|
|
||||||
| // Act Assert | ||||||
| DistributedTransaction transaction = manager.begin(); | ||||||
| Optional<Result> actual = | ||||||
| transaction.get( | ||||||
| Get.newBuilder() | ||||||
| .namespace(namespace1) | ||||||
| .table(TABLE_1) | ||||||
| .indexKey(Key.ofInt(BALANCE, INITIAL_BALANCE)) | ||||||
| .build()); | ||||||
|
|
||||||
| assertThat(actual).isPresent(); | ||||||
| assertThat(actual.get().getInt(ACCOUNT_ID)).isEqualTo(0); | ||||||
| assertThat(actual.get().getInt(ACCOUNT_TYPE)).isEqualTo(0); | ||||||
| assertThat(actual.get().getInt(BALANCE)).isEqualTo(INITIAL_BALANCE); | ||||||
|
|
||||||
| transaction.update( | ||||||
| Update.newBuilder() | ||||||
| .namespace(namespace1) | ||||||
| .table(TABLE_1) | ||||||
| .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) | ||||||
| .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) | ||||||
| .intValue(BALANCE, 1) | ||||||
| .build()); | ||||||
|
|
||||||
| transaction.commit(); | ||||||
|
|
||||||
| transaction = manager.begin(); | ||||||
| actual = | ||||||
| transaction.get( | ||||||
| Get.newBuilder() | ||||||
| .namespace(namespace1) | ||||||
| .table(TABLE_1) | ||||||
| .partitionKey(Key.ofInt(ACCOUNT_ID, 0)) | ||||||
| .clusteringKey(Key.ofInt(ACCOUNT_TYPE, 0)) | ||||||
| .build()); | ||||||
| transaction.commit(); | ||||||
|
|
||||||
| assertThat(actual).isPresent(); | ||||||
| assertThat(actual.get().getInt(ACCOUNT_ID)).isEqualTo(0); | ||||||
| assertThat(actual.get().getInt(ACCOUNT_TYPE)).isEqualTo(0); | ||||||
| assertThat(actual.get().getInt(BALANCE)).isEqualTo(1); | ||||||
| } | ||||||
|
|
||||||
| @Test | ||||||
| public void scanAndUpdate_ScanWithIndexGiven_ShouldUpdate() throws TransactionException { | ||||||
| // Arrange | ||||||
| manager.mutate( | ||||||
| Arrays.asList( | ||||||
|
||||||
| Arrays.asList( | |
| List.of( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For Get with index, set
keyto null at this point.