Skip to content

Commit

Permalink
High availablity. Performing round robin between nodes .issue #3
Browse files Browse the repository at this point in the history
  • Loading branch information
itaiag committed Dec 2, 2018
1 parent 8d5f003 commit de68393
Show file tree
Hide file tree
Showing 12 changed files with 178 additions and 41 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<topq.repository.rootUrl>http://maven.top-q.co.il</topq.repository.rootUrl>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<elastic.version>5.2.0</elastic.version>
<elastic.version>6.0.0-alpha2</elastic.version>
</properties>
<dependencies>
<dependency>
Expand Down
36 changes: 35 additions & 1 deletion src/main/java/il/co/topq/elastic/ESClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpHost;
Expand All @@ -15,7 +17,17 @@ public class ESClient implements Closeable {
private final ESRest rest;

public ESClient(String host, int port) {
rest = new ESRest(RestClient.builder(new HttpHost(host, port, "http")).build());
final List<RestClient> clients = new ArrayList<RestClient>();
clients.add(RestClient.builder(new HttpHost(host, port, "http")).build());
rest = new ESRest(clients);
}

public static Builder builder() {
return new Builder();
}

private ESClient(List<RestClient> clients) {
rest = new ESRest(clients);
}

@Override
Expand All @@ -42,4 +54,26 @@ public GenericResponseHandler stats() throws IOException{
return new GenericResponseHandler(response);
}

public static class Builder {

private List<RestClient> clients;

private Builder() {
clients = new ArrayList<RestClient>();
}

public Builder addClient(String host, int port) {
RestClient client = RestClient.builder(new HttpHost(host, port, "http")).build();
clients.add(client);
return this;
}

public ESClient build() {
if (clients.isEmpty()) {
throw new IllegalArgumentException("Clients can't be null");
}
return new ESClient(clients);
}

}
}
102 changes: 78 additions & 24 deletions src/main/java/il/co/topq/elastic/ESRest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
Expand All @@ -13,34 +17,37 @@

import com.fasterxml.jackson.databind.ObjectMapper;

