Skip to content
This repository has been archived by the owner on Nov 14, 2024. It is now read-only.

Commit

Permalink
[TTS] InMemory implementation of MCAS (#6243)
Browse files Browse the repository at this point in the history
  • Loading branch information
gmaretic authored Sep 29, 2022
1 parent bd07a88 commit 9587c74
Show file tree
Hide file tree
Showing 8 changed files with 368 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.palantir.atlasdb.keyvalue.api;

import com.palantir.logsafe.Preconditions;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -31,6 +32,11 @@ public interface CheckAndSetCompatibility {
*/
boolean supportsCheckAndSetOperations();

@Value.Default
default boolean supportsMultiCheckAndSetOperations() {
return false;
}

/**
* If false, there are no guarantees that a {@link CheckAndSetException#getActualValues()} or
* {@link KeyAlreadyExistsException#getExistingKeys()} from exceptions thrown by the the {@link KeyValueService}
Expand All @@ -53,6 +59,13 @@ public interface CheckAndSetCompatibility {
*/
boolean consistentOnFailure();

@Value.Check
default void cannotOnlySupportMultiCheckAndSet() {
Preconditions.checkArgument(
supportsCheckAndSetOperations() || !supportsMultiCheckAndSetOperations(),
"Support for MultiCAS implies support for CAS.");
}

static CheckAndSetCompatibility intersect(Stream<CheckAndSetCompatibility> compatibilities) {
Set<CheckAndSetCompatibility> presentCompatibilities = compatibilities.collect(Collectors.toSet());

Expand All @@ -61,10 +74,13 @@ static CheckAndSetCompatibility intersect(Stream<CheckAndSetCompatibility> compa
if (!supported) {
return Unsupported.INSTANCE;
}
boolean multiCas =
presentCompatibilities.stream().allMatch(CheckAndSetCompatibility::supportsMultiCheckAndSetOperations);
boolean detail = presentCompatibilities.stream().allMatch(CheckAndSetCompatibility::supportsDetailOnFailure);
boolean consistency = presentCompatibilities.stream().allMatch(CheckAndSetCompatibility::consistentOnFailure);

return supportedBuilder()
.supportsMultiCheckAndSetOperations(multiCas)
.supportsDetailOnFailure(detail)
.consistentOnFailure(consistency)
.build();
Expand All @@ -86,6 +102,11 @@ public boolean supportsCheckAndSetOperations() {
return false;
}

@Override
public boolean supportsMultiCheckAndSetOperations() {
return false;
}

@Override
public boolean supportsDetailOnFailure() {
throw new UnsupportedOperationException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,8 @@ default boolean supportsCheckAndSet() {
* In this case, you can be sure that all your cells have been updated to their new values.
* In case of failure, there are no guarantees that the operation was not partially applied but the
* implementations may offer such a guarantee.
* Reads concurrent with this operation may see a partially applied update that later succeeds, though
* implementations may offer stronger guarantees.
*
* If a {@link MultiCheckAndSetException} is thrown, it is likely that the values stored in the cells were not as
* you expected.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,32 @@
import org.junit.Test;

public class CheckAndSetCompatibilityTest {
private static final CheckAndSetCompatibility SUPPORTS_DETAIL_NOT_CONSISTENT_ON_FAILURE =
CheckAndSetCompatibility.supportedBuilder()
.supportsDetailOnFailure(true)
.consistentOnFailure(false)
.build();
private static final CheckAndSetCompatibility NO_DETAIL_CONSISTENT_ON_FAILURE =
CheckAndSetCompatibility.supportedBuilder()
.supportsDetailOnFailure(false)
.consistentOnFailure(true)
.build();
private static final CheckAndSetCompatibility SUPPORTS_DETAIL_CONSISTENT_ON_FAILURE =
CheckAndSetCompatibility.supportedBuilder()
.supportsDetailOnFailure(true)
.consistentOnFailure(true)
.build();
private static final CheckAndSetCompatibility DETAIL_ONLY = CheckAndSetCompatibility.supportedBuilder()
.supportsMultiCheckAndSetOperations(false)
.supportsDetailOnFailure(true)
.consistentOnFailure(false)
.build();
private static final CheckAndSetCompatibility CONSISTENT_ONLY = CheckAndSetCompatibility.supportedBuilder()
.supportsMultiCheckAndSetOperations(false)
.supportsDetailOnFailure(false)
.consistentOnFailure(true)
.build();
private static final CheckAndSetCompatibility MCAS_ONLY = CheckAndSetCompatibility.supportedBuilder()
.supportsMultiCheckAndSetOperations(true)
.supportsDetailOnFailure(false)
.consistentOnFailure(false)
.build();
private static final CheckAndSetCompatibility DETAIL_AND_CONSISTENT = CheckAndSetCompatibility.supportedBuilder()
.supportsMultiCheckAndSetOperations(false)
.supportsDetailOnFailure(true)
.consistentOnFailure(true)
.build();

private static final CheckAndSetCompatibility MCAS_DETAIL_CONSISTENT = CheckAndSetCompatibility.supportedBuilder()
.supportsMultiCheckAndSetOperations(true)
.supportsDetailOnFailure(true)
.consistentOnFailure(true)
.build();

@Test
public void checkingDetailSupportedOnUnsupportedThrows() {
Expand All @@ -59,9 +70,16 @@ public void unsupportedDoesNotSupportCheckAndSetOperations() {
.isFalse();
}

@Test
public void unsupportedDoesNotSupportMultiCheckAndSetOperations() {
assertThat(CheckAndSetCompatibility.unsupported().supportsMultiCheckAndSetOperations())
.isFalse();
}

@Test
public void supportedBuilderSupportsCheckAndSetOperations() {
assertThat(CheckAndSetCompatibility.supportedBuilder()
.supportsMultiCheckAndSetOperations(false)
.supportsDetailOnFailure(false)
.consistentOnFailure(false)
.build()
Expand All @@ -73,53 +91,63 @@ public void supportedBuilderSupportsCheckAndSetOperations() {
public void intersectReturnsLeastRestrictiveWhenNoCompatibilitiesProvided() {
CheckAndSetCompatibility intersection = intersectCompatibility();
assertThat(intersection.supportsCheckAndSetOperations()).isTrue();
assertThat(intersection.supportsMultiCheckAndSetOperations()).isTrue();
assertThat(intersection.supportsDetailOnFailure()).isTrue();
assertThat(intersection.consistentOnFailure()).isTrue();
}

@Test
public void intersectAppliesToBothProperties() {
public void intersectAppliesToAllProperties() {
CheckAndSetCompatibility intersection = intersectCompatibility(
SUPPORTS_DETAIL_NOT_CONSISTENT_ON_FAILURE,
NO_DETAIL_CONSISTENT_ON_FAILURE,
SUPPORTS_DETAIL_CONSISTENT_ON_FAILURE);
DETAIL_ONLY, CONSISTENT_ONLY, MCAS_ONLY, DETAIL_AND_CONSISTENT, MCAS_DETAIL_CONSISTENT);
assertThat(intersection.supportsCheckAndSetOperations()).isTrue();
assertThat(intersection.supportsMultiCheckAndSetOperations()).isFalse();
assertThat(intersection.supportsDetailOnFailure()).isFalse();
assertThat(intersection.consistentOnFailure()).isFalse();
}

@Test
public void intersectReturnsDetailSupportedWhenAllSupport() {
CheckAndSetCompatibility intersection = intersectCompatibility(
SUPPORTS_DETAIL_NOT_CONSISTENT_ON_FAILURE, SUPPORTS_DETAIL_CONSISTENT_ON_FAILURE);
CheckAndSetCompatibility intersection = intersectCompatibility(DETAIL_ONLY, DETAIL_AND_CONSISTENT);
assertThat(intersection.supportsCheckAndSetOperations()).isTrue();
assertThat(intersection.supportsMultiCheckAndSetOperations()).isFalse();
assertThat(intersection.supportsDetailOnFailure()).isTrue();
assertThat(intersection.consistentOnFailure()).isFalse();
}

@Test
public void intersectReturnsConsistentWhenAllConsistent() {
CheckAndSetCompatibility intersection =
intersectCompatibility(SUPPORTS_DETAIL_CONSISTENT_ON_FAILURE, NO_DETAIL_CONSISTENT_ON_FAILURE);
CheckAndSetCompatibility intersection = intersectCompatibility(DETAIL_AND_CONSISTENT, CONSISTENT_ONLY);
assertThat(intersection.supportsCheckAndSetOperations()).isTrue();
assertThat(intersection.supportsMultiCheckAndSetOperations()).isFalse();
assertThat(intersection.supportsDetailOnFailure()).isFalse();
assertThat(intersection.consistentOnFailure()).isTrue();
}

@Test
public void intersectReturnsMultiCheckAndSetOperationsSupportedWhenAllSupport() {
CheckAndSetCompatibility intersection = intersectCompatibility(MCAS_ONLY, MCAS_DETAIL_CONSISTENT);
assertThat(intersection.supportsCheckAndSetOperations()).isTrue();
assertThat(intersection.supportsMultiCheckAndSetOperations()).isTrue();
assertThat(intersection.supportsDetailOnFailure()).isFalse();
assertThat(intersection.consistentOnFailure()).isFalse();
}

@Test
public void intersectDoesNotRestrictUnnecessarily() {
CheckAndSetCompatibility intersection =
intersectCompatibility(SUPPORTS_DETAIL_CONSISTENT_ON_FAILURE, SUPPORTS_DETAIL_CONSISTENT_ON_FAILURE);
CheckAndSetCompatibility intersection = intersectCompatibility(MCAS_DETAIL_CONSISTENT, MCAS_DETAIL_CONSISTENT);
assertThat(intersection.supportsCheckAndSetOperations()).isTrue();
assertThat(intersection.supportsMultiCheckAndSetOperations()).isTrue();
assertThat(intersection.supportsDetailOnFailure()).isTrue();
assertThat(intersection.consistentOnFailure()).isTrue();
}

@Test
public void intersectWithUnsupportedIsUnsupported() {
CheckAndSetCompatibility intersection =
intersectCompatibility(SUPPORTS_DETAIL_CONSISTENT_ON_FAILURE, CheckAndSetCompatibility.unsupported());
intersectCompatibility(DETAIL_AND_CONSISTENT, CheckAndSetCompatibility.unsupported());
assertThat(intersection.supportsCheckAndSetOperations()).isFalse();
assertThat(intersection.supportsMultiCheckAndSetOperations()).isFalse();
}

private CheckAndSetCompatibility intersectCompatibility(CheckAndSetCompatibility... compatibilities) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1936,6 +1936,7 @@ private static Column prepareColumnForPutUnlessExists(Map.Entry<Cell, byte[]> in
@Override
public CheckAndSetCompatibility getCheckAndSetCompatibility() {
return CheckAndSetCompatibility.supportedBuilder()
.supportsMultiCheckAndSetOperations(true)
.supportsDetailOnFailure(true)
.consistentOnFailure(false)
.build();
Expand Down Expand Up @@ -1980,6 +1981,7 @@ public void checkAndSet(final CheckAndSetRequest request) throws CheckAndSetExce
* In this case, you can be sure that all your cells have been updated to their new values.
* If the old cells initially did not have the values you expected, none of the cells will be updated and
* {@link MultiCheckAndSetException} will be thrown.
* Reads concurrent with this operation will not see a partial update.
*
* Another thing to note is that the check operation will **only be performed on values of cells that are declared
* in the set of expected values** i.e. the check operation DOES NOT take updates into account.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ protected static ExecutorService createThreadPool(String threadNamePrefix, int _
@Override
public CheckAndSetCompatibility getCheckAndSetCompatibility() {
return CheckAndSetCompatibility.supportedBuilder()
.supportsMultiCheckAndSetOperations(false)
.consistentOnFailure(true)
.supportsDetailOnFailure(false)
.build();
Expand Down
Loading

0 comments on commit 9587c74

Please sign in to comment.