forked from valkey-io/valkey-glide
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add cluster client, request routes configuration and support for bulk…
… 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]>
- Loading branch information
1 parent
888109e
commit 6d180ba
Showing
3 changed files
with
221 additions
and
2 deletions.
There are no files selected for viewing
126 changes: 126 additions & 0 deletions
126
java/client/src/main/java/glide/api/models/configuration/Route.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package glide.api.models.configuration; | ||
|
||
import java.util.Optional; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
/** Request routing configuration. */ | ||
public class Route { | ||
|
||
public enum RouteType { | ||
/** Route request to all nodes. */ | ||
ALL_NODES, | ||
/** Route request to all primary nodes. */ | ||
ALL_PRIMARIES, | ||
/** Route request to a random node. */ | ||
RANDOM, | ||
/** Route request to the primary node that contains the slot with the given id. */ | ||
PRIMARY_SLOT_ID, | ||
/** Route request to the replica node that contains the slot with the given id. */ | ||
REPLICA_SLOT_ID, | ||
/** Route request to the primary node that contains the slot that the given key matches. */ | ||
PRIMARY_SLOT_KEY, | ||
/** Route request to the replica node that contains the slot that the given key matches. */ | ||
REPLICA_SLOT_KEY, | ||
} | ||
|
||
@Getter private final RouteType routeType; | ||
|
||
private final Optional<Integer> slotId; | ||
|
||
private final Optional<String> slotKey; | ||
|
||
public Integer getSlotId() { | ||
assert slotId.isPresent(); | ||
return slotId.get(); | ||
} | ||
|
||
public String getSlotKey() { | ||
assert slotKey.isPresent(); | ||
return slotKey.get(); | ||
} | ||
|
||
private Route(RouteType routeType, Integer slotId) { | ||
this.routeType = routeType; | ||
this.slotId = Optional.of(slotId); | ||
this.slotKey = Optional.empty(); | ||
} | ||
|
||
private Route(RouteType routeType, String slotKey) { | ||
this.routeType = routeType; | ||
this.slotId = Optional.empty(); | ||
this.slotKey = Optional.of(slotKey); | ||
} | ||
|
||
private Route(RouteType routeType) { | ||
this.routeType = routeType; | ||
this.slotId = Optional.empty(); | ||
this.slotKey = Optional.empty(); | ||
} | ||
|
||
public static class Builder { | ||
private final RouteType routeType; | ||
private int slotId; | ||
private boolean slotIdSet = false; | ||
private String slotKey; | ||
private boolean slotKeySet = false; | ||
|
||
/** | ||
* Request routing configuration overrides the {@link ReadFrom} connection configuration.<br> | ||
* If {@link RouteType#REPLICA_SLOT_ID} or {@link RouteType#REPLICA_SLOT_KEY} is used, the | ||
* request will be routed to a replica, even if the strategy is {@link ReadFrom#PRIMARY}. | ||
*/ | ||
public Builder(RouteType routeType) { | ||
this.routeType = routeType; | ||
} | ||
|
||
/** | ||
* Slot number. There are 16384 slots in a redis cluster, and each shard manages a slot range. | ||
* Unless the slot is known, it's better to route using {@link RouteType#PRIMARY_SLOT_KEY} or | ||
* {@link RouteType#REPLICA_SLOT_KEY}.<br> | ||
* Could be used with {@link RouteType#PRIMARY_SLOT_ID} or {@link RouteType#REPLICA_SLOT_ID} | ||
* only. | ||
*/ | ||
public Builder setSlotId(int slotId) { | ||
if (!(routeType == RouteType.PRIMARY_SLOT_ID || routeType == RouteType.REPLICA_SLOT_ID)) { | ||
throw new IllegalArgumentException( | ||
"Slot ID could be set for corresponding types of route only"); | ||
} | ||
this.slotId = slotId; | ||
slotIdSet = true; | ||
return this; | ||
} | ||
|
||
/** | ||
* The request will be sent to nodes managing this key.<br> | ||
* Could be used with {@link RouteType#PRIMARY_SLOT_KEY} or {@link RouteType#REPLICA_SLOT_KEY} | ||
* only. | ||
*/ | ||
public Builder setSlotKey(String slotKey) { | ||
if (!(routeType == RouteType.PRIMARY_SLOT_KEY || routeType == RouteType.REPLICA_SLOT_KEY)) { | ||
throw new IllegalArgumentException( | ||
"Slot key could be set for corresponding types of route only"); | ||
} | ||
this.slotKey = slotKey; | ||
slotKeySet = true; | ||
return this; | ||
} | ||
|
||
public Route build() { | ||
if (routeType == RouteType.PRIMARY_SLOT_ID || routeType == RouteType.REPLICA_SLOT_ID) { | ||
if (!slotIdSet) { | ||
throw new IllegalArgumentException("Slot ID is missing"); | ||
} | ||
return new Route(routeType, slotId); | ||
} | ||
if (routeType == RouteType.PRIMARY_SLOT_KEY || routeType == RouteType.REPLICA_SLOT_KEY) { | ||
if (!slotKeySet) { | ||
throw new IllegalArgumentException("Slot key is missing"); | ||
} | ||
return new Route(routeType, slotKey); | ||
} | ||
|
||
return new Route(routeType); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
java/client/src/test/java/glide/api/models/RouteBuilderTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package glide.api.models; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertAll; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
|
||
import glide.api.models.configuration.Route; | ||
import glide.api.models.configuration.Route.RouteType; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.EnumSource; | ||
|
||
public class RouteBuilderTests { | ||
|
||
@ParameterizedTest | ||
@EnumSource( | ||
value = RouteType.class, | ||
names = {"PRIMARY_SLOT_ID", "REPLICA_SLOT_ID"}) | ||
public void slot_id_is_required(RouteType routeType) { | ||
var exception = | ||
assertThrows(IllegalArgumentException.class, () -> new Route.Builder(routeType).build()); | ||
assertEquals("Slot ID is missing", exception.getMessage()); | ||
} | ||
|
||
@ParameterizedTest | ||
@EnumSource( | ||
value = RouteType.class, | ||
names = {"PRIMARY_SLOT_KEY", "REPLICA_SLOT_KEY"}) | ||
public void slot_key_is_required(RouteType routeType) { | ||
var exception = | ||
assertThrows(IllegalArgumentException.class, () -> new Route.Builder(routeType).build()); | ||
assertEquals("Slot key is missing", exception.getMessage()); | ||
} | ||
|
||
@ParameterizedTest | ||
@EnumSource( | ||
value = RouteType.class, | ||
names = {"PRIMARY_SLOT_KEY", "REPLICA_SLOT_KEY", "ALL_NODES", "ALL_PRIMARIES", "RANDOM"}) | ||
public void slot_id_not_acceptable(RouteType routeType) { | ||
var exception = | ||
assertThrows( | ||
IllegalArgumentException.class, () -> new Route.Builder(routeType).setSlotId(42)); | ||
assertEquals( | ||
"Slot ID could be set for corresponding types of route only", exception.getMessage()); | ||
} | ||
|
||
@ParameterizedTest | ||
@EnumSource( | ||
value = RouteType.class, | ||
names = {"PRIMARY_SLOT_ID", "REPLICA_SLOT_ID", "ALL_NODES", "ALL_PRIMARIES", "RANDOM"}) | ||
public void slot_key_not_acceptable(RouteType routeType) { | ||
var exception = | ||
assertThrows( | ||
IllegalArgumentException.class, () -> new Route.Builder(routeType).setSlotKey("D'oh")); | ||
assertEquals( | ||
"Slot key could be set for corresponding types of route only", exception.getMessage()); | ||
} | ||
|
||
@ParameterizedTest | ||
@EnumSource( | ||
value = RouteType.class, | ||
names = {"PRIMARY_SLOT_ID", "REPLICA_SLOT_ID"}) | ||
public void build_with_slot_id(RouteType routeType) { | ||
var route = new Route.Builder(routeType).setSlotId(42).build(); | ||
assertAll( | ||
() -> assertEquals(routeType, route.getRouteType()), | ||
() -> assertEquals(42, route.getSlotId()), | ||
() -> assertThrows(Throwable.class, () -> route.getSlotKey())); | ||
} | ||
|
||
@ParameterizedTest | ||
@EnumSource( | ||
value = RouteType.class, | ||
names = {"PRIMARY_SLOT_KEY", "REPLICA_SLOT_KEY"}) | ||
public void build_with_slot_key(RouteType routeType) { | ||
var route = new Route.Builder(routeType).setSlotKey("test").build(); | ||
assertAll( | ||
() -> assertEquals(routeType, route.getRouteType()), | ||
() -> assertEquals("test", route.getSlotKey()), | ||
() -> assertThrows(Throwable.class, () -> route.getSlotId())); | ||
} | ||
|
||
@ParameterizedTest | ||
@EnumSource( | ||
value = RouteType.class, | ||
names = {"ALL_NODES", "ALL_PRIMARIES", "RANDOM"}) | ||
public void build_simple_route(RouteType routeType) { | ||
var route = new Route.Builder(routeType).build(); | ||
assertAll( | ||
() -> assertEquals(routeType, route.getRouteType()), | ||
() -> assertThrows(Throwable.class, () -> route.getSlotKey()), | ||
() -> assertThrows(Throwable.class, () -> route.getSlotId())); | ||
} | ||
} |