Skip to content
This repository was archived by the owner on Jan 31, 2022. It is now read-only.

Commit dc5986a

Browse files
committed
Fix/close connections (#94)
* Client: Always close stream, consume connection whenever appropriate * Tests: Add a test to ensure KeepAlive is working
1 parent bef90f6 commit dc5986a

File tree

2 files changed

+81
-7
lines changed

2 files changed

+81
-7
lines changed

algoliasearch/src/main/java/com/algolia/search/saas/Client.java

+17-7
Original file line numberDiff line numberDiff line change
@@ -659,10 +659,12 @@ private byte[] _requestRaw(Method m, String url, String json, List<String> hosts
659659
throw new IllegalArgumentException("Method " + m + " is not supported");
660660
}
661661

662+
InputStream stream = null;
663+
HttpURLConnection hostConnection = null;
662664
// set URL
663665
try {
664666
URL hostURL = new URL("https://" + host + url);
665-
HttpURLConnection hostConnection = (HttpURLConnection) hostURL.openConnection();
667+
hostConnection = (HttpURLConnection) hostURL.openConnection();
666668

667669
//set timeouts
668670
hostConnection.setRequestMethod(requestMethod);
@@ -694,8 +696,7 @@ private byte[] _requestRaw(Method m, String url, String json, List<String> hosts
694696
// read response
695697
int code = hostConnection.getResponseCode();
696698
final boolean codeIsError = code / 100 != 2;
697-
InputStream stream = codeIsError ?
698-
hostConnection.getErrorStream() : hostConnection.getInputStream();
699+
stream = codeIsError ? hostConnection.getErrorStream() : hostConnection.getInputStream();
699700
// As per the official Java docs (not the Android docs):
700701
// - `getErrorStream()` may return null => we have to handle this case.
701702
// See <https://docs.oracle.com/javase/7/docs/api/java/net/HttpURLConnection.html#getErrorStream()>.
@@ -716,27 +717,36 @@ private byte[] _requestRaw(Method m, String url, String json, List<String> hosts
716717
// handle http errors
717718
if (codeIsError) {
718719
if (code / 100 == 4) {
719-
String message = _getJSONObject(rawResponse).getString("message");
720720
consumeQuietly(hostConnection);
721-
throw new AlgoliaException(message, code);
721+
throw new AlgoliaException(_getJSONObject(rawResponse).getString("message"), code);
722722
} else {
723-
final String errorMessage = _toCharArray(stream);
724723
consumeQuietly(hostConnection);
725-
errors.add(new AlgoliaException(errorMessage, code));
724+
errors.add(new AlgoliaException(_toCharArray(stream), code));
726725
continue;
727726
}
728727
}
729728
return rawResponse;
730729

731730
}
732731
catch (JSONException e) { // fatal
732+
consumeQuietly(hostConnection);
733733
throw new AlgoliaException("Invalid JSON returned by server", e);
734734
}
735735
catch (UnsupportedEncodingException e) { // fatal
736+
consumeQuietly(hostConnection);
736737
throw new AlgoliaException("Invalid encoding returned by server", e);
737738
}
738739
catch (IOException e) { // host error, continue on the next host
740+
consumeQuietly(hostConnection);
739741
errors.add(e);
742+
} finally {
743+
if (stream != null) {
744+
try {
745+
stream.close();
746+
} catch (IOException e) {
747+
e.printStackTrace();
748+
}
749+
}
740750
}
741751
}
742752

algoliasearch/src/test/java/com/algolia/search/saas/IndexTest.java

+64
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,70 @@ public void testSNI() throws Exception {
478478
testSearchAsync();
479479
}
480480

481+
@Test
482+
public void testKeepAlive() throws Exception {
483+
final int nbTimes = 10;
484+
485+
// Given all hosts being the same one
486+
String appId = (String) Whitebox.getInternalState(client, "applicationID");
487+
List<String> hostsArray = (List<String>) Whitebox.getInternalState(client, "readHosts");
488+
hostsArray.set(0, appId + "-1.algolianet.com");
489+
hostsArray.set(1, appId + "-1.algolianet.com");
490+
hostsArray.set(2, appId + "-1.algolianet.com");
491+
hostsArray.set(3, appId + "-1.algolianet.com");
492+
Whitebox.setInternalState(client, "readHosts", hostsArray);
493+
494+
//And an index that does not cache search queries
495+
index.disableSearchCache();
496+
497+
498+
// Expect first successful search
499+
final long[] startEndTimeArray = new long[2];
500+
startEndTimeArray[0] = System.nanoTime();
501+
final CountDownLatch signal = new CountDownLatch(1);
502+
index.searchAsync(new Query("Francisco"), new CompletionHandler() {
503+
@Override
504+
public void requestCompleted(JSONObject content, AlgoliaException error) {
505+
if (error == null) {
506+
assertEquals(1, content.optInt("nbHits"));
507+
startEndTimeArray[1] = System.nanoTime();
508+
} else {
509+
fail(error.getMessage());
510+
}
511+
signal.countDown();
512+
}
513+
});
514+
assertTrue("No callback was called", signal.await(Helpers.wait, TimeUnit.SECONDS));
515+
516+
final long firstDurationNanos = startEndTimeArray[1] - startEndTimeArray[0];
517+
System.out.println("First query duration: " + firstDurationNanos);
518+
519+
final CountDownLatch signal2 = new CountDownLatch(nbTimes);
520+
for (int i = 0; i < nbTimes; i++) {
521+
startEndTimeArray[0] = System.nanoTime();
522+
final int finalIter = i;
523+
index.searchAsync(new Query("Francisco"), new CompletionHandler() {
524+
@Override
525+
public void requestCompleted(JSONObject content, AlgoliaException error) {
526+
if (error == null) {
527+
startEndTimeArray[1] = System.nanoTime();
528+
final long iterDiff = startEndTimeArray[1] - startEndTimeArray[0];
529+
final String iterString = String.format("iteration %d: %d < %d", finalIter, iterDiff, firstDurationNanos);
530+
531+
// And successful fastest subsequent calls
532+
assertEquals(1, content.optInt("nbHits"));
533+
assertTrue("Subsequent calls should be fastest than first (" + iterString + ")", startEndTimeArray[1] - startEndTimeArray[0] < firstDurationNanos);
534+
} else {
535+
fail(error.getMessage());
536+
}
537+
signal2.countDown();
538+
}
539+
});
540+
}
541+
assertTrue("No callback was called", signal2.await(Helpers.wait, TimeUnit.SECONDS));
542+
}
543+
544+
481545
private void addDummyObjects(int objectCount) throws Exception {
482546
// Construct an array of dummy objects.
483547
objects = new ArrayList<JSONObject>();

0 commit comments

Comments
 (0)