Skip to content

Commit

Permalink
[CONJ-992] load balance distribution now use round robin, ensuring be…
Browse files Browse the repository at this point in the history
…tter distribution than previous random order
  • Loading branch information
rusher committed Oct 28, 2022
1 parent d6fa61b commit 0fbe892
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 31 deletions.
3 changes: 1 addition & 2 deletions src/main/java/org/mariadb/jdbc/Configuration.java
Expand Up @@ -10,7 +10,6 @@
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.*;

import org.mariadb.jdbc.export.HaMode;
import org.mariadb.jdbc.export.SslMode;
import org.mariadb.jdbc.plugin.Codec;
Expand Down Expand Up @@ -666,7 +665,7 @@ private static void mapPropertiesToOption(Builder builder, Properties properties
if (isSet("useSsl", nonMappedOptions) || isSet("useSSL", nonMappedOptions)) {
Properties deprecatedDesc = new Properties();
try (InputStream inputStream =
Driver.class.getClassLoader().getResourceAsStream("deprecated.properties")) {
Driver.class.getClassLoader().getResourceAsStream("deprecated.properties")) {
deprecatedDesc.load(inputStream);
} catch (IOException io) {
// eat
Expand Down
61 changes: 45 additions & 16 deletions src/main/java/org/mariadb/jdbc/export/HaMode.java
Expand Up @@ -16,7 +16,7 @@ public Optional<HostAddress> getAvailableHost(
List<HostAddress> hostAddresses,
ConcurrentMap<HostAddress, Long> denyList,
boolean primary) {
return HaMode.getAvailableHostRandomOrder(hostAddresses, denyList, primary);
return HaMode.getAvailableRoundRobinHost(this, hostAddresses, denyList, primary);
}
},
/** sequential: driver will always connect according to connection string order */
Expand All @@ -28,13 +28,16 @@ public Optional<HostAddress> getAvailableHost(
return getAvailableHostInOrder(hostAddresses, denyList, primary);
}
},
/** load-balance: driver will randomly connect to any host, permitting balancing connections */
/**
* load-balance: driver will connect to any host using round-robin, permitting balancing
* connections
*/
LOADBALANCE("load-balance") {
public Optional<HostAddress> getAvailableHost(
List<HostAddress> hostAddresses,
ConcurrentMap<HostAddress, Long> denyList,
boolean primary) {
return HaMode.getAvailableHostRandomOrder(hostAddresses, denyList, primary);
return HaMode.getAvailableRoundRobinHost(this, hostAddresses, denyList, primary);
}
},
/** no ha-mode. Connect to first host only */
Expand All @@ -48,6 +51,8 @@ public Optional<HostAddress> getAvailableHost(
};

private final String value;
private HostAddress lastRoundRobinPrimaryHost = null;
private HostAddress lastRoundRobinSecondaryHost = null;

HaMode(String value) {
this.value = value;
Expand Down Expand Up @@ -97,27 +102,51 @@ public static Optional<HostAddress> getAvailableHostInOrder(

/**
* return hosts of corresponding type (primary or not) without blacklisted hosts. hosts in
* blacklist reaching blacklist timeout will be present. Order is random.
* blacklist reaching blacklist timeout will be present, RoundRobin Order.
*
* @param haMode current haMode
* @param hostAddresses hosts
* @param denyList blacklist
* @param primary returns primary hosts or replica
* @return list without denied hosts
*/
public static Optional<HostAddress> getAvailableHostRandomOrder(
List<HostAddress> hostAddresses, ConcurrentMap<HostAddress, Long> denyList, boolean primary) {
// use in order not blacklisted server
List<HostAddress> loopAddress = new ArrayList<>(hostAddresses);

// ensure denied server have not reached denied timeout
denyList.entrySet().stream()
.filter(e -> e.getValue() < System.currentTimeMillis())
.forEach(e -> denyList.remove(e.getKey()));
public static Optional<HostAddress> getAvailableRoundRobinHost(
HaMode haMode,
List<HostAddress> hostAddresses,
ConcurrentMap<HostAddress, Long> denyList,
boolean primary) {
HostAddress lastChosenHost =
primary ? haMode.lastRoundRobinPrimaryHost : haMode.lastRoundRobinSecondaryHost;

loopAddress.removeAll(denyList.keySet());
List<HostAddress> loopList;
if (lastChosenHost == null) {
loopList = hostAddresses;
} else {
int lastChosenIndex = hostAddresses.indexOf(lastChosenHost);
loopList = new ArrayList<>();
loopList.addAll(hostAddresses.subList(lastChosenIndex + 1, hostAddresses.size()));
loopList.addAll(hostAddresses.subList(0, lastChosenIndex + 1));
}

Collections.shuffle(loopAddress);
return loopAddress.stream().filter(e -> e.primary == primary).findFirst();
for (HostAddress hostAddress : loopList) {
if (hostAddress.primary == primary) {
if (denyList.containsKey(hostAddress)) {
// take in account denied server that have reached denied timeout
if (denyList.get(hostAddress) > System.currentTimeMillis()) {
continue;
} else {
denyList.remove(hostAddress);
}
}
if (primary) {
haMode.lastRoundRobinPrimaryHost = hostAddress;
} else {
haMode.lastRoundRobinSecondaryHost = hostAddress;
}
return Optional.of(hostAddress);
}
}
return Optional.empty();
}

/**
Expand Down
35 changes: 22 additions & 13 deletions src/test/java/org/mariadb/jdbc/unit/util/constant/HaModeTest.java
Expand Up @@ -116,21 +116,30 @@ public void loadBalanceTest() throws InterruptedException {
available.add(host6);

ConcurrentMap<HostAddress, Long> denyList = new ConcurrentHashMap<>();
Map<HostAddress, Integer> res = loopPercReturn(available, denyList);
Integer use = res.get(host1);
Assertions.assertTrue(use > 250 && use < 400, "Expect 33% host1, 33% host2 and 33% host 3");
Map<HostAddress, Integer> res = loopPercReturn(available, denyList, true);
Assertions.assertEquals(334, res.get(host1));
Assertions.assertEquals(333, res.get(host2));
Assertions.assertEquals(333, res.get(host3));

denyList.putIfAbsent(host1, System.currentTimeMillis() + 10000);
denyList.putIfAbsent(host1, System.currentTimeMillis() + 1000000);

res = loopPercReturn(available, denyList, true);
Assertions.assertEquals(null, res.get(host1));
Assertions.assertEquals(500, res.get(host2));
Assertions.assertEquals(500, res.get(host3));

res = loopPercReturn(available, denyList);
use = res.get(host2);
Assertions.assertTrue(
use > 400 && use < 600, "Expect 50% host2 and 50% host 3, but was " + use);
denyList.clear();
denyList.putIfAbsent(host1, System.currentTimeMillis() - 1000000);

res = loopPercReturn(available, denyList, true);
Assertions.assertEquals(334, res.get(host1));
Assertions.assertEquals(333, res.get(host2));
Assertions.assertEquals(333, res.get(host3));

res = loopPercReturn(available, denyList);
use = res.get(host1);
Assertions.assertTrue(use > 250 && use < 400, "Expect 33% host1, 33% host2 and 33% host 3");
res = loopPercReturn(available, denyList, false);
Assertions.assertEquals(334, res.get(host4));
Assertions.assertEquals(333, res.get(host5));
Assertions.assertEquals(333, res.get(host6));
}

@Test
Expand All @@ -151,10 +160,10 @@ public void noneTest() {
}

private Map<HostAddress, Integer> loopPercReturn(
List<HostAddress> available, ConcurrentMap<HostAddress, Long> denyList) {
List<HostAddress> available, ConcurrentMap<HostAddress, Long> denyList, boolean primary) {
Map<HostAddress, Integer> resMap = new HashMap<>();
for (int i = 0; i < 1000; i++) {
Optional<HostAddress> res = HaMode.LOADBALANCE.getAvailableHost(available, denyList, true);
Optional<HostAddress> res = HaMode.LOADBALANCE.getAvailableHost(available, denyList, primary);
if (res.isPresent()) {
if (resMap.containsKey(res.get())) {
resMap.put(res.get(), resMap.get(res.get()) + 1);
Expand Down

0 comments on commit 0fbe892

Please sign in to comment.