From ddacd33c5e19d283a09435f5c85d0f0f43044145 Mon Sep 17 00:00:00 2001 From: mmuller Date: Tue, 22 Nov 2016 11:40:41 -0800 Subject: [PATCH] Retry RDE report on SocketTimeoutException It's likely that either 1) we should be doing this for more than a SocketTimeoutException - we probably want to do this in the case of a number of different transient failures 2) we don't want to do this at all because it happens in a background task that will get re-run anyway. In any case, this seems like the right fix and it addresses a problem we see occassionally. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=139935190 --- java/google/registry/rde/RdeReporter.java | 29 +++++++++++++------ java/google/registry/util/Retrier.java | 4 +-- .../registry/rde/RdeReportActionTest.java | 18 ++++++++++++ 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/java/google/registry/rde/RdeReporter.java b/java/google/registry/rde/RdeReporter.java index 27fcd414b..c18cc1ed0 100644 --- a/java/google/registry/rde/RdeReporter.java +++ b/java/google/registry/rde/RdeReporter.java @@ -33,6 +33,7 @@ import google.registry.config.RegistryConfig; import google.registry.keyring.api.KeyModule.Key; import google.registry.request.HttpException.InternalServerErrorException; import google.registry.util.FormattingLogger; +import google.registry.util.Retrier; import google.registry.util.UrlFetchException; import google.registry.xjc.XjcXmlTransformer; import google.registry.xjc.iirdea.XjcIirdeaResponseElement; @@ -43,7 +44,9 @@ import google.registry.xml.XmlException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.MalformedURLException; +import java.net.SocketTimeoutException; import java.net.URL; +import java.util.concurrent.Callable; import javax.inject.Inject; /** @@ -60,6 +63,7 @@ public class RdeReporter { private static final String REPORT_MIME = "text/xml"; @Inject RegistryConfig config; + @Inject Retrier retrier; @Inject URLFetchService urlFetchService; @Inject @Config("rdeReportUrlPrefix") String reportUrlPrefix; @Inject @Key("icannReportingPassword") String password; @@ -75,19 +79,26 @@ public class RdeReporter { URL url = makeReportUrl(header.getTld(), report.getId()); String username = header.getTld() + "_ry"; String token = base64().encode(String.format("%s:%s", username, password).getBytes(UTF_8)); - HTTPRequest req = new HTTPRequest(url, PUT, validateCertificate().setDeadline(60d)); + final HTTPRequest req = new HTTPRequest(url, PUT, validateCertificate().setDeadline(60d)); req.addHeader(new HTTPHeader(CONTENT_TYPE, REPORT_MIME)); req.addHeader(new HTTPHeader(AUTHORIZATION, "Basic " + token)); req.setPayload(reportBytes); logger.infofmt("Sending report:\n%s", new String(reportBytes, UTF_8)); - HTTPResponse rsp = urlFetchService.fetch(req); - switch (rsp.getResponseCode()) { - case SC_OK: - case SC_BAD_REQUEST: - break; - default: - throw new UrlFetchException("PUT failed", req, rsp); - } + HTTPResponse rsp = retrier.callWithRetry( + new Callable() { + @Override + public HTTPResponse call() throws Exception { + HTTPResponse rsp = urlFetchService.fetch(req); + switch (rsp.getResponseCode()) { + case SC_OK: + case SC_BAD_REQUEST: + break; + default: + throw new UrlFetchException("PUT failed", req, rsp); + } + return rsp; + } + }, SocketTimeoutException.class); // Ensure the XML response is valid. XjcIirdeaResult result = parseResult(rsp); diff --git a/java/google/registry/util/Retrier.java b/java/google/registry/util/Retrier.java index d25de94de..1001c4610 100644 --- a/java/google/registry/util/Retrier.java +++ b/java/google/registry/util/Retrier.java @@ -93,8 +93,8 @@ public class Retrier implements Serializable { @SafeVarargs public final V callWithRetry( Callable callable, - Class retryableError, - Class... moreRetryableErrors) { + Class retryableError, + Class... moreRetryableErrors) { final Set> retryables = new ImmutableSet.Builder>().add(retryableError).add(moreRetryableErrors).build(); return callWithRetry(callable, new Predicate() { diff --git a/javatests/google/registry/rde/RdeReportActionTest.java b/javatests/google/registry/rde/RdeReportActionTest.java index 93c1812cd..c2ae0f4fa 100644 --- a/javatests/google/registry/rde/RdeReportActionTest.java +++ b/javatests/google/registry/rde/RdeReportActionTest.java @@ -52,11 +52,15 @@ import google.registry.request.HttpException.InternalServerErrorException; import google.registry.testing.AppEngineRule; import google.registry.testing.BouncyCastleProviderRule; import google.registry.testing.ExceptionRule; +import google.registry.testing.FakeClock; import google.registry.testing.FakeResponse; +import google.registry.testing.FakeSleeper; +import google.registry.util.Retrier; import google.registry.xjc.XjcXmlTransformer; import google.registry.xjc.rdereport.XjcRdeReportReport; import google.registry.xml.XmlException; import java.io.ByteArrayInputStream; +import java.net.SocketTimeoutException; import java.util.Map; import org.bouncycastle.openpgp.PGPPublicKey; import org.joda.time.DateTime; @@ -103,6 +107,7 @@ public class RdeReportActionTest { reporter.reportUrlPrefix = "https://rde-report.example"; reporter.urlFetchService = urlFetchService; reporter.password = "foo"; + reporter.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3); RdeReportAction action = new RdeReportAction(); action.gcsUtils = new GcsUtils(gcsService, 1024); action.ghostryde = new Ghostryde(1024); @@ -185,6 +190,19 @@ public class RdeReportActionTest { createAction().runWithLock(loadRdeReportCursor()); } + @Test + public void testRunWithLock_socketTimeout_doesRetry() throws Exception { + when(httpResponse.getResponseCode()).thenReturn(SC_OK); + when(httpResponse.getContent()).thenReturn(IIRDEA_GOOD_XML.read()); + when(urlFetchService.fetch(request.capture())) + .thenThrow(new SocketTimeoutException()) + .thenReturn(httpResponse); + createAction().runWithLock(loadRdeReportCursor()); + assertThat(response.getStatus()).isEqualTo(200); + assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8); + assertThat(response.getPayload()).isEqualTo("OK test 2006-06-06T00:00:00.000Z\n"); + } + private DateTime loadRdeReportCursor() { return ofy() .load()