RetryOptions

View as Markdown

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.

NameTypeDescription
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

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.

NameTypeDescription
strategy (required)RetryOptions.BackoffStrategy

Rule that turns the base delay into each wait.

Default: Not applicable

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.

NameTypeDescription
customStrategy (required)CustomBackoffStrategy

Calculates the wait in milliseconds before each retry.

Default: Not applicable

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.

NameTypeDescription
delayMs (required)long

Base wait between attempts, in milliseconds.

Default: Not applicable

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.

NameTypeDescription
enabled (required)boolean

true retries failed requests, false sends one attempt.

Default: Not applicable

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.

NameTypeDescription
limit (required)int

Maximum retries after the first attempt, from 0 to 10.

Default: Not applicable

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.

NameTypeDescription
codes (required)int...

HTTP statuses that trigger a retry, each from 100 to 599.

Default: Not applicable

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]}
    }
}