public class ESRest implements Closeable{
public class ESRest implements Closeable {

protected static final ObjectMapper mapper = new ObjectMapper();

protected final RestClient client;
protected List<RestClient> clients;

public ESRest(RestClient client) {
this.client = client;
public ESRest(List<RestClient> clients) {
Objects.requireNonNull(clients,"Clients can't be null");
if (clients.isEmpty()) {
throw new IllegalArgumentException("Client list can't be empty");
}
this.clients = Collections.synchronizedList(clients);
}
public <T> T get(String resource, Class<T> responseClass, boolean assertSuccess) throws IOException{
final Response response = client.performRequest("GET", resource, Collections.singletonMap("pretty", "true"));
if (assertSuccess){

public <T> T get(String resource, Class<T> responseClass, boolean assertSuccess) throws IOException {
final Response response = performRequest("GET", resource, Collections.singletonMap("pretty", "true"));
if (assertSuccess) {
assertSuccess(response);
}
return mapper.readValue(IOUtils.toString(response.getEntity().getContent(), "UTF-8"), responseClass);
}

public <T> T post(String resource, String body, Class<T> responseClass, boolean assertSuccess)
throws IOException {
final Response response = client.performRequest("POST", resource, Collections.singletonMap("pretty", "true"),
public <T> T post(String resource, String body, Class<T> responseClass, boolean assertSuccess) throws IOException {
final Response response = performRequest("POST", resource, Collections.singletonMap("pretty", "true"),
new NStringEntity(body, ContentType.APPLICATION_JSON));
if (assertSuccess) {
assertSuccess(response);
}
return mapper.readValue(IOUtils.toString(response.getEntity().getContent(), "UTF-8"), responseClass);
}

/**
*
* @param resource
Expand All @@ -49,40 +56,87 @@ public <T> T post(String resource, String body, Class<T> responseClass, boolean
* @throws IOException
*/
public int head(String resource, boolean assertSuccess) throws IOException {
Response response = client.performRequest("HEAD", resource, Collections.singletonMap("pretty", "true"));
Response response = performRequest("HEAD", resource, Collections.singletonMap("pretty", "true"));
return response.getStatusLine().getStatusCode();
}

public <T> T put(String resource, String body ,Class<T> responseClass, boolean assertSuccess) throws IOException {
public <T> T put(String resource, String body, Class<T> responseClass, boolean assertSuccess) throws IOException {
final HttpEntity entity = new NStringEntity(body, ContentType.APPLICATION_JSON);
final Response response = client.performRequest("PUT", resource, Collections.<String, String>emptyMap(),
entity);
final Response response = performRequest("PUT", resource, Collections.<String, String>emptyMap(), entity);
if (assertSuccess) {
assertSuccess(response);
}
return mapper.readValue(IOUtils.toString(response.getEntity().getContent(), "UTF-8"), responseClass);
}

public <T> T delete(String resource,Class<T> responseClass, boolean assertSuccess) throws IOException {
final Response response = client.performRequest("DELETE", resource, Collections.<String, String>emptyMap());
private Response performRequest(String method, String endpoint, Map<String, String> headers) throws IOException {
return performRequest(method, endpoint, headers, null);
}

private Response performRequest(String method, String endpoint, Map<String, String> headers, HttpEntity entity)
throws IOException {
IOException savedException = null;
int clientIndex = 0;
for (; clientIndex < clients.size(); clientIndex++) {
try {
final Response response = clients.get(clientIndex).performRequest(method, endpoint, headers, entity);
if (clientIndex != 0) {
synchronized (clients) {
// If the client is not at the top of the list, we should move
// it since it is the successful one.
clients = moveToTop(clients, clients.get(clientIndex));
}
}
return response;
} catch (IOException e) {
// TODO: Save all the exceptions and throw them like in TestNG
// soft assert:
// https://github.com/cbeust/testng/blob/master/src/main/java/org/testng/asserts/SoftAssert.java
savedException = e;
}

}
throw savedException;
}

private static <T> List<T> moveToTop(List<T> items, T input) {
int index = items.indexOf(input);
List<T> copy;
if (index >= 0) {
copy = new ArrayList<T>(items.size());
copy.add(items.get(index));
copy.addAll(items.subList(0, index));
copy.addAll(items.subList(index + 1, items.size()));
} else {
return items;
}
return copy;
}

public <T> T delete(String resource, Class<T> responseClass, boolean assertSuccess) throws IOException {
final Response response = performRequest("DELETE", resource, Collections.<String, String>emptyMap());
if (assertSuccess) {
assertSuccess(response);
}
return mapper.readValue(IOUtils.toString(response.getEntity().getContent(), "UTF-8"), responseClass);
}

private void assertSuccess(Response response) throws IOException {
if (response.getStatusLine().getStatusCode() != 200) {
throw new IOException("Return status is " + response.getStatusLine().getStatusCode());
}
}

@Override
public void close() throws IOException{
if (client != null){
client.close();
public void close() throws IOException {
if (null == clients || clients.isEmpty()) {
return;
}
for (RestClient client : clients) {
if (client != null) {
client.close();
}
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@
import org.junit.After;
import org.junit.Before;

public abstract class AbstractCreateRemoveIndexTestCase extends AbstractTestCase{
public abstract class AbstractCreateRemoveIndexTestCase extends AbstractTestCase {

@Before
public void setUp() throws IOException{
if (!client.index(INDEX).isExists()){
public void setUp() throws IOException {
if (!client.index(INDEX).isExists()) {
client.index(INDEX).create(SETTINGS);
}
}

@After
public void tearDown() throws IOException{
if (client.index(INDEX).isExists()){
public void tearDown() throws IOException {
if (client.index(INDEX).isExists()) {
client.index(INDEX).delete();
}
}

}
6 changes: 5 additions & 1 deletion src/test/java/il/co/topq/elastic/AbstractTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ protected void sleep(int timeInSeconds) {

@Before
public void setup() {
client = new ESClient("localhost", 9200);
client = initClient();
}

protected ESClient initClient() {
return ESClient.builder().addClient("localhost", 9200).build();
}

@After
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/il/co/topq/elastic/TestAggregations.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import il.co.topq.elastic.model.Post;

@Ignore
//@Ignore
public class TestAggregations extends AbstractCreateRemoveIndexTestCase {

@Test
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/il/co/topq/elastic/TestDocument.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import il.co.topq.elastic.model.Post;

@Ignore
//@Ignore
public class TestDocument extends AbstractCreateRemoveIndexTestCase {


Expand Down
2 changes: 1 addition & 1 deletion src/test/java/il/co/topq/elastic/TestElasticStatus.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.junit.Ignore;
import org.junit.Test;

@Ignore
//@Ignore
public class TestElasticStatus extends AbstractCreateRemoveIndexTestCase {

@Test
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/il/co/topq/elastic/TestIndex.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import org.junit.Ignore;
import org.junit.Test;

@Ignore
//@Ignore
public class TestIndex extends AbstractTestCase{

@Test
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/il/co/topq/elastic/TestIndexStatus.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.junit.Ignore;
import org.junit.Test;

@Ignore
//@Ignore
public class TestIndexStatus extends AbstractCreateRemoveIndexTestCase {

@Test
Expand Down
45 changes: 45 additions & 0 deletions src/test/java/il/co/topq/elastic/TestMutipleNodes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package il.co.topq.elastic;

import java.io.IOException;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;

import il.co.topq.elastic.model.Post;

public class TestMutipleNodes extends AbstractCreateRemoveIndexTestCase {

@Override
protected ESClient initClient() {
return ESClient.builder().addClient("localhost", 9200).addClient("localhost", 9201).build();
}

@Test
public void testHighAvailiablity() throws IOException, InterruptedException {
addPostAndVerify(555);
System.out.println("Stop the main Elasticsearch node");
// sleep(10);
addPostAndVerify(666);
System.out.println("Restart the main Elasticsearch node and take down the secondery");
// sleep(10);
addPostAndVerify(777);
}

private void addPostAndVerify(int id) throws IOException {
if (!client.index(INDEX).isExists()) {
client.index(INDEX).create(SETTINGS);
}
Post post0 = new Post();
post0.setId(id);
post0.setOp("Itai");
post0.setPoints(100);
post0.setSubreddit("all");
client.index(INDEX).document(DOC).add().single(id + "", post0);
sleep(1);
List<Post> posts = client.index(INDEX).document(DOC).search().byTerm("id", id + "").asClass(Post.class);
Assert.assertNotNull(posts);
Assert.assertEquals(1, posts.size());
}

}
2 changes: 1 addition & 1 deletion src/test/java/il/co/topq/elastic/TestSearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import il.co.topq.elastic.model.Post;

@Ignore
//@Ignore
public class TestSearch extends AbstractCreateRemoveIndexTestCase {

@Test
Expand Down

0 comments on commit de68393

Please sign in to comment.