Skip to content
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

Java: Add transaction requests for standalone and cluster-mode #70

Conversation

acarbonetto
Copy link

@acarbonetto acarbonetto commented Jan 26, 2024

Description:

Adds transactions for both standalone and cluster clients. This allows users to specify a route (for cluster clients) and a pipeline of commands in a Transaction object. The object is then passed to Redis through the submitNewCommand(Transaction) call.

This PR also adds get/set/ping/info calls to the standalone, cluster, and transactions.

Examples:

// Single commands
RedisClient client = RedisClient.createClient(config).get();
String pingResult = client.ping().get();
client.set("key", "value").get();
String getResult = client.get("key").get(); // returns "value"
String infoResult = client.info().get();

// Cluster-mode single commands
RedisClusterClient clusterClient = RedisClusterClient.createClient(config).get();
String infoResult = clusterClient.info(ALL_NODES).get();

// Transactions
Transaction standaloneTransaction = new Transaction()
    .ping()
    .set("key", "value")
    .get("key")
    .info();
Object[] transactionResult = client.exec(standaloneTransaction).get();

ClusterTransaction clusterTransaction = new ClusterTransaction()
    .ping()
    .set("key", "value")
    .get("key")
    .info();
Object[] transactionResult = clusterClient.exec(clusterTransaction, ALL_NODES).get();

… response (#59)

* Add cluster client and routes support for cluster client.

Signed-off-by: Yury-Fridlyand <[email protected]>

* Address PR feedback and add tests.

Signed-off-by: Yury-Fridlyand <[email protected]>

* Minor javadoc update.

Signed-off-by: Yury-Fridlyand <[email protected]>

* Minor javadoc fix

Signed-off-by: Yury-Fridlyand <[email protected]>

* Address PR review comments.

Signed-off-by: Yury-Fridlyand <[email protected]>

* Address PR review comments.

Signed-off-by: Yury-Fridlyand <[email protected]>

* Address PR review comments.

Signed-off-by: Yury-Fridlyand <[email protected]>

---------

Signed-off-by: Yury-Fridlyand <[email protected]>
@acarbonetto acarbonetto force-pushed the java/dev_acarbo_api_transactions branch from 9a16d86 to 6d308d2 Compare January 29, 2024 17:52
Signed-off-by: Yury-Fridlyand <[email protected]>
@acarbonetto acarbonetto changed the base branch from main to java/integ_acarbo_api_transactions January 30, 2024 08:22
command,
RequestType.CUSTOM_COMMAND,
args,
Optional.ofNullable(route),
response ->
route.isSingleNodeRoute()
? ClusterValue.ofSingleValue(handleObjectResponse(response))
: ClusterValue.ofMultiValue((Map<String, Object>) handleObjectResponse(response)));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you are not actually casting the keys in to strings with this (Map<String, Object>) , at runtime they will still remain as type Object not string. We may need to cast keys individually.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will test this and update the examples in the PR description.

Copy link

@Yury-Fridlyand Yury-Fridlyand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Please groom all javadocs to follow our standard we recently discussed.
  2. Do you want to rebase on top of IT branch and add ITs?
  3. Please address all comments posted in Add GET/SET/INFO/PING commands #66 here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uncomment?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated... these tests became fairly simple.

Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
Copy link
Author

@acarbonetto acarbonetto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add IT tests for this.

Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
@acarbonetto acarbonetto force-pushed the java/integ_acarbo_api_transactions branch from 1ea0060 to 150e0fd Compare February 1, 2024 04:01
Signed-off-by: Andrew Carbonetto <[email protected]>
Copy link

@Yury-Fridlyand Yury-Fridlyand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to change the test approach:

  1. in UT test that client.command causes the correct protobuf request. This will engage frontend-write client part and provide enough code coverage
  2. all rest - in IT

String key = "testKey";
String value = "testValue";
CompletableFuture<Void> testResponse = mock(CompletableFuture.class);
when(testResponse.get()).thenReturn(null);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is no-op. A mock always returns null for all calls unless reconfigured.
but null != void

Suggested change
when(testResponse.get()).thenReturn(null);
when(testResponse.get()).thenReturn(Void);

Is it possible?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no difference, really.

CompletableFuture<ClusterValue<String>> response = service.info(route);

// verify
assertEquals(testResponse, response);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

@@ -69,18 +69,18 @@ public ChannelHandler(
/**
* Complete a protobuf message and write it to the channel (to UDS).
*
* @param request Incomplete request, function completes it by setting callback ID
* @param requestBuilder Incomplete request, function completes it by setting callback ID
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
@Getter
public abstract class BaseTransaction<T extends BaseTransaction<T>> {
/** Command class to send a single request to Redis. */
Transaction.Builder transactionBuilder = Transaction.newBuilder();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace with List<Pair<RequestType, String[]>> and in the prepareRedisRequest, convert the list to a Transaction.Builder.

@@ -73,7 +73,7 @@ public void createClient_with_config_successfully_returns_RedisClient() {

@SneakyThrows
@Test
public void createClient_error_on_connection_throws_ExecutionException() {
public void createClient_throws_ExecutionException() {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

CompletableFuture<Object> testResponse = mock(CompletableFuture.class);
when(testResponse.get()).thenReturn(value);
when(commandManager.submitNewCommand(any(), any())).thenReturn(testResponse);

ArgumentCaptor<RedisRequestOuterClass.RedisRequest.Builder> captor =
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

String key = "testKey";
String value = "testValue";
CompletableFuture<Void> testResponse = mock(CompletableFuture.class);
when(testResponse.get()).thenReturn(null);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no difference, really.

// setup
String key = "testKey";
String value = "testValue";
CompletableFuture<Void> testResponse = mock(CompletableFuture.class);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change this to real object and complete it with null.

response ->
route.isSingleNodeRoute()
? ClusterValue.ofSingleValue(handleObjectResponse(response))
: ClusterValue.ofMultiValue((Map<String, Object>) handleObjectResponse(response)));
}

@Override
public CompletableFuture<Object[]> exec(ClusterTransaction transaction) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to test this:

  1. Is this a list of ClusterValue[], OR
  2. Is this a ClusterValue(Object[])

}

@Override
public CompletableFuture<Object[]> exec(ClusterTransaction transaction, Route route) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

@@ -20,23 +22,31 @@ public interface Route {
boolean isSingleNodeRoute();
}

@RequiredArgsConstructor
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rebase

@@ -40,7 +40,6 @@ public static EventLoopGroup createOrGetNettyThreadPool(
() -> new EpollEventLoopGroup(threadCount, new DefaultThreadFactory(name, true)));
}
// TODO support IO-Uring and NIO

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

@SneakyThrows
@ParameterizedTest
@EnumSource(RequestType.class)
public void submitNewCommand_covers_all_mapRequestTypes(RequestType requestType) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this test

@acarbonetto acarbonetto deleted the java/dev_acarbo_api_transactions branch February 23, 2024 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants