RetryOptions
RetryOptions
RetryOptions sets the retry behavior of the Java Delivery SDK for a request that fails with a transient HTTP status or a network error. Use this class to
- change the number of retries and the wait between them
- choose a backoff strategy, or supply your own delay calculation
- choose which HTTP status codes trigger a retry
- turn retries off
Create an instance with new RetryOptions(), which starts with the defaults in the Class-Level Properties table below. Each setter returns the same instance, so you can chain them. Pass the instance to the setRetryOptions method of Config before you create the stack.
| Name | Type | Description |
|---|---|---|
| retryEnabled | boolean | setRetryEnabled, isRetryEnabled. Turns the retry step on or off. |
| retryLimit | int | setRetryLimit, getRetryLimit. Maximum number of retries, from 0 to 10. |
| retryDelayMs | long | setRetryDelay, getRetryDelay. Base wait between attempts, in milliseconds. |
| backoffStrategy | RetryOptions.BackoffStrategy | setBackoffStrategy, getBackoffStrategy. How the wait grows from one retry to the next. |
| retryableStatusCodes | int[] | setRetryableStatusCodes, getRetryableStatusCodes. HTTP statuses that trigger a retry. |
| customBackoffStrategy | CustomBackoffStrategy | setCustomBackoffStrategy, getCustomBackoffStrategy, hasCustomBackoff. Your own wait calculation. |
getBackoffStrategy
getBackoffStrategy returns the backoff strategy value stored on the options.
Returns: BackoffStrategy. The configured backoff strategy.
Validation
- There is no client-side validation.
- getBackoffStrategy does not throw.
Behavior
- The default is EXPONENTIAL.
- setCustomBackoffStrategy sets the value to CUSTOM.
- The value does not always name the rule in use. After setCustomBackoffStrategy, the custom calculation decides every wait, even when a later setBackoffStrategy call changes this value. Check hasCustomBackoff as well.
Example
Basic usage: read and compare the strategy
import com.contentstack.sdk.RetryOptions;
public class ReadBackoffStrategy {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions();
System.out.println(retryOptions.getBackoffStrategy()); // EXPONENTIAL, the default
retryOptions.setBackoffStrategy(RetryOptions.BackoffStrategy.FIXED);
if (retryOptions.getBackoffStrategy() == RetryOptions.BackoffStrategy.FIXED) {
System.out.println("Every retry waits " + retryOptions.getRetryDelay() + " ms");
}
}
}getCustomBackoffStrategy
getCustomBackoffStrategy returns the wait calculation that setCustomBackoffStrategy stored.
Returns: CustomBackoffStrategy. The custom backoff strategy, or null.
Validation
- There is no client-side validation.
- getCustomBackoffStrategy does not throw.
Behavior
- Returns null until you call setCustomBackoffStrategy, which is the default state. Call hasCustomBackoff to check first.
- The returned object is the one you passed, so you can call its calculateDelay method to preview the waits.
Example
Basic usage: preview the waits of a custom strategy
import com.contentstack.sdk.CustomBackoffStrategy;
import com.contentstack.sdk.RetryOptions;
public class PreviewCustomDelays {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions()
.setCustomBackoffStrategy((attempt, statusCode, exception) -> 1500L * (attempt + 1));
CustomBackoffStrategy strategy = retryOptions.getCustomBackoffStrategy();
if (strategy != null) {
for (int attempt = 0; attempt < retryOptions.getRetryLimit(); attempt++) {
// Preview the wait for a 503 response on each retry
System.out.println("Retry " + attempt + ": " + strategy.calculateDelay(attempt, 503, null) + " ms");
}
}
}
}getRetryDelay
getRetryDelay returns the base wait, in milliseconds, that the backoff strategy uses.
Returns: long. The base wait in milliseconds.
Validation
- There is no client-side validation.
- getRetryDelay does not throw.
Behavior
- The default is 1000.
- The value is the base, not the actual wait. The strategy turns it into each wait. With the default EXPONENTIAL strategy, the waits are 1, 2, and 4 times the base delay. The strategy table on the RetryOptions class page lists every strategy.
- The SDK ignores this value once the options hold a custom backoff strategy. Check with the hasCustomBackoff method.
Example
Basic usage: read the base delay
import com.contentstack.sdk.RetryOptions;
public class ReadRetryDelay {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions().setRetryDelay(250);
System.out.println(retryOptions.getRetryDelay()); // 250
System.out.println(new RetryOptions().getRetryDelay()); // 1000, the default
}
}getRetryLimit
getRetryLimit returns the maximum number of times the SDK retries a failed request after the first attempt.
Returns: int. The maximum number of retries.
Validation
- There is no client-side validation.
- getRetryLimit does not throw.
Behavior
- The default is 3. The SDK then sends up to four attempts in total.
- The value is always from 0 to 10, because the setRetryLimit method rejects anything else.
Example
Basic usage: read the retry limit
import com.contentstack.sdk.RetryOptions;
public class ReadRetryLimit {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions().setRetryLimit(5);
System.out.println(retryOptions.getRetryLimit()); // 5
System.out.println(new RetryOptions().getRetryLimit()); // 3, the default
}
}getRetryableStatusCodes
getRetryableStatusCodes returns the HTTP status codes that make the SDK retry a request.
Returns: int[]. A copy of the retryable status codes.
Validation
- There is no client-side validation.
- getRetryableStatusCodes does not throw.
Behavior
- The default list is 408, 429, 502, 503, and 504.
- Each call returns a new copy. Changing the returned array does not change the options. Use the setRetryableStatusCodes method instead.
- An empty array means the SDK retries network errors only.
Example
Basic usage: read the status codes
import com.contentstack.sdk.RetryOptions;
import java.util.Arrays;
public class ReadStatusCodes {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions();
int[] codes = retryOptions.getRetryableStatusCodes();
System.out.println(Arrays.toString(codes)); // [408, 429, 502, 503, 504]
codes[0] = 500; // changes the copy only
System.out.println(Arrays.toString(retryOptions.getRetryableStatusCodes())); // [408, 429, 502, 503, 504]
}
}hasCustomBackoff
hasCustomBackoff reports whether a custom calculation decides the wait before each retry.
Returns: boolean. True when the options hold a custom strategy.
Validation
- There is no client-side validation.
- hasCustomBackoff does not throw.
Behavior
- Returns false by default.
- Returns true from the first setCustomBackoffStrategy call onward. No method sets it back to false.
- When it returns true, the SDK ignores getRetryDelay and getBackoffStrategy when it calculates a wait.
Example
Basic usage: check for a custom strategy
import com.contentstack.sdk.RetryOptions;
public class CheckCustomBackoff {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions();
System.out.println(retryOptions.hasCustomBackoff()); // false
retryOptions.setCustomBackoffStrategy((attempt, statusCode, exception) -> 3000L);
System.out.println(retryOptions.hasCustomBackoff()); // true
}
}isRetryEnabled
isRetryEnabled reports whether the SDK's retry step runs.
Returns: boolean. True when the retry step runs.
Validation
- There is no client-side validation.
- isRetryEnabled does not throw.
Behavior
- The default is true.
- The value reflects these options only. A stack created while the value was false has no retry step, so it does not retry even after the value changes to true. See the setRetryEnabled method.
- true with a retry limit of 0 still sends one attempt.
Example
Basic usage: check whether retries are on
import com.contentstack.sdk.RetryOptions;
public class ReadRetryEnabled {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions();
System.out.println(retryOptions.isRetryEnabled()); // true, the default
retryOptions.setRetryEnabled(false);
System.out.println(retryOptions.isRetryEnabled()); // false
}
}setBackoffStrategy
setBackoffStrategy chooses the rule that grows the wait from one retry to the next.
| Name | Type | Description |
|---|---|---|
| strategy (required) | RetryOptions.BackoffStrategy | Rule that turns the base delay into each wait. |
Returns: RetryOptions. The same retry options, for chaining.
Validation
- Throws NullPointerException if strategy is null, because the SDK needs a rule to calculate each wait.
- The exception comes from setBackoffStrategy itself, at the call. The options keep their previous strategy.
Behavior
- The default strategy is EXPONENTIAL.
- The four values calculate the wait before retry n (starting at 0) from the base delay that setRetryDelay sets, as follows:
- FIXED waits the base delay every time.
- LINEAR waits the base delay × (n + 1).
- EXPONENTIAL waits the base delay × 2 raised to the power n.
- CUSTOM waits the value from your CustomBackoffStrategy. Without one, it waits the base delay, the same as FIXED.
- Selecting CUSTOM here does not supply a calculation. Use the setCustomBackoffStrategy method, which also selects CUSTOM.
- After setCustomBackoffStrategy, a later setBackoffStrategy call changes the value that getBackoffStrategy returns, but the custom calculation still decides every wait.
Example
Basic usage: use a linear backoff
import com.contentstack.sdk.Config;
import com.contentstack.sdk.Contentstack;
import com.contentstack.sdk.RetryOptions;
import com.contentstack.sdk.Stack;
public class LinearBackoff {
public static void main(String[] args) throws IllegalAccessException {
RetryOptions retryOptions = new RetryOptions()
.setRetryDelay(1000)
.setBackoffStrategy(RetryOptions.BackoffStrategy.LINEAR); // waits 1000, 2000, 3000 ms
// Attach the options before creating the stack
Config config = new Config();
config.setRetryOptions(retryOptions);
Stack stack = Contentstack.stack("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>", config);
System.out.println(config.getRetryOptions().getBackoffStrategy()); // LINEAR
}
}Edge case: a custom strategy keeps deciding the wait after a strategy change
import com.contentstack.sdk.RetryOptions;
public class CustomStrategyPriority {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions()
.setCustomBackoffStrategy((attempt, statusCode, exception) -> 2000L)
.setBackoffStrategy(RetryOptions.BackoffStrategy.FIXED);
System.out.println(retryOptions.getBackoffStrategy()); // FIXED
System.out.println(retryOptions.hasCustomBackoff()); // true, so every wait is still 2000 ms
}
}setCustomBackoffStrategy
setCustomBackoffStrategy supplies your own calculation for the wait before each retry, and selects the CUSTOM strategy.
| Name | Type | Description |
|---|---|---|
| customStrategy (required) | CustomBackoffStrategy | Calculates the wait in milliseconds before each retry. |
Returns: RetryOptions. The same retry options, for chaining.
Validation
- Throws NullPointerException if customStrategy is null, because the SDK calls it before every retry.
- The exception comes from setCustomBackoffStrategy itself, at the call. The options keep their previous strategy.
Behavior
- CustomBackoffStrategy is a functional interface, so a lambda works. Its one method is long calculateDelay(int attempt, int statusCode, IOException exception). The RetryOptions class page describes each argument.
- The SDK calls calculateDelay before every retry, for a status retry and a network error retry alike, and waits for the number of milliseconds it returns.
- For a status retry, statusCode holds the HTTP status and exception is null.
- For a network error, statusCode is -1 and exception holds the java.io.IOException.
- Return 0 or a positive number. A negative value makes Thread.sleep throw IllegalArgumentException.
- The custom calculation replaces the base delay and the strategy value. A later setBackoffStrategy call does not switch it off.
- No method removes a custom strategy, because setCustomBackoffStrategy rejects null. Create a new RetryOptions to go back to a built-in strategy.
- The SDK still applies the retry limit and the status list. calculateDelay sets only the wait.
Example
Basic usage: wait longer after a rate limit
import com.contentstack.sdk.Config;
import com.contentstack.sdk.Contentstack;
import com.contentstack.sdk.RetryOptions;
import com.contentstack.sdk.Stack;
public class RateLimitBackoff {
public static void main(String[] args) throws IllegalAccessException {
RetryOptions retryOptions = new RetryOptions()
.setCustomBackoffStrategy((attempt, statusCode, exception) -> {
if (statusCode == 429) {
return 5000L; // rate limited: wait five seconds
}
return 1000L * (attempt + 1); // other failures: 1000, 2000, 3000 ms
});
// Attach the options before creating the stack
Config config = new Config();
config.setRetryOptions(retryOptions);
Stack stack = Contentstack.stack("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>", config);
System.out.println(config.getRetryOptions().getBackoffStrategy()); // CUSTOM
}
}Edge case: log network errors from an anonymous class
import com.contentstack.sdk.CustomBackoffStrategy;
import com.contentstack.sdk.RetryOptions;
import java.io.IOException;
public class LogNetworkRetries {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions().setCustomBackoffStrategy(new CustomBackoffStrategy() {
@Override
public long calculateDelay(int attempt, int statusCode, IOException exception) {
if (exception != null) {
// statusCode is -1 for a network error
System.out.println("Retry " + attempt + " after: " + exception.getMessage());
}
return 2000L;
}
});
System.out.println(retryOptions.hasCustomBackoff()); // true
}
}Error handling: reject a null strategy
import com.contentstack.sdk.RetryOptions;
public class RejectNullStrategy {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions();
try {
retryOptions.setCustomBackoffStrategy(null);
} catch (NullPointerException e) {
// Thrown at the call when the strategy is null
System.out.println("Strategy required: " + e.getMessage());
}
System.out.println(retryOptions.hasCustomBackoff()); // false
}
}setRetryDelay
setRetryDelay sets the base wait, in milliseconds, that the backoff strategy uses to calculate the wait before each retry.
| Name | Type | Description |
|---|---|---|
| delayMs (required) | long | Base wait between attempts, in milliseconds. |
Returns: RetryOptions. The same retry options, for chaining.
Validation
- Throws IllegalArgumentException if delayMs is below 0, because a wait cannot be negative.
- The exception comes from setRetryDelay itself, at the call. The options keep their previous delay.
- There is no upper limit. A large value blocks the calling thread for that long before each retry.
Behavior
- The default base delay is 1000 ms.
- The strategy turns the base delay into the actual wait. With the default EXPONENTIAL strategy, a base of 500 gives waits of 500, 1000, and 2000 ms. See the strategy table on the RetryOptions class page.
- 0 sends the retries back to back with no wait.
- A custom backoff strategy replaces this value. The SDK ignores the base delay after you call setCustomBackoffStrategy.
- An int argument such as 500 widens to long, so it needs no L suffix.
Example
Basic usage: wait half a second before the first retry
import com.contentstack.sdk.Config;
import com.contentstack.sdk.Contentstack;
import com.contentstack.sdk.RetryOptions;
import com.contentstack.sdk.Stack;
public class ShortRetryDelay {
public static void main(String[] args) throws IllegalAccessException {
RetryOptions retryOptions = new RetryOptions().setRetryDelay(500);
// Attach the options before creating the stack
Config config = new Config();
config.setRetryOptions(retryOptions);
Stack stack = Contentstack.stack("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>", config);
System.out.println(config.getRetryOptions().getRetryDelay()); // 500
}
}Error handling: reject a negative delay
import com.contentstack.sdk.RetryOptions;
public class RejectRetryDelay {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions();
try {
retryOptions.setRetryDelay(-1);
} catch (IllegalArgumentException e) {
// Thrown at the call for a delay below 0
System.out.println("Invalid retry delay: " + e.getMessage());
}
System.out.println(retryOptions.getRetryDelay()); // 1000, the default, unchanged
}
}setRetryEnabled
setRetryEnabled turns the SDK's retry step on or off.
| Name | Type | Description |
|---|---|---|
| enabled (required) | boolean | true retries failed requests, false sends one attempt. |
Returns: RetryOptions. The same retry options, for chaining.
Validation
- There is no client-side validation.
Behavior
- The default is true.
- false makes the SDK send one attempt per request, whatever the limit, delay, strategy, and status list hold. A failure then goes to the callback at once.
- IMPORTANT: Set the value before you call Contentstack.stack(...). The SDK adds the retry step only when this value is true at stack creation. Calling setRetryEnabled(true) after creating a stack with the value false has no effect, and nothing reports it.
- Calling setRetryEnabled(false) on the attached options after stack creation stops retries from the next request.
- Passing null to the setRetryOptions method of Config also turns retries off.
Example
Basic usage: turn retries off
import com.contentstack.sdk.Config;
import com.contentstack.sdk.Contentstack;
import com.contentstack.sdk.RetryOptions;
import com.contentstack.sdk.Stack;
public class DisableRetries {
public static void main(String[] args) throws IllegalAccessException {
RetryOptions retryOptions = new RetryOptions().setRetryEnabled(false);
// Attach the options before creating the stack
Config config = new Config();
config.setRetryOptions(retryOptions);
Stack stack = Contentstack.stack("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>", config);
System.out.println(config.getRetryOptions().isRetryEnabled()); // false
}
}setRetryLimit
setRetryLimit sets the maximum number of times the SDK retries a failed request after the first attempt.
| Name | Type | Description |
|---|---|---|
| limit (required) | int | Maximum retries after the first attempt, from 0 to 10. |
Returns: RetryOptions. The same retry options, for chaining.
Validation
- Throws IllegalArgumentException if limit is below 0, because a retry count cannot be negative.
- Throws IllegalArgumentException if limit is above 10. The cap keeps one failing request from blocking the calling thread indefinitely.
- The exception comes from setRetryLimit itself, at the call. The options keep their previous limit.
Behavior
- The limit counts retries, not attempts. The SDK sends up to limit + 1 attempts, so the default of 3 sends up to four attempts.
- 0 turns retries off while leaving the retry step in place. The SDK then sends one attempt.
- The total wait grows with the limit. With the default EXPONENTIAL strategy and a limit of 10, the waits add up to 1023 times the base delay. That is about 17 minutes with the default 1000 ms delay.
- Read the value back with the getRetryLimit method.
Example
Basic usage: allow five retries
import com.contentstack.sdk.Config;
import com.contentstack.sdk.Contentstack;
import com.contentstack.sdk.RetryOptions;
import com.contentstack.sdk.Stack;
public class AllowFiveRetries {
public static void main(String[] args) throws IllegalAccessException {
RetryOptions retryOptions = new RetryOptions().setRetryLimit(5);
// Attach the options before creating the stack
Config config = new Config();
config.setRetryOptions(retryOptions);
Stack stack = Contentstack.stack("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>", config);
System.out.println(config.getRetryOptions().getRetryLimit()); // 5
}
}Error handling: reject a limit above 10
import com.contentstack.sdk.RetryOptions;
public class RejectRetryLimit {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions();
try {
retryOptions.setRetryLimit(11);
} catch (IllegalArgumentException e) {
// Thrown at the call for a limit below 0 or above 10
System.out.println("Invalid retry limit: " + e.getMessage());
}
System.out.println(retryOptions.getRetryLimit()); // 3, the default, unchanged
}
}setRetryableStatusCodes
setRetryableStatusCodes replaces the list of HTTP status codes that make the SDK retry a request.
| Name | Type | Description |
|---|---|---|
| codes (required) | int... | HTTP statuses that trigger a retry, each from 100 to 599. |
Returns: RetryOptions. The same retry options, for chaining.
Validation
- Throws IllegalArgumentException if any code is below 100 or above 599, because a value outside that range is not an HTTP status.
- The exception comes from setRetryableStatusCodes itself, at the call. The options keep their previous list, even when the other codes in the call are valid.
- No arguments, or a null array, is valid. It empties the list.
Behavior
- The default list is 408, 429, 502, 503, and 504.
- Each call replaces the whole list. It does not add to the current list, so include every code you want retried.
- The SDK copies the codes. Changing your array after the call does not change the options.
- An empty list stops retries on status codes. The SDK still retries network errors, such as a timeout, up to the retry limit. To stop every retry, use the setRetryEnabled method.
- A response with a status outside the list goes to the callback after one attempt.
- The SDK accepts any code from 100 to 599, including success codes. A 2xx code in the list makes the SDK retry successful responses.
Example
Basic usage: retry only on 429 and 503
import com.contentstack.sdk.Config;
import com.contentstack.sdk.Contentstack;
import com.contentstack.sdk.RetryOptions;
import com.contentstack.sdk.Stack;
import java.util.Arrays;
public class RetryOnRateLimit {
public static void main(String[] args) throws IllegalAccessException {
RetryOptions retryOptions = new RetryOptions().setRetryableStatusCodes(429, 503);
// Attach the options before creating the stack
Config config = new Config();
config.setRetryOptions(retryOptions);
Stack stack = Contentstack.stack("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>", config);
System.out.println(Arrays.toString(config.getRetryOptions().getRetryableStatusCodes())); // [429, 503]
}
}Edge case: retry network errors only
import com.contentstack.sdk.RetryOptions;
import java.util.Arrays;
public class RetryNetworkErrorsOnly {
public static void main(String[] args) {
// No codes: status retries stop, network error retries continue
RetryOptions retryOptions = new RetryOptions().setRetryableStatusCodes();
System.out.println(Arrays.toString(retryOptions.getRetryableStatusCodes())); // []
System.out.println(retryOptions.isRetryEnabled()); // true
}
}Error handling: reject a code outside the HTTP range
import com.contentstack.sdk.RetryOptions;
import java.util.Arrays;
public class RejectStatusCode {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions();
try {
retryOptions.setRetryableStatusCodes(429, 600);
} catch (IllegalArgumentException e) {
// Thrown at the call for any code below 100 or above 599
System.out.println("Invalid status code: " + e.getMessage());
}
System.out.println(Arrays.toString(retryOptions.getRetryableStatusCodes())); // the default list, unchanged
}
}toString
toString returns a one-line description of the retry settings, for logs.
Returns: String. A one-line summary of the settings.
Validation
- There is no client-side validation.
- toString does not throw.
Behavior
- The text has the shape RetryOptions{enabled=true, limit=3, delay=1000ms, strategy=EXPONENTIAL, retryableCodes=[408, 429, 502, 503, 504]}, shown here with the defaults.
- The text does not include the custom backoff strategy. A strategy=CUSTOM value means the options hold one.
- The SDK writes the same text to its java.util.logging log at level FINE when it adds the retry step to a new stack.
- Use the text for logs only. The format can change in any release, so read individual values with the getters.
Example
Basic usage: log the retry settings
import com.contentstack.sdk.RetryOptions;
public class LogRetryOptions {
public static void main(String[] args) {
RetryOptions retryOptions = new RetryOptions().setRetryLimit(5).setRetryDelay(500);
System.out.println(retryOptions);
// RetryOptions{enabled=true, limit=5, delay=500ms, strategy=EXPONENTIAL, retryableCodes=[408, 429, 502, 503, 504]}
}
}