(window.webpackJsonp=window.webpackJsonp||[]).push([[33],{461:function(e,t,a){"use strict";a.r(t);var r=a(56),o=Object(r.a)({},(function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[a("h1",{attrs:{id:"retry"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#retry"}},[e._v("#")]),e._v(" Retry")]),e._v(" "),a("h2",{attrs:{id:"retry-2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#retry-2"}},[e._v("#")]),e._v(" Retry")]),e._v(" "),a("p",[e._v("XMLJavaBoth")]),e._v(" "),a("p",[e._v("To make processing more robust and less prone to failure, it sometimes helps to\nautomatically retry a failed operation in case it might succeed on a subsequent attempt.\nErrors that are susceptible to intermittent failure are often transient in nature.\nExamples include remote calls to a web service that fails because of a network glitch or a"),a("code",[e._v("DeadlockLoserDataAccessException")]),e._v(" in a database update.")]),e._v(" "),a("h3",{attrs:{id:"retrytemplate"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#retrytemplate"}},[e._v("#")]),e._v(" "),a("code",[e._v("RetryTemplate")])]),e._v(" "),a("table",[a("thead",[a("tr",[a("th"),e._v(" "),a("th",[e._v("The retry functionality was pulled out of Spring Batch as of 2.2.0."),a("br"),e._v("It is now part of a new library, "),a("a",{attrs:{href:"https://github.com/spring-projects/spring-retry",target:"_blank",rel:"noopener noreferrer"}},[e._v("Spring Retry"),a("OutboundLink")],1),e._v(".")])])]),e._v(" "),a("tbody")]),e._v(" "),a("p",[e._v("To automate retry operations Spring Batch has the "),a("code",[e._v("RetryOperations")]),e._v(" strategy. The\nfollowing interface definition for "),a("code",[e._v("RetryOperations")]),e._v(":")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v("public interface RetryOperations {\n\n T execute(RetryCallback retryCallback) throws E;\n\n T execute(RetryCallback retryCallback, RecoveryCallback recoveryCallback)\n throws E;\n\n T execute(RetryCallback retryCallback, RetryState retryState)\n throws E, ExhaustedRetryException;\n\n T execute(RetryCallback retryCallback, RecoveryCallback recoveryCallback,\n RetryState retryState) throws E;\n\n}\n")])])]),a("p",[e._v("The basic callback is a simple interface that lets you insert some business logic to be\nretried, as shown in the following interface definition:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v("public interface RetryCallback {\n\n T doWithRetry(RetryContext context) throws E;\n\n}\n")])])]),a("p",[e._v("The callback runs and, if it fails (by throwing an "),a("code",[e._v("Exception")]),e._v("), it is retried until\neither it is successful or the implementation aborts. There are a number of overloaded"),a("code",[e._v("execute")]),e._v(" methods in the "),a("code",[e._v("RetryOperations")]),e._v(" interface. Those methods deal with various use\ncases for recovery when all retry attempts are exhausted and deal with retry state, which\nlets clients and implementations store information between calls (we cover this in more\ndetail later in the chapter).")]),e._v(" "),a("p",[e._v("The simplest general purpose implementation of "),a("code",[e._v("RetryOperations")]),e._v(" is "),a("code",[e._v("RetryTemplate")]),e._v(". It\ncan be used as follows:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v("RetryTemplate template = new RetryTemplate();\n\nTimeoutRetryPolicy policy = new TimeoutRetryPolicy();\npolicy.setTimeout(30000L);\n\ntemplate.setRetryPolicy(policy);\n\nFoo result = template.execute(new RetryCallback() {\n\n public Foo doWithRetry(RetryContext context) {\n // Do stuff that might fail, e.g. webservice operation\n return result;\n }\n\n});\n")])])]),a("p",[e._v("In the preceding example, we make a web service call and return the result to the user. If\nthat call fails, then it is retried until a timeout is reached.")]),e._v(" "),a("h4",{attrs:{id:"retrycontext"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#retrycontext"}},[e._v("#")]),e._v(" "),a("code",[e._v("RetryContext")])]),e._v(" "),a("p",[e._v("The method parameter for the "),a("code",[e._v("RetryCallback")]),e._v(" is a "),a("code",[e._v("RetryContext")]),e._v(". Many callbacks ignore\nthe context, but, if necessary, it can be used as an attribute bag to store data for the\nduration of the iteration.")]),e._v(" "),a("p",[e._v("A "),a("code",[e._v("RetryContext")]),e._v(" has a parent context if there is a nested retry in progress in the same\nthread. The parent context is occasionally useful for storing data that need to be shared\nbetween calls to "),a("code",[e._v("execute")]),e._v(".")]),e._v(" "),a("h4",{attrs:{id:"recoverycallback"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#recoverycallback"}},[e._v("#")]),e._v(" "),a("code",[e._v("RecoveryCallback")])]),e._v(" "),a("p",[e._v("When a retry is exhausted, the "),a("code",[e._v("RetryOperations")]),e._v(" can pass control to a different callback,\ncalled the "),a("code",[e._v("RecoveryCallback")]),e._v(". To use this feature, clients pass in the callbacks together\nto the same method, as shown in the following example:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v("Foo foo = template.execute(new RetryCallback() {\n public Foo doWithRetry(RetryContext context) {\n // business logic here\n },\n new RecoveryCallback() {\n Foo recover(RetryContext context) throws Exception {\n // recover logic here\n }\n});\n")])])]),a("p",[e._v("If the business logic does not succeed before the template decides to abort, then the\nclient is given the chance to do some alternate processing through the recovery callback.")]),e._v(" "),a("h4",{attrs:{id:"stateless-retry"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#stateless-retry"}},[e._v("#")]),e._v(" Stateless Retry")]),e._v(" "),a("p",[e._v("In the simplest case, a retry is just a while loop. The "),a("code",[e._v("RetryTemplate")]),e._v(" can just keep\ntrying until it either succeeds or fails. The "),a("code",[e._v("RetryContext")]),e._v(" contains some state to\ndetermine whether to retry or abort, but this state is on the stack and there is no need\nto store it anywhere globally, so we call this stateless retry. The distinction between\nstateless and stateful retry is contained in the implementation of the "),a("code",[e._v("RetryPolicy")]),e._v(" (the"),a("code",[e._v("RetryTemplate")]),e._v(" can handle both). In a stateless retry, the retry callback is always\nexecuted in the same thread it was on when it failed.")]),e._v(" "),a("h4",{attrs:{id:"stateful-retry"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#stateful-retry"}},[e._v("#")]),e._v(" Stateful Retry")]),e._v(" "),a("p",[e._v("Where the failure has caused a transactional resource to become invalid, there are some\nspecial considerations. This does not apply to a simple remote call because there is no\ntransactional resource (usually), but it does sometimes apply to a database update,\nespecially when using Hibernate. In this case it only makes sense to re-throw the\nexception that called the failure immediately, so that the transaction can roll back and\nwe can start a new, valid transaction.")]),e._v(" "),a("p",[e._v("In cases involving transactions, a stateless retry is not good enough, because the\nre-throw and roll back necessarily involve leaving the "),a("code",[e._v("RetryOperations.execute()")]),e._v(" method\nand potentially losing the context that was on the stack. To avoid losing it we have to\nintroduce a storage strategy to lift it off the stack and put it (at a minimum) in heap\nstorage. For this purpose, Spring Batch provides a storage strategy called"),a("code",[e._v("RetryContextCache")]),e._v(", which can be injected into the "),a("code",[e._v("RetryTemplate")]),e._v(". The default\nimplementation of the "),a("code",[e._v("RetryContextCache")]),e._v(" is in memory, using a simple "),a("code",[e._v("Map")]),e._v(". Advanced\nusage with multiple processes in a clustered environment might also consider implementing\nthe "),a("code",[e._v("RetryContextCache")]),e._v(" with a cluster cache of some sort (however, even in a clustered\nenvironment, this might be overkill).")]),e._v(" "),a("p",[e._v("Part of the responsibility of the "),a("code",[e._v("RetryOperations")]),e._v(" is to recognize the failed operations\nwhen they come back in a new execution (and usually wrapped in a new transaction). To\nfacilitate this, Spring Batch provides the "),a("code",[e._v("RetryState")]),e._v(" abstraction. This works in\nconjunction with a special "),a("code",[e._v("execute")]),e._v(" methods in the "),a("code",[e._v("RetryOperations")]),e._v(" interface.")]),e._v(" "),a("p",[e._v("The way the failed operations are recognized is by identifying the state across multiple\ninvocations of the retry. To identify the state, the user can provide a "),a("code",[e._v("RetryState")]),e._v("object that is responsible for returning a unique key identifying the item. The identifier\nis used as a key in the "),a("code",[e._v("RetryContextCache")]),e._v(" interface.")]),e._v(" "),a("table",[a("thead",[a("tr",[a("th"),e._v(" "),a("th",[e._v("Be very careful with the implementation of "),a("code",[e._v("Object.equals()")]),e._v(" and "),a("code",[e._v("Object.hashCode()")]),e._v(" in"),a("br"),e._v("the key returned by "),a("code",[e._v("RetryState")]),e._v(". The best advice is to use a business key to identify the"),a("br"),e._v("items. In the case of a JMS message, the message ID can be used.")])])]),e._v(" "),a("tbody")]),e._v(" "),a("p",[e._v("When the retry is exhausted, there is also the option to handle the failed item in a\ndifferent way, instead of calling the "),a("code",[e._v("RetryCallback")]),e._v(" (which is now presumed to be likely\nto fail). Just like in the stateless case, this option is provided by the"),a("code",[e._v("RecoveryCallback")]),e._v(", which can be provided by passing it in to the "),a("code",[e._v("execute")]),e._v(" method of"),a("code",[e._v("RetryOperations")]),e._v(".")]),e._v(" "),a("p",[e._v("The decision to retry or not is actually delegated to a regular "),a("code",[e._v("RetryPolicy")]),e._v(", so the\nusual concerns about limits and timeouts can be injected there (described later in this\nchapter).")]),e._v(" "),a("h3",{attrs:{id:"retry-policies"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#retry-policies"}},[e._v("#")]),e._v(" Retry Policies")]),e._v(" "),a("p",[e._v("Inside a "),a("code",[e._v("RetryTemplate")]),e._v(", the decision to retry or fail in the "),a("code",[e._v("execute")]),e._v(" method is\ndetermined by a "),a("code",[e._v("RetryPolicy")]),e._v(", which is also a factory for the "),a("code",[e._v("RetryContext")]),e._v(". The"),a("code",[e._v("RetryTemplate")]),e._v(" has the responsibility to use the current policy to create a"),a("code",[e._v("RetryContext")]),e._v(" and pass that in to the "),a("code",[e._v("RetryCallback")]),e._v(" at every attempt. After a callback\nfails, the "),a("code",[e._v("RetryTemplate")]),e._v(" has to make a call to the "),a("code",[e._v("RetryPolicy")]),e._v(" to ask it to update its\nstate (which is stored in the "),a("code",[e._v("RetryContext")]),e._v(") and then asks the policy if another attempt\ncan be made. If another attempt cannot be made (such as when a limit is reached or a\ntimeout is detected) then the policy is also responsible for handling the exhausted state.\nSimple implementations throw "),a("code",[e._v("RetryExhaustedException")]),e._v(", which causes any enclosing\ntransaction to be rolled back. More sophisticated implementations might attempt to take\nsome recovery action, in which case the transaction can remain intact.")]),e._v(" "),a("table",[a("thead",[a("tr",[a("th"),e._v(" "),a("th",[e._v("Failures are inherently either retryable or not. If the same exception is always going to"),a("br"),e._v("be thrown from the business logic, it does no good to retry it. So do not retry on all"),a("br"),e._v("exception types. Rather, try to focus on only those exceptions that you expect to be"),a("br"),e._v("retryable. It is not usually harmful to the business logic to retry more aggressively, but"),a("br"),e._v("it is wasteful, because, if a failure is deterministic, you spend time retrying something"),a("br"),e._v("that you know in advance is fatal.")])])]),e._v(" "),a("tbody")]),e._v(" "),a("p",[e._v("Spring Batch provides some simple general purpose implementations of stateless"),a("code",[e._v("RetryPolicy")]),e._v(", such as "),a("code",[e._v("SimpleRetryPolicy")]),e._v(" and "),a("code",[e._v("TimeoutRetryPolicy")]),e._v(" (used in the preceding example).")]),e._v(" "),a("p",[e._v("The "),a("code",[e._v("SimpleRetryPolicy")]),e._v(' allows a retry on any of a named list of exception types, up to a\nfixed number of times. It also has a list of "fatal" exceptions that should never be\nretried, and this list overrides the retryable list so that it can be used to give finer\ncontrol over the retry behavior, as shown in the following example:')]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v("SimpleRetryPolicy policy = new SimpleRetryPolicy();\n// Set the max retry attempts\npolicy.setMaxAttempts(5);\n// Retry on all exceptions (this is the default)\npolicy.setRetryableExceptions(new Class[] {Exception.class});\n// ... but never retry IllegalStateException\npolicy.setFatalExceptions(new Class[] {IllegalStateException.class});\n\n// Use the policy...\nRetryTemplate template = new RetryTemplate();\ntemplate.setRetryPolicy(policy);\ntemplate.execute(new RetryCallback() {\n public Foo doWithRetry(RetryContext context) {\n // business logic here\n }\n});\n")])])]),a("p",[e._v("There is also a more flexible implementation called "),a("code",[e._v("ExceptionClassifierRetryPolicy")]),e._v(",\nwhich lets the user configure different retry behavior for an arbitrary set of exception\ntypes though the "),a("code",[e._v("ExceptionClassifier")]),e._v(" abstraction. The policy works by calling on the\nclassifier to convert an exception into a delegate "),a("code",[e._v("RetryPolicy")]),e._v(". For example, one\nexception type can be retried more times before failure than another by mapping it to a\ndifferent policy.")]),e._v(" "),a("p",[e._v("Users might need to implement their own retry policies for more customized decisions. For\ninstance, a custom retry policy makes sense when there is a well-known, solution-specific\nclassification of exceptions into retryable and not retryable.")]),e._v(" "),a("h3",{attrs:{id:"backoff-policies"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#backoff-policies"}},[e._v("#")]),e._v(" Backoff Policies")]),e._v(" "),a("p",[e._v("When retrying after a transient failure, it often helps to wait a bit before trying again,\nbecause usually the failure is caused by some problem that can only be resolved by\nwaiting. If a "),a("code",[e._v("RetryCallback")]),e._v(" fails, the "),a("code",[e._v("RetryTemplate")]),e._v(" can pause execution according to\nthe "),a("code",[e._v("BackoffPolicy")]),e._v(".")]),e._v(" "),a("p",[e._v("The following code shows the interface definition for the "),a("code",[e._v("BackOffPolicy")]),e._v(" interface:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v("public interface BackoffPolicy {\n\n BackOffContext start(RetryContext context);\n\n void backOff(BackOffContext backOffContext)\n throws BackOffInterruptedException;\n\n}\n")])])]),a("p",[e._v("A "),a("code",[e._v("BackoffPolicy")]),e._v(" is free to implement the backOff in any way it chooses. The policies\nprovided by Spring Batch out of the box all use "),a("code",[e._v("Object.wait()")]),e._v(". A common use case is to\nbackoff with an exponentially increasing wait period, to avoid two retries getting into\nlock step and both failing (this is a lesson learned from ethernet). For this purpose,\nSpring Batch provides the "),a("code",[e._v("ExponentialBackoffPolicy")]),e._v(".")]),e._v(" "),a("h3",{attrs:{id:"listeners"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#listeners"}},[e._v("#")]),e._v(" Listeners")]),e._v(" "),a("p",[e._v("Often, it is useful to be able to receive additional callbacks for cross cutting concerns\nacross a number of different retries. For this purpose, Spring Batch provides the"),a("code",[e._v("RetryListener")]),e._v(" interface. The "),a("code",[e._v("RetryTemplate")]),e._v(" lets users register "),a("code",[e._v("RetryListeners")]),e._v(", and\nthey are given callbacks with "),a("code",[e._v("RetryContext")]),e._v(" and "),a("code",[e._v("Throwable")]),e._v(" where available during the\niteration.")]),e._v(" "),a("p",[e._v("The following code shows the interface definition for "),a("code",[e._v("RetryListener")]),e._v(":")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v("public interface RetryListener {\n\n boolean open(RetryContext context, RetryCallback callback);\n\n void onError(RetryContext context, RetryCallback callback, Throwable throwable);\n\n void close(RetryContext context, RetryCallback callback, Throwable throwable);\n}\n")])])]),a("p",[e._v("The "),a("code",[e._v("open")]),e._v(" and "),a("code",[e._v("close")]),e._v(" callbacks come before and after the entire retry in the simplest\ncase, and "),a("code",[e._v("onError")]),e._v(" applies to the individual "),a("code",[e._v("RetryCallback")]),e._v(" calls. The "),a("code",[e._v("close")]),e._v(" method\nmight also receive a "),a("code",[e._v("Throwable")]),e._v(". If there has been an error, it is the last one thrown by\nthe "),a("code",[e._v("RetryCallback")]),e._v(".")]),e._v(" "),a("p",[e._v("Note that, when there is more than one listener, they are in a list, so there is an order.\nIn this case, "),a("code",[e._v("open")]),e._v(" is called in the same order while "),a("code",[e._v("onError")]),e._v(" and "),a("code",[e._v("close")]),e._v(" are called in\nreverse order.")]),e._v(" "),a("h3",{attrs:{id:"declarative-retry"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#declarative-retry"}},[e._v("#")]),e._v(" Declarative Retry")]),e._v(" "),a("p",[e._v("Sometimes, there is some business processing that you know you want to retry every time it\nhappens. The classic example of this is the remote service call. Spring Batch provides an\nAOP interceptor that wraps a method call in a "),a("code",[e._v("RetryOperations")]),e._v(" implementation for just\nthis purpose. The "),a("code",[e._v("RetryOperationsInterceptor")]),e._v(" executes the intercepted method and retries\non failure according to the "),a("code",[e._v("RetryPolicy")]),e._v(" in the provided "),a("code",[e._v("RepeatTemplate")]),e._v(".")]),e._v(" "),a("p",[e._v("The following example shows a declarative retry that uses the Spring AOP namespace to\nretry a service call to a method called "),a("code",[e._v("remoteCall")]),e._v(" (for more detail on how to configure\nAOP interceptors, see the Spring User Guide):")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('\n \n \n\n\n\n')])])]),a("p",[e._v("The following example shows a declarative retry that uses java configuration to retry a\nservice call to a method called "),a("code",[e._v("remoteCall")]),e._v(" (for more detail on how to configure AOP\ninterceptors, see the Spring User Guide):")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('@Bean\npublic MyService myService() {\n\tProxyFactory factory = new ProxyFactory(RepeatOperations.class.getClassLoader());\n\tfactory.setInterfaces(MyService.class);\n\tfactory.setTarget(new MyService());\n\n\tMyService service = (MyService) factory.getProxy();\n\tJdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();\n\tpointcut.setPatterns(".*remoteCall.*");\n\n\tRetryOperationsInterceptor interceptor = new RetryOperationsInterceptor();\n\n\t((Advised) service).addAdvisor(new DefaultPointcutAdvisor(pointcut, interceptor));\n\n\treturn service;\n}\n')])])]),a("p",[e._v("The preceding example uses a default "),a("code",[e._v("RetryTemplate")]),e._v(" inside the interceptor. To change the\npolicies or listeners, you can inject an instance of "),a("code",[e._v("RetryTemplate")]),e._v(" into the interceptor.")])])}),[],!1,null,null,null);t.default=o.exports}}]);