Skip to content

Commit

Permalink
Retries and telemetry
Browse files Browse the repository at this point in the history
  • Loading branch information
richardm-stripe committed May 13, 2021
1 parent 6b13bc0 commit 304aa7e
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 25 deletions.
73 changes: 57 additions & 16 deletions src/main/java/com/stripe/net/HttpClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,13 @@ public StripeResponseStream requestStream(StripeRequest request) throws StripeEx
"streamingRequest is unimplemented for this HttpClient");
}

/**
* Sends the given request to Stripe's API, handling telemetry if not disabled.
*
* @param request the request
* @return the response
* @throws StripeException If the request fails for any reason
*/
public StripeResponse requestWithTelemetry(StripeRequest request) throws StripeException {
@FunctionalInterface
private interface RequestSendFunction<R> {
R apply(StripeRequest request) throws StripeException;
}

private <T extends StripeResponseInterface> T sendWithTelemetry(
StripeRequest request, RequestSendFunction<T> send) throws StripeException {
Optional<String> telemetryHeaderValue = requestTelemetry.getHeaderValue(request.headers());
if (telemetryHeaderValue.isPresent()) {
request =
Expand All @@ -65,7 +64,7 @@ public StripeResponse requestWithTelemetry(StripeRequest request) throws StripeE

Stopwatch stopwatch = Stopwatch.startNew();

StripeResponse response = this.request(request);
T response = send.apply(request);

stopwatch.stop();

Expand All @@ -75,23 +74,40 @@ public StripeResponse requestWithTelemetry(StripeRequest request) throws StripeE
}

/**
* Sends the given request to Stripe's API, retrying the request in cases of intermittent
* problems.
* Sends the given request to Stripe's API, handling telemetry if not disabled.
*
* @param request the request
* @return the response
* @throws StripeException If the request fails for any reason
*/
public StripeResponse requestWithRetries(StripeRequest request) throws StripeException {
public StripeResponse requestWithTelemetry(StripeRequest request) throws StripeException {
return sendWithTelemetry(request, (r) -> this.request(r));
}

/**
* Sends the given request to Stripe's API, streaming the response, and handling telemetry if not
* disabled.
*
* @param request the request
* @return the response
* @throws StripeException If the request fails for any reason
*/
public StripeResponseStream requestStreamWithTelemetry(StripeRequest request)
throws StripeException {
return sendWithTelemetry(request, (r) -> this.requestStream(r));
}

public <T extends StripeResponseInterface> T sendWithRetries(
StripeRequest request, RequestSendFunction<T> send) throws StripeException {
ApiConnectionException requestException = null;
StripeResponse response = null;
T response = null;
int retry = 0;

while (true) {
requestException = null;

try {
response = this.requestWithTelemetry(request);
response = send.apply(request);
} catch (ApiConnectionException e) {
requestException = e;
}
Expand All @@ -118,6 +134,31 @@ public StripeResponse requestWithRetries(StripeRequest request) throws StripeExc
return response;
}

/**
* Sends the given request to Stripe's API, retrying the request in cases of intermittent
* problems.
*
* @param request the request
* @return the response
* @throws StripeException If the request fails for any reason
*/
public StripeResponse requestWithRetries(StripeRequest request) throws StripeException {
return sendWithRetries(request, (r) -> this.requestWithTelemetry(r));
}

/**
* Sends the given request to Stripe's API, streaming the response, retrying the request in cases
* of intermittent problems.
*
* @param request the request
* @return the response
* @throws StripeException If the request fails for any reason
*/
public StripeResponseStream requestStreamWithRetries(StripeRequest request)
throws StripeException {
return sendWithRetries(request, (r) -> this.requestStreamWithTelemetry(r));
}

/**
* Builds the value of the {@code User-Agent} header.
*
Expand Down Expand Up @@ -177,8 +218,8 @@ private static String formatAppInfo(Map<String, String> info) {
return str;
}

private boolean shouldRetry(
int numRetries, StripeException exception, StripeRequest request, StripeResponse response) {
private <T extends StripeResponseInterface> boolean shouldRetry(
int numRetries, StripeException exception, StripeRequest request, T response) {
// Do not retry if we are out of retries.
if (numRetries >= request.options().getMaxNetworkRetries()) {
return false;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/stripe/net/LiveStripeResponseGetter.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public InputStream requestStream(
RequestOptions options)
throws StripeException {
StripeRequest request = new StripeRequest(method, url, params, options);
StripeResponseStream responseStream = httpClient.requestStream(request);
StripeResponseStream responseStream = httpClient.requestStreamWithRetries(request);

int responseCode = responseStream.code();

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/stripe/net/RequestTelemetry.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public Optional<String> getHeaderValue(HttpHeaders headers) {
* @param response the Stripe response
* @param duration the request duration
*/
public void maybeEnqueueMetrics(StripeResponse response, Duration duration) {
public void maybeEnqueueMetrics(StripeResponseInterface response, Duration duration) {
if (!Stripe.enableTelemetry) {
return;
}
Expand Down
12 changes: 8 additions & 4 deletions src/main/java/com/stripe/net/StripeResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Optional;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.Value;
Expand All @@ -16,20 +15,22 @@
/** A response from Stripe's API. */
@Value
@Accessors(fluent = true)
public class StripeResponse {
public class StripeResponse implements StripeResponseInterface {
/** The HTTP status code of the response. */
@Getter(onMethod_ = {@Override})
int code;

/** The HTTP headers of the response. */
@Getter(onMethod_ = {@Override})
HttpHeaders headers;

/** The body of the response. */
String body;

/** Number of times the request was retried. Used for internal tests only. */
@NonFinal
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
@Getter(onMethod_ = {@Override})
@Setter(onMethod_ = {@Override})
int numRetries;

/**
Expand All @@ -54,6 +55,7 @@ public StripeResponse(int code, HttpHeaders headers, String body) {
*
* @return the date of the request, as returned by Stripe
*/
@Override
public Instant date() {
Optional<String> dateStr = this.headers.firstValue("Date");
if (!dateStr.isPresent()) {
Expand All @@ -67,6 +69,7 @@ public Instant date() {
*
* @return the idempotency key of the request, as returned by Stripe
*/
@Override
public String idempotencyKey() {
return this.headers.firstValue("Idempotency-Key").orElse(null);
}
Expand All @@ -76,6 +79,7 @@ public String idempotencyKey() {
*
* @return the ID of the request, as returned by Stripe
*/
@Override
public String requestId() {
return this.headers.firstValue("Request-Id").orElse(null);
}
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/com/stripe/net/StripeResponseStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
/** A response from Stripe's API. */
@Value
@Accessors(fluent = true)
public class StripeResponseStream {
public class StripeResponseStream implements StripeResponseInterface {
/** The HTTP status code of the response. */
@Getter(onMethod_ = {@Override})
int code;

/** The HTTP headers of the response. */
@Getter(onMethod_ = {@Override})
HttpHeaders headers;

/** The body of the response. */
Expand All @@ -33,8 +35,8 @@ public class StripeResponseStream {

/** Number of times the request was retried. Used for internal tests only. */
@NonFinal
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
@Getter(onMethod_ = {@Override})
@Setter(onMethod_ = {@Override})
int numRetries;

/**
Expand Down Expand Up @@ -65,6 +67,7 @@ public StripeResponse unstream() throws IOException {
*
* @return the date of the request, as returned by Stripe
*/
@Override
public Instant date() {
Optional<String> dateStr = this.headers.firstValue("Date");
if (!dateStr.isPresent()) {
Expand All @@ -78,6 +81,7 @@ public Instant date() {
*
* @return the idempotency key of the request, as returned by Stripe
*/
@Override
public String idempotencyKey() {
return this.headers.firstValue("Idempotency-Key").orElse(null);
}
Expand All @@ -87,6 +91,7 @@ public String idempotencyKey() {
*
* @return the ID of the request, as returned by Stripe
*/
@Override
public String requestId() {
return this.headers.firstValue("Request-Id").orElse(null);
}
Expand Down

0 comments on commit 304aa7e

Please sign in to comment.