-
Notifications
You must be signed in to change notification settings - Fork 68
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: API layer with RedisClient #737
Java: API layer with RedisClient #737
Conversation
import java.util.concurrent.ExecutionException; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
|
||
/** Factory class for creating Glide/Redis-client connections */ |
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.
this class is the actual connection, not just the factory.
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.
It should say it's the standalone redis client
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.
Updated in d23001f.
// verify | ||
// no exception | ||
assertNull(result.get()); | ||
verify(channel).connect(eq(expectedProtobufConnectionRequest)); |
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.
I don't understand what these tests actually test. What kind of behavior breakage will this test catch?
I don't see the value for quite a few of these tests. They seem to just be passthrough for the mock,
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.
They test that the protobuf connection request is built as expected.
They will break if the protobuf connection requests change in a way that is unexpected.
} | ||
|
||
@Test | ||
public void CheckBabushkaResponse_ConstantResponse_returnsSuccessfully() |
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.
where are you testing that the constant response was passed to the user?
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.
Note: On a connection, the user receives a RedisClient with a completed CompletableFuture. They don't see the constant response. If we did that, we would have to change our CreateClient method.
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.
I will add unit tests for CreateClient.
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.
Tests added under 09fd5c7
private final AtomicInteger requestId = new AtomicInteger(0); | ||
|
||
/** | ||
* Storage of Futures to handle responses. Map key is callback id, which starts from 1.<br> |
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.
remove
throughout
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.
Updated in d23001f.
java/client/src/main/java/glide/managers/ConnectionManager.java
Outdated
Show resolved
Hide resolved
java/client/src/main/java/glide/managers/ConnectionManager.java
Outdated
Show resolved
Hide resolved
import java.util.concurrent.ExecutionException; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
|
||
/** Factory class for creating Glide/Redis-client connections */ |
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.
It should say it's the standalone redis client
} | ||
|
||
/** | ||
* Async (non-blocking) connection to Redis. |
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.
should be changed to something like:
Creates an async (non-blocking) client for Redis in Standalone mode.
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.
in general, the user-facing term we use is client, not connection. That is, unless we're discussing a concrete connection to a single node, or the UDS connection (which is mostly discussed internally and isn't user facing).
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.
Updated in d23001f.
import lombok.Singular; | ||
import lombok.experimental.SuperBuilder; | ||
|
||
/** Represents the configuration settings for a Redis client. */ |
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.
should be changed to something like-
Represents the shared configuration settings for both Standalone and Cluster Redis clients.
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.
Updated in d23001f.
|
||
CompletableFuture<Response> channel; | ||
|
||
public CompletableFuture<Void> closeConnection() { |
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.
Adding to Shachar's question - why CommandManager has closeConnection function?
} else if (response.hasConstantResponse()) { | ||
return null; | ||
} else if (response.hasRespPointer()) { | ||
throw new RuntimeException("Unexpected data in response"); |
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.
why?
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.
In a connection request, we only handle the error scenario and the OK successful response. This would be unexpected... I don't know what to do with the data...
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.
In conversation today, we decided that a ClosingException would be best (and cancel all the waiting responses).
I'll add a TODO and fix it under https://github.com/orgs/Bit-Quill/projects/4/views/6?pane=issue&itemId=48063887
} else if (response.hasRespPointer()) { | ||
throw new RuntimeException("Unexpected data in response"); | ||
} | ||
// TODO commented out due to #710 https://github.com/aws/babushka/issues/710 |
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.
can we remove this todo?
This PR changing connection response to OK was merged:
#711
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.
Updated in d23001f.
* Add java client connection layer. Signed-off-by: Andrew Carbonetto <[email protected]> --------- Signed-off-by: Yury-Fridlyand <[email protected]> Signed-off-by: Andrew Carbonetto <[email protected]> Co-authored-by: Yury-Fridlyand <[email protected]> Co-authored-by: SanHalacogluImproving <[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]>
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]>
09fd5c7
to
617ab3e
Compare
Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
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.
round
java/client/src/main/java/glide/api/models/configuration/BaseClientConfiguration.java
Outdated
Show resolved
Hide resolved
java/client/src/test/java/glide/managers/ConnectionManagerTest.java
Outdated
Show resolved
Hide resolved
java/client/src/test/java/glide/managers/ConnectionManagerTest.java
Outdated
Show resolved
Hide resolved
java/client/src/test/java/glide/managers/ConnectionManagerTest.java
Outdated
Show resolved
Hide resolved
java/client/src/test/java/glide/managers/ConnectionManagerTest.java
Outdated
Show resolved
Hide resolved
Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
connectionManager.closeConnection().get(); | ||
} catch (InterruptedException interruptedException) { | ||
// AutoCloseable functions are strongly advised to avoid throwing InterruptedExceptions | ||
// TODO: marking resources as closed: |
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.
restore interrupt
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.
I'm not sure this is right.
see: https://github.com/orgs/Bit-Quill/projects/4/views/6?pane=issue&itemId=48063887
Ultimately, it would be best to throw a RedisException/ClosingException after interrupting on all existing responses.
Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
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.
Minor things left. Do you have uncommited changes? I had to unresolve few things, because they are not fixed yet.
java/client/src/main/java/glide/managers/ConnectionManager.java
Outdated
Show resolved
Hide resolved
java/client/src/main/java/glide/managers/ConnectionManager.java
Outdated
Show resolved
Hide resolved
java/client/src/main/java/glide/managers/ConnectionManager.java
Outdated
Show resolved
Hide resolved
java/client/src/main/java/glide/managers/ConnectionManager.java
Outdated
Show resolved
Hide resolved
java/client/src/main/java/glide/managers/ConnectionManager.java
Outdated
Show resolved
Hide resolved
java/client/src/main/java/glide/managers/ConnectionManager.java
Outdated
Show resolved
Hide resolved
java/client/src/main/java/glide/managers/ConnectionManager.java
Outdated
Show resolved
Hide resolved
Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
// TODO: Support exception throwing, including interrupted exceptions | ||
return connectionManager | ||
.connectToRedis(config) | ||
.thenApply(ignore -> new RedisClient(connectionManager, commandManager)); |
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.
.thenApply(ignore -> new RedisClient(connectionManager, commandManager)); | |
.thenApplyAsync(ignore -> new RedisClient(connectionManager, commandManager)); |
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.
Is it necessary to run this in a separate thread?
ChannelHandler channelHandler = buildChannelHandler(); | ||
ConnectionManager connectionManager = buildConnectionManager(channelHandler); | ||
CommandManager commandManager = buildCommandManager(channelHandler); | ||
// TODO: Support exception throwing, including interrupted exceptions |
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.
I think all exeptions are properly propagated from that async pipeline. They are wrapped by ExectionException
. Please, re-check. Maybe you can remove this TODO and/or add a test.
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.
We have a task to add exception handling. It would be 'nicer' if the API supported throwing RedisException, rather than an ExecutionException with a RedisException.
I'm pretty certain we can extend CompletableFutures to handle RedisException, in parallel with ExcutionExceptions and that would provide a better experience for our users.
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.
Also, tests are already included in the follow-up PR, that adds RedisExceptions.
java/client/src/main/java/glide/connectors/handlers/ChannelHandler.java
Outdated
Show resolved
Hide resolved
if (msg instanceof Response) { | ||
Response response = (Response) msg; | ||
callbackDispatcher.completeRequest(response); | ||
ctx.fireChannelRead(msg); |
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.
why not fireChannelReadComplete
?
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.
Because that doesn't pass the message to other read handlers. That tells the read pipeline to complete, I believe (documentation on that method is somewhat lacking).
Signed-off-by: Andrew Carbonetto <[email protected]>
6fae6dc
to
31bc4c5
Compare
Signed-off-by: Andrew Carbonetto <[email protected]>
* | ||
* @return a Future to connect and return a RedisClient | ||
*/ | ||
public static CompletableFuture<RedisClient> CreateClient() { |
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.
please don't add new public functions that don't exist in other wrappers without discussing it with us.
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.
Removed in 0c7d03b
import lombok.experimental.SuperBuilder; | ||
|
||
/** | ||
* Configuration settings class for creating a Redis Client. Shared settings for both a standalone |
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.
minor: correct me if I'm wrong, but isn't it "for both standalone [...]" with the "a"?
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.
fixed in 0c7d03b
|
||
@Test | ||
@SneakyThrows | ||
public void createClient_withConfig_successfullyReturnsRedisClient() { |
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.
I still don't see the value in these tests, but if you insist on having them 🤷
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.
Understood. I think it's useful to have API unit tests that solidify the API contract with the user, even if it's through a static interface.
Signed-off-by: Andrew Carbonetto <[email protected]>
Signed-off-by: Andrew Carbonetto <[email protected]>
Depends on #717.
Issue #, if available:
API layer for: #589
Description of changes:
In this PR, we're adding an API layer that creates a RedisClient async-ly using a RedisConfiguration. It then connects to Redis using the ConnectionManager. There are several builders in the RedisClient that use different configurations (localhost, host and port, or a full config object).
Out of Scope
Use cases
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.