Skip to content

Commit

Permalink
HBASE-28500 Rest Java client library assumes stateless servers (#5804)
Browse files Browse the repository at this point in the history
add a sticky flag which forces using the same server for all requests

Signed-off-by: Peter Somogyi <[email protected]>
Signed-off-by: Duo Zhang <[email protected]>
  • Loading branch information
stoty committed Apr 16, 2024
1 parent 4d3a8b8 commit 85cbc35
Showing 1 changed file with 40 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public class Client {

private HttpClient httpClient;
private Cluster cluster;
private Integer lastNodeId;
private boolean sticky = false;
private Configuration conf;
private boolean sslEnabled;
private HttpResponse resp;
Expand Down Expand Up @@ -249,10 +251,12 @@ public void removeExtraHeader(final String name) {
}

/**
* Execute a transaction method given only the path. Will select at random one of the members of
* the supplied cluster definition and iterate through the list until a transaction can be
* successfully completed. The definition of success here is a complete HTTP transaction,
* irrespective of result code.
* Execute a transaction method given only the path. If sticky is false: Will select at random one
* of the members of the supplied cluster definition and iterate through the list until a
* transaction can be successfully completed. The definition of success here is a complete HTTP
* transaction, irrespective of result code. If sticky is true: For the first request it will
* select a random one of the members of the supplied cluster definition. For subsequent requests
* it will use the same member, and it will not automatically re-try if the call fails.
* @param cluster the cluster definition
* @param method the transaction method
* @param headers HTTP header values to send
Expand All @@ -265,10 +269,12 @@ public HttpResponse executePathOnly(Cluster cluster, HttpUriRequest method, Head
if (cluster.nodes.size() < 1) {
throw new IOException("Cluster is empty");
}
int start = (int) Math.round((cluster.nodes.size() - 1) * Math.random());
int i = start;
if (lastNodeId == null || !sticky) {
lastNodeId = (int) (cluster.nodes.size() * Math.random());
}
int start = lastNodeId;
do {
cluster.lastHost = cluster.nodes.get(i);
cluster.lastHost = cluster.nodes.get(lastNodeId);
try {
StringBuilder sb = new StringBuilder();
if (sslEnabled) {
Expand Down Expand Up @@ -302,7 +308,11 @@ public HttpResponse executePathOnly(Cluster cluster, HttpUriRequest method, Head
} catch (URISyntaxException use) {
lastException = new IOException(use);
}
} while (++i != start && i < cluster.nodes.size());
if (!sticky) {
lastNodeId = (++lastNodeId) % cluster.nodes.size();
}
// Do not retry if sticky. Let the caller handle the error.
} while (!sticky && lastNodeId != start);
throw lastException;
}

Expand Down Expand Up @@ -406,6 +416,28 @@ public void setCluster(Cluster cluster) {
this.cluster = cluster;
}

/**
* The default behaviour is load balancing by sending each request to a random host. This DOES NOT
* work with scans, which have state on the REST servers. Make sure sticky is set to true before
* attempting Scan related operations if more than one host is defined in the cluster.
* @return whether subsequent requests will use the same host
*/
public boolean isSticky() {
return sticky;
}

/**
* The default behaviour is load balancing by sending each request to a random host. This DOES NOT
* work with scans, which have state on the REST servers. Set sticky to true before attempting
* Scan related operations if more than one host is defined in the cluster. Nodes must not be
* added or removed from the Cluster object while sticky is true.
* @param sticky whether subsequent requests will use the same host
*/
public void setSticky(boolean sticky) {
lastNodeId = null;
this.sticky = sticky;
}

/**
* Send a HEAD request
* @param path the path or URI
Expand Down

0 comments on commit 85cbc35

Please sign in to comment.