Editing pass for retry.adoc
I made it more readable and consistent and fixed up sentence errors. I added a TODO for a spot that could benefit from an example or two. polishing on merge
This commit is contained in:
@@ -6,10 +6,17 @@
|
||||
|
||||
== Retry
|
||||
|
||||
To make processing more robust and less prone to failure, it sometimes
|
||||
helps to automatically retry a failed operation in case it might
|
||||
succeed on a subsequent attempt. Errors that are susceptible to intermittent failure
|
||||
are often transient in nature. Examples include remote calls to a web
|
||||
service that fails because of a network glitch or a
|
||||
`DeadlockLoserDataAccessException` in a database update.
|
||||
|
||||
[[retryTemplate]]
|
||||
|
||||
|
||||
=== RetryTemplate
|
||||
=== `RetryTemplate`
|
||||
|
||||
|
||||
[NOTE]
|
||||
@@ -19,16 +26,9 @@ The retry functionality was pulled out of Spring Batch as of 2.2.0.
|
||||
====
|
||||
|
||||
|
||||
To make processing more robust and less prone to failure, sometimes
|
||||
it helps to automatically retry a failed operation in case it might
|
||||
succeed on a subsequent attempt. Errors that are susceptible to this kind
|
||||
of treatment are transient in nature. For example a remote call to a web
|
||||
service or RMI service that fails because of a network glitch or a
|
||||
`DeadlockLoserDataAccessException` in a database update may
|
||||
resolve themselves after a short wait. To automate the retry of such
|
||||
To automate retry
|
||||
operations Spring Batch has the `RetryOperations`
|
||||
strategy. The `RetryOperations` interface looks like
|
||||
this:
|
||||
strategy. The following interface definition for `RetryOperations`:
|
||||
|
||||
|
||||
[source, java]
|
||||
@@ -49,8 +49,8 @@ public interface RetryOperations {
|
||||
}
|
||||
----
|
||||
|
||||
The basic callback is a simple interface that allows you to
|
||||
insert some business logic to be retried:
|
||||
The basic callback is a simple interface that lets you
|
||||
insert some business logic to be retried, as shown in the following interface definition:
|
||||
|
||||
|
||||
[source, java]
|
||||
@@ -62,18 +62,18 @@ public interface RetryCallback<T> {
|
||||
}
|
||||
----
|
||||
|
||||
The callback is executed and if it fails (by throwing an
|
||||
`Exception`), it will be retried until either it is
|
||||
successful, or the implementation decides to abort. There are a number of
|
||||
The callback runs and, if it fails (by throwing an
|
||||
`Exception`), it is retried until either it is
|
||||
successful or the implementation aborts. There are a number of
|
||||
overloaded `execute` methods in the
|
||||
`RetryOperations` interface dealing with various use
|
||||
cases for recovery when all retry attempts are exhausted, and also with
|
||||
`RetryOperations` interface. Those methods deal with various use
|
||||
cases for recovery when all retry attempts are exhausted and deal with
|
||||
retry state, which allows clients and implementations to store information
|
||||
between calls (more on this later).
|
||||
between calls (we cover this in more detail later in the chapter).
|
||||
|
||||
The simplest general purpose implementation of
|
||||
`RetryOperations` is
|
||||
`RetryTemplate`. It could be used like this
|
||||
`RetryTemplate`. It can be used as follows:
|
||||
|
||||
|
||||
[source, java]
|
||||
@@ -95,21 +95,21 @@ Foo result = template.execute(new RetryCallback<Foo>() {
|
||||
});
|
||||
----
|
||||
|
||||
In the example we execute a web service call and return the result
|
||||
to the user. If that call fails then it is retried until a timeout is
|
||||
In the preceding example, we make a web service call and return the result
|
||||
to the user. If that call fails, then it is retried until a timeout is
|
||||
reached.
|
||||
|
||||
[[retryContext]]
|
||||
|
||||
|
||||
==== RetryContext
|
||||
==== `RetryContext`
|
||||
|
||||
The method parameter for the `RetryCallback`
|
||||
is a `RetryContext`. Many callbacks will simply
|
||||
ignore the context, but if necessary it can be used as an attribute bag
|
||||
is a `RetryContext`. Many callbacks
|
||||
ignore the context, but, if necessary, it can be used as an attribute bag
|
||||
to store data for the duration of the iteration.
|
||||
|
||||
A `RetryContext` will have a parent context
|
||||
A `RetryContext` has a parent context
|
||||
if there is a nested retry in progress in the same thread. The parent
|
||||
context is occasionally useful for storing data that need to be shared
|
||||
between calls to `execute`.
|
||||
@@ -117,13 +117,13 @@ A `RetryContext` will have a parent context
|
||||
[[recoveryCallback]]
|
||||
|
||||
|
||||
==== RecoveryCallback
|
||||
==== `RecoveryCallback`
|
||||
|
||||
When a retry is exhausted the
|
||||
When a retry is exhausted, the
|
||||
`RetryOperations` can pass control to a different
|
||||
callback, the `RecoveryCallback`. To use this
|
||||
feature clients just pass in the callbacks together to the same method,
|
||||
for example:
|
||||
callback, called the `RecoveryCallback`. To use this
|
||||
feature, clients pass in the callbacks together to the same method,
|
||||
as shown in the following example:
|
||||
|
||||
|
||||
[source, java]
|
||||
@@ -148,7 +148,7 @@ If the business logic does not succeed before the template
|
||||
|
||||
==== Stateless Retry
|
||||
|
||||
In the simplest case, a retry is just a while loop: the
|
||||
In the simplest case, a retry is just a while loop. The
|
||||
`RetryTemplate` can just keep trying until it
|
||||
either succeeds or fails. The `RetryContext`
|
||||
contains some state to determine whether to retry or abort, but this
|
||||
@@ -157,7 +157,7 @@ In the simplest case, a retry is just a while loop: the
|
||||
stateless and stateful retry is contained in the implementation of the
|
||||
`RetryPolicy` (the
|
||||
`RetryTemplate` can handle both). In a stateless
|
||||
retry, the callback is always executed in the same thread on retry as
|
||||
retry, the retry callback is always executed in the same thread it was on
|
||||
when it failed.
|
||||
|
||||
[[statefulRetry]]
|
||||
@@ -169,23 +169,23 @@ Where the failure has caused a transactional resource to become
|
||||
invalid, there are some special considerations. This does not apply to a
|
||||
simple remote call because there is no transactional resource (usually),
|
||||
but it does sometimes apply to a database update, especially when using
|
||||
Hibernate. In this case it only makes sense to rethrow the exception
|
||||
that called the failure immediately so that the transaction can roll
|
||||
back and we can start a new valid one.
|
||||
Hibernate. In this case it only makes sense to re-throw the exception
|
||||
that called the failure immediately, so that the transaction can roll
|
||||
back and we can start a new, valid transaction.
|
||||
|
||||
In these cases a stateless retry is not good enough because the
|
||||
In cases involving transactions, a stateless retry is not good enough, because the
|
||||
re-throw and roll back necessarily involve leaving the
|
||||
`RetryOperations.execute()` method and potentially losing the
|
||||
context that was on the stack. To avoid losing it we have to introduce a
|
||||
storage strategy to lift it off the stack and put it (at a minimum) in
|
||||
heap storage. For this purpose Spring Batch provides a storage strategy
|
||||
`RetryContextCache` which can be injected into the
|
||||
heap storage. For this purpose, Spring Batch provides a storage strategy called
|
||||
`RetryContextCache`, which can be injected into the
|
||||
`RetryTemplate`. The default implementation of the
|
||||
`RetryContextCache` is in memory, using a simple
|
||||
`Map`. Advanced usage with multiple processes in a
|
||||
clustered environment might also consider implementing the
|
||||
`RetryContextCache` with a cluster cache of some
|
||||
sort (though, even in a clustered environment this might be
|
||||
sort (however, even in a clustered environment, this might be
|
||||
overkill).
|
||||
|
||||
Part of the responsibility of the
|
||||
@@ -194,14 +194,14 @@ Part of the responsibility of the
|
||||
in a new transaction). To facilitate this, Spring Batch provides the
|
||||
`RetryState` abstraction. This works in conjunction
|
||||
with a special `execute` methods in the
|
||||
`RetryOperations`.
|
||||
`RetryOperations` interface.
|
||||
|
||||
The way the failed operations are recognized is by identifying the
|
||||
state across multiple invocations of the retry. To identify the state,
|
||||
the user can provide an `RetryState` object that is
|
||||
the user can provide a `RetryState` object that is
|
||||
responsible for returning a unique key identifying the item. The
|
||||
identifier is used as a key in the
|
||||
`RetryContextCache`.
|
||||
`RetryContextCache` interface.
|
||||
|
||||
|
||||
[WARNING]
|
||||
@@ -210,13 +210,13 @@ Be very careful with the implementation of
|
||||
`Object.equals()` and `Object.hashCode()` in the
|
||||
key returned by `RetryState`. The best advice is
|
||||
to use a business key to identify the items. In the case of a JMS
|
||||
message the message ID can be used.
|
||||
message, the message ID can be used.
|
||||
====
|
||||
|
||||
|
||||
When the retry is exhausted there is also the option to handle the
|
||||
When the retry is exhausted, there is also the option to handle the
|
||||
failed item in a different way, instead of calling the
|
||||
`RetryCallback` (which is presumed now to be likely
|
||||
`RetryCallback` (which is now presumed to be likely
|
||||
to fail). Just like in the stateless case, this option is provided by
|
||||
the `RecoveryCallback`, which can be provided by
|
||||
passing it in to the `execute` method of
|
||||
@@ -224,28 +224,28 @@ When the retry is exhausted there is also the option to handle the
|
||||
|
||||
The decision to retry or not is actually delegated to a regular
|
||||
`RetryPolicy`, so the usual concerns about limits
|
||||
and timeouts can be injected there (see below).
|
||||
and timeouts can be injected there (described later in this chapter).
|
||||
|
||||
[[retryPolicies]]
|
||||
|
||||
|
||||
=== Retry Policies
|
||||
|
||||
Inside a `RetryTemplate` the decision to retry
|
||||
Inside a `RetryTemplate`, the decision to retry
|
||||
or fail in the `execute` method is determined by a
|
||||
`RetryPolicy` which is also a factory for the
|
||||
`RetryPolicy`, which is also a factory for the
|
||||
`RetryContext`. The
|
||||
`RetryTemplate` has the responsibility to use the
|
||||
current policy to create a `RetryContext` and pass
|
||||
that in to the `RetryCallback` at every attempt.
|
||||
After a callback fails the `RetryTemplate` has to
|
||||
After a callback fails, the `RetryTemplate` has to
|
||||
make a call to the `RetryPolicy` to ask it to update
|
||||
its state (which will be stored in the
|
||||
`RetryContext`), and then it asks the policy if
|
||||
another attempt can be made. If another attempt cannot be made (e.g. a
|
||||
its state (which is stored in the
|
||||
`RetryContext`) and then asks the policy if
|
||||
another attempt can be made. If another attempt cannot be made (such as when a
|
||||
limit is reached or a timeout is detected) then the policy is also
|
||||
responsible for handling the exhausted state. Simple implementations will
|
||||
just throw `RetryExhaustedException` which will cause
|
||||
responsible for handling the exhausted state. Simple implementations
|
||||
throw `RetryExhaustedException`, which causes
|
||||
any enclosing transaction to be rolled back. More sophisticated
|
||||
implementations might attempt to take some recovery action, in which case
|
||||
the transaction can remain intact.
|
||||
@@ -253,27 +253,26 @@ Inside a `RetryTemplate` the decision to retry
|
||||
|
||||
[TIP]
|
||||
====
|
||||
Failures are inherently either retryable or not - if the same
|
||||
Failures are inherently either retryable or not. If the same
|
||||
exception is always going to be thrown from the business logic, it
|
||||
doesn't help to retry it. So don't retry on all exception types - try to
|
||||
focus on only those exceptions that you expect to be retryable. It's not
|
||||
does no good to retry it. So do not retry on all exception types. Rather, try to
|
||||
focus on only those exceptions that you expect to be retryable. It is not
|
||||
usually harmful to the business logic to retry more aggressively, but
|
||||
it's wasteful because if a failure is deterministic there will be time
|
||||
spent retrying something that you know in advance is fatal.
|
||||
it is wasteful, because, if a failure is deterministic, you spend time
|
||||
retrying something that you know in advance is fatal.
|
||||
====
|
||||
|
||||
|
||||
Spring Batch provides some simple general purpose implementations of
|
||||
stateless `RetryPolicy`, for example a
|
||||
`SimpleRetryPolicy`, and the
|
||||
`TimeoutRetryPolicy` used in the example
|
||||
above.
|
||||
stateless `RetryPolicy`, such as
|
||||
`SimpleRetryPolicy` and
|
||||
`TimeoutRetryPolicy` (used in the preceding example).
|
||||
|
||||
The `SimpleRetryPolicy` just allows a retry on
|
||||
The `SimpleRetryPolicy` allows a retry on
|
||||
any of a named list of exception types, up to a fixed number of times. It
|
||||
also has a list of "fatal" exceptions that should never be retried, and
|
||||
this list overrides the retryable list so that it can be used to give
|
||||
finer control over the retry behavior:
|
||||
finer control over the retry behavior, as shown in the following example:
|
||||
|
||||
|
||||
[source, java]
|
||||
@@ -301,13 +300,13 @@ There is also a more flexible implementation called
|
||||
user to configure different retry behavior for an arbitrary set of
|
||||
exception types though the `ExceptionClassifier`
|
||||
abstraction. The policy works by calling on the classifier to convert an
|
||||
exception into a delegate `RetryPolicy`, so for
|
||||
exception into a delegate `RetryPolicy`. For
|
||||
example, one exception type can be retried more times before failure than
|
||||
another by mapping it to a different policy.
|
||||
|
||||
Users might need to implement their own retry policies for more
|
||||
customized decisions. For instance, if there is a well-known,
|
||||
solution-specific, classification of exceptions into retryable and not
|
||||
customized decisions. For instance, a custom retry policy makes sense when there is a well-known,
|
||||
solution-specific classification of exceptions into retryable and not
|
||||
retryable.
|
||||
|
||||
[[backoffPolicies]]
|
||||
@@ -315,13 +314,14 @@ Users might need to implement their own retry policies for more
|
||||
|
||||
=== Backoff Policies
|
||||
|
||||
When retrying after a transient failure it often helps to wait a bit
|
||||
When retrying after a transient failure, it often helps to wait a bit
|
||||
before trying again, because usually the failure is caused by some problem
|
||||
that will only be resolved by waiting. If a
|
||||
that can only be resolved by waiting. If a
|
||||
`RetryCallback` fails, the
|
||||
`RetryTemplate` can pause execution according to the
|
||||
`BackoffPolicy` in place.
|
||||
`BackoffPolicy`.
|
||||
|
||||
The following code shows the interface definition for the `BackOffPolicy` interface:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@@ -339,8 +339,8 @@ A `BackoffPolicy` is free to implement
|
||||
the backOff in any way it chooses. The policies provided by Spring Batch
|
||||
out of the box all use `Object.wait()`. A common use case is to
|
||||
backoff with an exponentially increasing wait period, to avoid two retries
|
||||
getting into lock step and both failing - this is a lesson learned from
|
||||
the ethernet. For this purpose Spring Batch provides the
|
||||
getting into lock step and both failing (this is a lesson learned from
|
||||
ethernet). For this purpose, Spring Batch provides the
|
||||
`ExponentialBackoffPolicy`.
|
||||
|
||||
[[retryListeners]]
|
||||
@@ -348,16 +348,16 @@ A `BackoffPolicy` is free to implement
|
||||
|
||||
=== Listeners
|
||||
|
||||
Often it is useful to be able to receive additional callbacks for
|
||||
Often, it is useful to be able to receive additional callbacks for
|
||||
cross cutting concerns across a number of different retries. For this
|
||||
purpose Spring Batch provides the `RetryListener`
|
||||
interface. The `RetryTemplate` allows users to
|
||||
register `RetryListeners`, and they will be given
|
||||
callbacks with the `RetryContext` and
|
||||
purpose, Spring Batch provides the `RetryListener`
|
||||
interface. The `RetryTemplate` lets users
|
||||
register `RetryListeners`, and they are given
|
||||
callbacks with `RetryContext` and
|
||||
`Throwable` where available during the
|
||||
iteration.
|
||||
|
||||
The interface looks like this:
|
||||
The following code shows the interface definition for `RetryListener`:
|
||||
|
||||
|
||||
[source, java]
|
||||
@@ -374,35 +374,35 @@ public interface RetryListener {
|
||||
|
||||
The `open` and
|
||||
`close` callbacks come before and after the entire
|
||||
retry in the simplest case and `onError` applies to
|
||||
retry in the simplest case, and `onError` applies to
|
||||
the individual `RetryCallback` calls. The
|
||||
`close` method might also receive a
|
||||
`Throwable`; if there has been an error it is the
|
||||
`Throwable`. If there has been an error, it is the
|
||||
last one thrown by the `RetryCallback`.
|
||||
|
||||
Note that when there is more than one listener, they are in a list,
|
||||
so there is an order. In this case `open` will be
|
||||
Note that, when there is more than one listener, they are in a list,
|
||||
so there is an order. In this case, `open` is
|
||||
called in the same order while `onError` and
|
||||
`close` will be called in reverse order.
|
||||
`close` are called in reverse order.
|
||||
|
||||
[[declarativeRetry]]
|
||||
|
||||
|
||||
=== Declarative Retry
|
||||
|
||||
Sometimes there is some business processing that you know you want
|
||||
Sometimes, there is some business processing that you know you want
|
||||
to retry every time it happens. The classic example of this is the remote
|
||||
service call. Spring Batch provides an AOP interceptor that wraps a method
|
||||
call in a `RetryOperations` for just this purpose.
|
||||
call in a `RetryOperations` implementation for just this purpose.
|
||||
The `RetryOperationsInterceptor` executes the
|
||||
intercepted method and retries on failure according to the
|
||||
`RetryPolicy` in the provided
|
||||
`RepeatTemplate`.
|
||||
|
||||
Here is an example of declarative iteration using the Spring AOP
|
||||
The following example shows a declarative iteration that uses the Spring AOP
|
||||
namespace to repeat a service call to a method called
|
||||
`remoteCall` (for more detail on how to configure
|
||||
AOP interceptors see the Spring User Guide):
|
||||
AOP interceptors, see the Spring User Guide):
|
||||
|
||||
|
||||
[source, xml]
|
||||
@@ -418,8 +418,7 @@ Here is an example of declarative iteration using the Spring AOP
|
||||
class="org.springframework.batch.retry.interceptor.RetryOperationsInterceptor"/>
|
||||
----
|
||||
|
||||
The example above uses a default
|
||||
The preceding example uses a default
|
||||
`RetryTemplate` inside the interceptor. To change the
|
||||
policies or listeners, you only need to inject an instance of
|
||||
policies or listeners, you can inject an instance of
|
||||
`RetryTemplate` into the interceptor.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user