The Programmer's Guide
  • About
  • Algorithm
    • Big O Notation
      • Tree
      • Problems
    • Basic Notes
    • Data Structure Implementation
      • Custom LinkedList
      • Custom Stack
      • Custom Queue
      • Custom Tree
        • Binary Tree Implementation
        • Binary Search Tree Implementation
        • Min Heap Implementation
        • Max Heap Implementation
        • Trie Implementation
      • Custom Graph
        • Adjacency List
        • Adjacency Matrix
        • Edge List
        • Bidirectional Search
    • Mathematical Algorithms
      • Problems - Set 1
      • Problems - Set 2
    • Bit Manipulation
      • Representation
      • Truth Tables
      • Number System
        • Java Program
      • Problems - Set 1
    • Searching
    • Sorting
    • Array Algorithms
    • String Algorithms
    • Tree
      • Tree Traversal Techniques
      • Tree Implementation
      • Applications of Trees
      • Problems - Set 1
    • Graph
      • Graph Traversal Techniques
      • Shortest Path Algorithms
      • Minimum Spanning Tree (MST) Algorithms
    • Dynamic Programming
      • Problems - Set 1
    • Recursion
    • Parallel Programming
    • Miscellaneous
      • Problems - Set 1
  • API
    • API Basics
      • What is an API?
      • Types of API
        • Comparison - TBU
      • Synchronous vs Asynchronous API
    • API Architecture
      • Synchronous & Asynchronous Communication
    • API Specification
  • Cloud Computing
    • Cloud Fundamentals
      • Cloud Terminology
      • Core Terminology
      • Cloud Models
      • Cloud Service Models
      • Benefits, Challenges and Risk of Cloud Computing
      • Cloud Ecosystem
  • Database
    • DBMS
      • Types of DBMS
        • Relational DBMS (RDBMS)
        • NoSQL DBMS
        • Object-Oriented DBMS (OODBMS)
        • Columnar DBMS
        • In-Memory DBMS
        • Distributed DBMS
        • Cloud-Based DBMS
        • Hierarchical DBMS
      • DBMS Architecture
      • DBMS Structure
    • SQL Databases
      • Terminology
      • RDBMS Concepts
        • Entity Relationship Diagram (ERD)
          • ERD Examples
        • Normalization
        • Denormalization
        • ACID & BASE Properties
          • ACID Properties
          • BASE Properties
        • Locking and Unlocking
      • SQL Fundamentals
        • SQL Commands
          • DDL (Data Definition Language)
          • DML (Data Manipulation Language)
          • DCL (Data Control Language)
          • TCL (Transaction Control Language)
          • DQL (Data Query Language)
        • SQL Operators
          • INTERSECT
          • EXCEPT
          • MINUS
          • IN and NOT IN
          • EXISTS and NOT EXISTS
        • SQL Clauses
          • Joins
          • OVER
          • WITH
          • CONNECT BY
          • MODEL
          • FETCH FIRST
          • KEEP
          • OFFSET with FETCH
        • SQL Functions
          • Oracle Specific
        • SQL Data Types
          • Numeric Types
          • Character Types
          • Date & Time Types
          • Large Object Types
        • Others
          • Indexing
      • Vendor Specific Concepts
        • Oracle Specific
          • Data Types
          • Character Set
          • Rownum, Rowid, Urowid
          • Order of Execution of the query
          • Keys
          • Tablespace
          • Partition
      • Best Practice
      • Resources & References
        • O’Reilly SQL Cookbook (2nd Edition)
          • 1. Retrieving Records
          • 2. Sorting Query Results
          • 3. Working with Multiple Tables
          • 4. Inserting, Updating, and Deleting
          • 5. Metadata Queries
          • 6. Working with Strings
          • 7. Working with Numbers
          • 8. Date Arithmetic
          • 9. Date Manipulation
          • 10. Working with Ranges
          • 11. Advanced Searching
          • 12. Reporting and Reshaping
          • 13. Hierarchical Queries
          • 14. Odds 'n' Ends
    • SQL vs NoSQL
    • Best Practices
  • Git
    • Commands
      • Setup and Configuration Commands
      • Getting and Creating Projects
      • Tracking Changes
      • Branching and Merging
      • Sharing and Updating Projects
      • Inspection and Comparison
      • Debugging
      • Patching
      • Stashing and Cleaning
      • Advanced Manipulations
    • Workflows
      • Branching Strategies
        • Git Flow
        • Trunk-Based Development
        • GitHub Flow
        • Comparison
      • Merge Strategies
        • Merge
        • Rebase
        • Squash
        • Fast-forward vs No-fast-forward
        • MR vs PR
      • Conflict Resolution
        • Handling Merge Conflicts
        • Merge Conflicts
        • Rebase Conflicts
        • Divergent Branches After git pull
        • Force Push
      • Patch & Recovery
        • Cherry-pick strategies
        • Revert vs Reset
        • Recover from a bad rebase
      • Rebasing Practices
        • Merge vs Rebase
        • Rebase develop branch on main branch
      • Repository Management
        • Working Directory
        • Mirror a repository
        • Convert a local folder to a Git repo
        • Backup and restore a Git repository
  • Java
    • Java Installation
    • Java Distributions
    • Java Platform Editions
      • Java SE
      • Java EE
      • Jakarta EE
      • Java ME
      • JavaFX
    • Java Overview
      • OOP Principles
        • Encapsulation
        • Inheritance
        • Polymorphism
        • Abstraction
          • Abstract Class & Method
          • Interface
            • Functional Interfaces
            • Marker Interfaces
          • Abstract Class vs Interface
      • OOP Basics
        • What is a Class?
          • Types of Classes
        • What is an Object?
          • Equals and HashCode
            • FAQ
          • Shallow Copy and Deep Copy
          • Ways to Create Object
          • Serialization & Deserialization
        • Methods & Fields
          • Method Overriding & Overloading
          • Method Signature & Header
          • Variables
        • Constructors
        • Access Modifiers
      • Parallelism & Concurrency
        • Ways to Identify Thread Concurrency or Parallelism
        • Thread Basics
          • Thread vs Process
          • Creating Threads
          • Thread Context Switching
          • Thread Lifecycle & States
          • Runnable & Callable
          • Types of Threads
          • Thread Priority
        • Thread Management & Synchronisation
          • Thread Resource Sharing
          • Thread Synchronization
            • Why is Synchronization Needed?
            • Synchronized Blocks & Methods
          • Thread Lock
            • Types of Locks
            • Intrinsic Lock (Monitor Lock)
            • Reentrant Lock
          • Semaphore
          • Thread Starvation
          • Thread Contention
          • Thread Deadlock
          • Best Practices for Avoiding Thread Issues
      • Keywords
        • this
        • super
        • Access Modifiers
      • Data Types
        • Default Values
        • Primitive Types
          • byte
          • short
          • int
          • long
          • float
          • double
          • char
          • boolean
        • Non-Primitive (Reference) Types
          • String
            • StringBuilder
            • StringBuffer
              • Problems
            • Multiline String
            • Comparison - String, StringBuilder & StringBuffer
          • Array
          • Collections
            • List
              • Array vs List
              • ArrayList
              • Vector
                • Stack
                  • Problems
              • LinkedList
            • Queue
              • PriorityQueue
              • Deque (Double-Ended Queue)
                • ArrayDeque
                • ConcurrentLinkedDeque - TBU
                • LinkedBlockingDeque - TBU
            • Map
              • HashMap
              • Hashtable
              • LinkedHashMap
              • ConcurrentHashMap
              • TreeMap
              • EnumMap
              • WeakHashMap
            • Set
              • HashSet
              • LinkedHashSet
              • TreeSet
              • EnumSet
              • ConcurrentSkipListSet
              • CopyOnWriteArraySet
        • Specialized Classes
          • BigInteger
          • BigDecimal
            • Examples
          • BitSet
          • Date and Time
            • Examples
          • Optional
          • Math
          • UUID
          • Scanner
          • Formatter
            • Examples
          • Properties
          • Regex (Pattern and Matcher)
            • Examples
          • Atomic Classes
          • Random
          • Format
            • NumberFormat
            • DateFormat
            • DecimalFormat
        • Others
          • Object
          • Enum
            • Pre-Defined Enum
            • Custom Enum
            • EnumSet and EnumMap
          • Record
          • Optional
          • System
          • Runtime
          • ProcessBuilder
          • Class
          • Void
          • Throwable
            • Error
            • Exception
              • Custom Exception Handling
              • Best Practice
            • Error vs Exception
            • StackTraceElement
    • Java Features by Version
      • How New Java Features are Released ?
      • Java Versions
        • Java 8
        • Java 9
        • Scoped Values
        • Unnamed Variables & Patterns
      • FAQ
    • Concepts
      • Set 1
        • Streams
          • flatmap
          • Collectors Utility Class
          • Problems
        • Functional Interfaces
          • Standard Built-In Interfaces
          • Custom Interfaces
        • Annotation
          • Custom Annotation
          • Meta Annotation
        • Generics
          • Covariance and Invariance
        • Asynchronous Computation
          • Future
          • CompletableFuture
          • Future v/s CompletableFuture
          • ExecutorService
            • Thread Pool
            • Types of Work Queues
            • Rejection Policies
            • ExecutorService Implementations
            • ExecutorService Usage
          • Locks, Atomic Variables, CountDownLatch, CyclicBarrier - TBU
          • Parallel Streams, Fork/Join Framework,Stream API with Parallelism - TBU
      • Set 2
        • Standards
          • ISO Standards
          • JSR
            • JSR 303, 349, 380 (Bean Validation)
        • Operator Precedence
      • Set 3
        • Date Time Formatter
        • Validation
      • Set 4
        • Input from User
        • Comparison & Ordering
          • Object Equality Check
          • Comparable and Comparator
            • Comparator Interface
          • Sorting of Objects
          • Insertion Ordering
    • Packages
      • Core Packages
        • java.lang
          • java.lang.System
          • java.lang.Thread
      • Jakarta Packages
        • jakarta.validation
        • javax.validation
      • Third-party Packages
    • Code Troubleshoot
      • Thread Dump
      • Heap Dump
    • Code Quality & Analysis
      • ArchUnit
      • Terminologies
        • Cyclic dependencies
    • Code Style
      • Naming Convention
      • Package Structure
      • Formatting
      • Comments and Documentation
      • Imports
      • Exception Handling
      • Class Structure
      • Method Guidelines
      • Page 1
      • Code Smells to Avoid
      • Lambdas and Streams Style
      • Tools
    • Tools
      • IntelliJ IDEA
        • Shortcuts for MAC
      • Apache JMeter
        • Examples
      • Thread Dump Capture
        • jstack
        • VisualVM - TBU
        • jcmd - TBU
        • JConsole - TBU
        • YourKit Java Profiler - TBU
        • Eclipse MAT - TBU
        • IntelliJ IDEA Profiler - TBU
        • AppDynamics - TBU
        • Dynatrace - TBU
        • Thread Dump Analyzers - TBU
      • Heap Dump Capture
        • jmap
        • VisualVM - TBU
        • jcmd - TBU
        • Eclipse MAT (Memory Analyzer Tool) - TBU
        • IntelliJ IDEA Profiler - TBU
        • YourKit Java Profiler - TBU
        • AppDynamics - TBU
        • Dynatrace - TBU
        • Kill -3 Command - TBU
        • jhat (Java Heap Analysis Tool) - TBU
        • JVM Options - TBU
      • Wireshark
        • Search Filters
    • Best Practices
      • Artifact and BOM Versioning
  • Maven
    • Installation
    • Local Repository & Configuration
    • Command-line Options
    • Build & Lifecycle
    • Dependency Management
      • Dependency
        • Transitive Dependency
        • Optional Dependency
      • Dependency Scope
        • Maven Lifecycle and Dependency Scope
      • Dependency Exclusions & Overrides
      • Bill of Materials (BOM)
      • Dependency Conflict Resolution
      • Dependency Tree & Analysis
      • Dependency Versioning Strategies
    • Plugins
      • Build Lifecycle Management
      • Dependency Management
      • Code Quality and Analysis
      • Documentation Generation
      • Code Generation
      • Packaging and Deployment
      • Reporting
      • Integration and Testing
      • Customization and Enhancement
        • build-helper-maven-plugin
        • properties-maven-plugin
        • ant-run plugin
        • exec-maven-plugin
        • gmavenplus-plugin
      • Performance Optimization
    • FAQs
      • Fixing Maven SSL Issues: Unable to Find Valid Certification Path
  • Spring
    • Spring Basics
      • What is Spring?
      • Why Use Spring
      • Spring Ecosystem
      • Versioning
      • Setting Up a Spring Project
    • Core Concepts
      • Spring Core
        • Dependency Injection (DI)
        • Stereotype Annotation
      • Spring Beans
        • Bean Lifecycle
        • Bean Scope
          • Singleton Bean
        • Lazy & Eager Initialization
          • Use Case of Lazy Initialization
        • BeanFactory
        • ApplicationContext
      • Spring Annotations
        • Spring Boot Specific
        • Controller Layer (Web & REST Controllers)
    • Spring Features
      • Auto Configuration
        • Spring Boot 2: spring.factories
        • Spring Boot 3: spring.factories
      • Spring Caching
        • In-Memory Caching
      • Spring AOP
        • Before Advice
        • After Returning Advice
        • After Throwing Advice
        • After (finally) Advice
        • Around Advice
      • Spring File Handling
      • Reactive Programming
        • Reactive System
        • Reactive Stream Specification
        • Project Reactor
          • Mono & Flux
      • Asynchronous Computation
        • @Async annotation
      • Spring Security
        • Authentication
          • Core Components
            • Security Filter Chain
              • HttpSecurity
              • Example
            • AuthenticationManager
            • AuthenticationProvider
            • UserDetailsService
              • UserDetails
              • PasswordEncoder
            • SecurityContext
            • SecurityContextHolder
            • GrantedAuthority
            • Security Configuration (Spring Security DSL)
          • Authentication Models
            • One-Way Authentication
            • Mutual Authentication
          • Authentication Mechanism
            • Basic Authentication
            • Form-Based Authentication
            • Token-Based Authentication (JWT)
            • OAuth2 Authentication
            • Multi-Factor Authentication (MFA)
            • SAML Authentication
            • X.509 Certificate Authentication
            • API Key Authentication
            • Remember-Me Authentication
            • Custom Authentication
          • Logout Handling
        • Authorization
        • Security Filters and Interceptors
        • CSRF
          • Real-World CSRF Attacks & Prevention
        • CORS
        • Session Management and Security
        • Best Practices
      • Spring Persistence
        • JDBC
          • JDBC Components
          • JDBC Template
          • Transaction Management
          • Best Practices in JDBC Usage
          • Datasource
            • Connection Pooling
              • HikariCP
            • Caching
        • JPA (Java Persistence API)
          • JPA Fundamentals
          • ORM Mapping Annotations
            • 1. Entity and Table Mappings
            • 2. Field/Column Mappings
            • 3. Relationship Mappings
            • 4. Inheritance Mappings
            • 5. Additional Configuration Annotations
          • Querying Data
            • JPQL
            • Criteria API
            • JPA Specification
              • Example - Employee Portal
            • Native SQL Queries
            • Named Queries
            • Query Return Types
            • Pagination & Sorting
              • Example - Employee Portal
            • Projection
          • Fetch Strategies in JPA
        • JPA Implementation
          • Hibernate
            • Properties
            • Example
        • Spring Data JPA
          • Repository Abstractions
          • Entity-to-Table Mapping
          • Derived Query Methods
        • Cross-Cutting Concerns
          • Transactions
          • Caching
          • Concurrency
        • Examples
          • Employee Portal
            • API
    • Distributed Systems & Communication
      • Distributed Scheduling
      • Inter-Service Communication
        • 1. RestTemplate
        • 2. WebClient
        • 3. OpenFeign
        • Retry Mechanism
          • @Retryable annotation
            • Example
    • Security & Data Protection
      • Encoding | Decoding
        • Types
          • Base Encoding
            • Base16 - TBD
              • Encoding and Decoding in Java - TBD
            • Base32
              • Encoding and Decoding in Java
            • Base64 -TBD
              • Encoding and Decoding in Java - TBD
          • Text Encoding - TBD
            • Extended ASCII
              • Encoding and Decoding in Java - TBD
                • ISO-8859-1
                • Windows-1252 - TBD
                • IBM Code Pages - TBD
            • ASCII
              • Encoding and Decoding in Java
        • Java Guidelines
          • Text Encoding Decoding Examples
          • Base Encoding Decoding Examples
          • Best Practices and Concepts
          • Libraries
      • Cryptography
        • Terminology
        • Java Cryptography Architecture (JCA)
        • Key Management
          • Key Generation
            • Tools and Libraries
              • OpenSSL
              • Java Keytool
                • Concept
                • Use Cases
            • Key & Certificate File Formats
          • Key Distribution
          • Key Storage
          • Key Rotation
          • Key Revocation
        • Encryption & Decryption
          • Symmetric Encryption
            • Algorithm
            • Modes of Operation
            • Examples
          • Asymmetric Encryption
            • Algorithm
            • Mode of Operation
            • Examples
    • Utilities & Libraries
      • Apache Libraries
        • Apache Camel
          • Camel Architecture
            • Camel Context
            • Camel Endpoints
            • Camel Components
            • Camel Exchange & MEP
          • Spring Dependency
          • Different Components
            • Camel SFTP
        • Apache Commons Lang
      • MapStruct Mapper
      • Utilities by Spring framework
        • FileCopyUtils
    • General Concepts
      • Spring Boot Artifact Packaging
      • Classpath and Resource Loading
      • Configuration - Mapping Properties to Java Class
      • Validations in Spring Framework
        • Jakarta Validation
          • Jakarta Bean Validation Annotations
    • Practical Guidelines
      • Spring Configuration
      • Spring Code Design
  • Software Testing
    • Software Testing Methodologies
      • Functional Testing
      • Non Functional Testing
    • Software Testing Life Cycle (STLC)
    • Integration Test
      • Dynamic Property Registration
    • Java Test Framework
      • JUnit
        • JUnit 4
          • Examples
        • JUnit 5
          • Examples
        • JUnit 4 vs JUnit 5
  • System Design
    • Foundations
      • Programming Paradigms
      • Object-Oriented Design
        • SOLID Principles
        • GRASP Principles
        • Composition
        • Aggregation
        • Association
      • Design Pattern
        • Creational Pattern
        • Structural Pattern
        • Behavioral Pattern
        • Examples
          • Data Collector
          • Payment Processor
        • Design Enhancements
          • Fluent API Design
            • Examples
    • Architectural Building Blocks
      • CAP Theorem
      • Load Balancer
        • Load Balancer Architecture
        • Load Balancing in Java Microservices
          • Client-Side Load Balancing Example
          • Server-Side Load Balancing Example
        • Load Balancer Monitoring Tool
      • Scaling
        • Vertical Scaling (Scaling Up)
        • Horizontal Scaling (Scaling Out)
        • Auto-Scaling
        • Database Scaling via Sharding
      • Caching
        • Pod-Level vs Distributed Caching
      • Networking Metrics
        • Types of Delay
        • Scenario
      • System Characteristics
      • Workload Types
      • Resilience & Failure Handling
    • Performance
      • Why Is My API Sometimes Slow ?
    • Security
      • Security by Design
      • Zero Trust Security Model
      • Zero Trust Architecture
      • Principles
        • CIA
        • Least Privilege Principle
        • Defense in Depth
      • Security Threats & Mitigations
        • OWASP
          • Top 10 Security Threats
          • Application Security Verification Standard
          • Software Assurance Maturity Model
          • Dependency Check
          • CSRFGuard
          • Cheat Sheets
          • Security Testing Guide
          • Threat Dragon
        • Threat Modeling
      • Compliance & Regulation
        • PCI DSS
    • Deployment Patterns
    • Diagrams
      • UML Diagrams
        • PlantUML
          • Class Diagram
          • Object Diagram
          • Sequence Diagram
          • Use Case Diagram
          • Activity Diagram
          • State Diagram
          • Architecture Diagram
          • Component Diagram
          • Timing Diagram
          • ER Diagram (Entity-Relationship)
          • Network Diagram
    • Common Terminologies
    • Problems
      • Reference Materials
      • Cache Design
  • Interview Guide
    • Non-Technical
      • Behavioural or Introductory Guide
      • Project Specific
    • Technical
      • Java Interview Companion
        • Java Key Concepts
          • Set 1
          • Set 2
        • Java Code Snippets
        • Java Practice Programs
          • Set 3 - Strings
          • Set 4 - Search
          • Set 5 - Streams and Collection
      • SQL Interview Companion
        • SQL Practice Problems
          • Set 1
      • Spring Interview Companion
        • Spring Key Concepts
          • Set 1 - General
          • Set 2 - Core Spring
        • Spring Code Snippets
          • JPA
      • Application Server
      • Maven
      • Containerized Application
      • Microservices
    • General
      • Applicant Tracking System (ATS)
      • Flowchart - How to Solve Coding Problem?
Powered by GitBook
On this page
  • 1. What is Spring Retry?
  • 2. What is @Retryable?
  • 3. What is @Recover?
  • 4. What is RetryTemplate?
  • 5. What is Retry Interceptor?
  • 6. Example
  • Basic Usage of @Retryable
  • Stateful vs Stateless Retries Example
  • 6. Best Practices

Was this helpful?

  1. Spring
  2. Distributed Systems & Communication
  3. Inter-Service Communication
  4. Retry Mechanism

@Retryable annotation

1. What is Spring Retry?

Spring Retry is a module that provides declarative retry support in Spring applications. It allows methods to be automatically re-invoked when they throw exceptions. Spring Retry provides a flexible API for retry policies, recovery logic, and handling exceptions.

Spring Dependency to add

 <dependency>
      <groupId>org.springframework.retry</groupId>
      <artifactId>spring-retry</artifactId>
 </dependency>
 <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
 </dependency>

We need enable retry feature before we can use @Retryable

// EnableRetry annotaion will enable spring boot retry pattern and then 
// only @Retryable annotation will work
@SpringBootApplication
@EnableRetry
public class MainApplication {
   public static void main(String[] args) {
    SpringApplication.run(RetryApplication.class, args);
   }
}

2. What is @Retryable?

@Retryable is a declarative annotation provided by Spring Retry that can be applied to any method in a Spring-managed bean to automatically retry that method when an exception occurs. The annotation can be customized with several attributes to define the retry behavior, including:

  • backoff: Defines the backoff policy for retries (delay and multiplier).

  • exceptionExpression: A SpEL (Spring Expression Language) expression to determine if the retry should occur based on the exception thrown.

  • exclude and include: Specifies the list of exceptions that will trigger retries. We can also define exceptions that will not trigger retries.

  • interceptor: Define retry interceptors for custom retry logic. A retry interceptor is used to customize or extend the behavior of the retry process. It intercepts each retry attempt and can add custom logic.

  • label: Define a label for better tracking.

  • stateful: If true, retry state is tracked for each invocation. This is useful for stateful retries where we don’t want to retry if the method succeeded once. Stateful retries keep track of the state of the method being retried across multiple attempts, allowing the retry process to resume where it left off.

  • maxAttempts: Defines how many times the operation should be retried before giving up.

  • maxAttemptsExpression: A dynamic expression to calculate max attempts.

  • value: The main exceptions that trigger retry logic.

In order to use @Retryable,our method needs to called from outside of the class, because under the hood @Retryable makes use of spring's AOP which makes use of proxy to call retires on our target method.

Retry Policy:

Retry policies define the logic of when to retry and how. Some common retry policies include:

  • Simple retry policy: Retries a method a fixed number of times.

  • Exception classifier retry policy: Retries based on different exceptions. Different retry behaviors can be applied for different exceptions.

  • Timeout retry policy: Retries the method until a specific timeout period is reached.

By default, Spring Retry uses a simple retry policy with a max attempt of 3, but this can be customized.

Stateful vs Stateless Retry:

  • Stateless retry: Each method invocation is considered independent. If a method succeeds after retries, future invocations will still start from scratch.

    • Every retry starts fresh.

    • No memory of previous progress.

    • Useful for simple, non-idempotent tasks that need to restart fully on failure.

  • Stateful retry: Keeps track of the state between method invocations. If the method already succeeded for a particular input, it won’t retry again for the same input. To use stateful retry, we need to set the stateful attribute to true.

    • Retries resume from where they left off.

    • Retains state across retry attempts.

    • Best for cases where progress must not be lost (e.g., partial success should not be redone).

The stateful retry mechanism in Spring only maintains state within the current application context or runtime session. If the application restarts (in-memory state is lost), the retry state is lost unless we implement a way to persist this state externally.

Without persistence, stateful retries will not resume where they left off after a system restart. They will start the retry process from scratch, as if it’s the first attempt. To make stateful retries resilient to restarts, we would need to persist retry state externally, in a database or other persistent storage. This allows the application to resume the retry sequence even after a restart.

Backoff Strategies :

  • Fixed backoff: A constant delay between retries.

@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 2000))

  • Exponential backoff: The delay increases exponentially after each failure.

@Retryable(maxAttempts = 5, backoff = @Backoff(delay = 1000, multiplier = 2))

3. What is @Recover?

If all retry attempts fail, Spring allows us to define a recovery method using the @Recover annotation. This method is invoked after all retries are exhausted, acting as a fallback mechanism.

@Service
public class SampleService {

    @Retryable(
        value = { RemoteServiceNotAvailableException.class }, 
        maxAttempts = 3, 
        backoff = @Backoff(delay = 2000)
    )
    public String callExternalService() {
        // Call to external service
        throw new RemoteServiceNotAvailableException("Service unavailable.");
    }

    @Recover
    public String recover(RemoteServiceNotAvailableException e) {
        return "Service is currently unavailable. Please try again later.";
    }
}

4. What is RetryTemplate?

RetryTemplate allows programmatic control over retry logic, making it suitable for complex applications where flexible, fine-grained retry handling is required as compared to the @Retryable annotation.

RetryTemplateExample.java class

package org.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.RetryPolicy;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.ExceptionClassifierRetryPolicy;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.HttpClientErrorException;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class RetryTemplateExample {

    @Bean
    public RetryTemplate createRetryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();

        // Setting up Simple Retry Policy
        // SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        // retryPolicy.setMaxAttempts(3); // Max attempts for retries
        // retryTemplate.setRetryPolicy(retryPolicy);

        // Setting up ExceptionClassifierRetryPolicy Retry Policy
        ExceptionClassifierRetryPolicy policy = new ExceptionClassifierRetryPolicy();
        Map<Class<? extends Throwable>, RetryPolicy> policyMap = new HashMap<>();
        policyMap.put(HttpClientErrorException.class, new SimpleRetryPolicy(3));
        policyMap.put(IOException.class, new NeverRetryPolicy()); // No retry for IOException
        policy.setPolicyMap(policyMap);
        retryTemplate.setRetryPolicy(policy);

        // Setting up Backoff Policy
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(500); // Initial wait time (milliseconds)
        backOffPolicy.setMultiplier(2); // Exponential factor
        backOffPolicy.setMaxInterval(5000); // Max wait time between retries
        retryTemplate.setBackOffPolicy(backOffPolicy);

        return retryTemplate;
    }
}

Usage of RetryTemplate in service class

package org.example.service;

import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;

@RequiredArgsConstructor
@Service
public class SampleService {
    private final RetryTemplate retryTemplate;

    public void processOrder(String orderId) {
        retryTemplate.execute(context -> {
            System.out.println("Attempt: " + (context.getRetryCount() + 1));

            // Simulated order processing
            attemptOrderProcessing(orderId);

            System.out.println("Order processed successfully.");
            return null;
        }, context -> {
            // Recovery logic if retries are exhausted
            System.out.println("Failed to process order after retries. Initiating recovery for order: " + orderId);
            return null;
        });
    }

    private void attemptOrderProcessing(String orderId) throws HttpClientErrorException {
        // Simulate failure
        throw new HttpClientErrorException(HttpStatus.SERVICE_UNAVAILABLE, "Temporary failure in processing order");
    }
}

Result Output

2024-11-05T12:37:33.148+05:30  INFO 8676 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
Attempt: 1
Attempt: 2
Attempt: 3
Failed to process order after retries. Initiating recovery for order: 123456

We can add Retry Listeners which are useful for tracking the retry process, like logging attempts or handling specific actions on retries.

5. What is Retry Interceptor?

Retry interceptors in Spring Retry allow's us to customize and extend the behavior of retry operations. They are part of the Spring Retry infrastructure and act as middleware, intercepting each retry attempt to execute custom logic before or after each attempt. This makes interceptors powerful tools for enhancing retries with features like monitoring, logging, or even dynamically altering retry behavior.

  • Interception of Retry Attempts:

    • Interceptors capture each retry attempt and allow additional logic to be applied, such as logging retries, updating counters, or sending alerts.

    • For example, we can log each retry attempt with details like the exception causing the retry, the retry count, and the method being retried.

  • Customizing Retry Logic:

    • Interceptors provide hooks to adjust retry logic dynamically, such as modifying backoff settings or retry conditions on-the-fly based on custom logic.

    • We can implement specific actions on each retry (e.g., throttling if the retry count exceeds a limit).

  • Retry Listeners:

    • Spring Retry includes RetryListener, a specific type of interceptor for adding logic at different stages of retry attempts:

      • Before Retry: Logic before the first retry attempt.

      • On Retry: Logic on each retry attempt (e.g., logging each attempt).

      • On Recovery: Logic after all retry attempts are exhausted (e.g., sending alerts).

CustomRetryListener.java class

package org.example.config;

import org.springframework.retry.listener.MethodInvocationRetryListenerSupport;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryCallback;
import org.springframework.stereotype.Component;

@Component
public class CustomRetryListener extends MethodInvocationRetryListenerSupport {

    @Override
    public <T, E extends Throwable> void onError(
            RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        System.out.println("Retry attempt: " + context.getRetryCount() + " due to: " + throwable.getMessage());
    }

    @Override
    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
        System.out.println("Starting retry operation for: " + context.getAttribute("context.name"));
        return true;  // Allow retries
    }

    @Override
    public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        System.out.println("Retry operation completed.");
    }
}

RetryTemplateConfig.java class

package org.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.RetryListener;
import org.springframework.retry.RetryPolicy;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.interceptor.RetryInterceptorBuilder;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
import org.springframework.retry.policy.ExceptionClassifierRetryPolicy;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.HttpClientErrorException;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class RetryTemplateConfig {

    @Bean
    public RetryTemplate createRetryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();

        // Setting up Simple Retry Policy
        // SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        //retryPolicy.setMaxAttempts(3); // Max attempts for retries
        //retryTemplate.setRetryPolicy(retryPolicy);

        // Setting up ExceptionClassifierRetryPolicy Retry Policy
        ExceptionClassifierRetryPolicy policy = new ExceptionClassifierRetryPolicy();
        Map<Class<? extends Throwable>, RetryPolicy> policyMap = new HashMap<>();
        policyMap.put(HttpClientErrorException.class, new SimpleRetryPolicy(3));
        policyMap.put(IOException.class, new NeverRetryPolicy()); // No retry for IOException
        policy.setPolicyMap(policyMap);
        retryTemplate.setRetryPolicy(policy);

        // Setting up Backoff Policy
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(500); // Initial wait time (milliseconds)
        backOffPolicy.setMultiplier(2); // Exponential factor
        backOffPolicy.setMaxInterval(5000); // Max wait time between retries
        retryTemplate.setBackOffPolicy(backOffPolicy);

        // Setting up listener
        retryTemplate.setListeners(new RetryListener[]{new CustomRetryListener()});

        return retryTemplate;
    }

    @Bean
    public RetryOperationsInterceptor retryInterceptor() {
        return RetryInterceptorBuilder.stateless()
                .retryOperations(createRetryTemplate())
                .build();
    }
}

SampleService.java class

package org.example.service;

import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;

@RequiredArgsConstructor
@Service
public class SampleService {
    private int attempt = 0;

    @Retryable(interceptor = "retryInterceptor")
    public void processOrder(String orderId) throws HttpClientErrorException {
        attempt++;
        System.out.println("Stateless attempt " + attempt + " for order: " + orderId);

        // Simulating failure
        if (attempt < 3) {
            // Simulate partial progress and throw exception
            throw new HttpClientErrorException(HttpStatus.SERVICE_UNAVAILABLE, "Failed to process order!");
        }

        // Successful processing (only on the 3rd attempt)
        System.out.println("Order processed successfully on attempt " + attempt);
    }
}

Hit the service class method via any controller class and observe the output.

Result Output

2024-11-05T13:20:25.566+05:30  INFO 7776 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
Starting retry operation for: null
Stateless attempt 1 for order: 123456
Retry attempt: 1 due to: 503 Failed to process order!
Stateless attempt 2 for order: 123456
Retry attempt: 2 due to: 503 Failed to process order!
Stateless attempt 3 for order: 123456
Order processed successfully on attempt 3
Retry operation completed.

6. Example

Basic Usage of @Retryable

@Service
public class SampleService {

    // Retries occur when RemoteServiceNotAvailableException is thrown.
    // Maximum number of retry attempts is 3
    // Delay between retry attempts is 2000ms
    @Retryable(
        value = { RemoteServiceNotAvailableException.class }, 
        maxAttempts = 3, 
        backoff = @Backoff(delay = 2000)
    )
    public String callExternalService() {
        // Simulate call to external service
        if (Math.random() > 0.5) {
            return "Success";
        } else {
            throw new RemoteServiceNotAvailableException("Service unavailable.");
        }
    }
}

Stateful vs Stateless Retries Example

Below are two examples that demonstrate the difference between stateful and stateless retries using a scenario where we process orders. The state of the retry in each case determines whether it remembers the current progress or starts fresh on each retry.

Stateful Retry

package org.example.service;

import org.springframework.http.HttpStatus;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;

@Service
public class SampleService {
    private int attempt = 0;

    // Stateful retry example
    @Retryable(retryFor = { HttpClientErrorException.class }, maxAttempts = 3, stateful = true)
    public void processOrder(String orderId) throws HttpClientErrorException {
        attempt++;
        System.out.println("Stateful attempt " + attempt + " for order: " + orderId);

        // Simulating failure
        if (attempt < 3) {
            // Simulate partial progress and throw exception
            throw new HttpClientErrorException(HttpStatus.SERVICE_UNAVAILABLE, "Failed to process order!");
        }

        // Successful processing (only on the 3rd attempt)
        System.out.println("Order processed successfully on attempt " + attempt);
    }
}

Suppose a controller class calls the above service class method then

  • Attempt 1 result output for the API call: Failure (but progress remembered)

2024-11-05T10:36:29.343+05:30  INFO 13924 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
Stateful attempt 1 for order: 123456
2024-11-05T10:36:30.418+05:30 ERROR 13924 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.web.client.HttpClientErrorException: 503 Failed to process order!] with root cause

org.springframework.web.client.HttpClientErrorException: 503 Failed to process order!
	at org.example.service.SampleService.processOrder(SampleService.java:22) ~[classes/:na]
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) ~[spring-aop-6.0.18.jar:6.0.18]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.0.18.jar:6.0.18]
  • Attempt 2 result output for the API call: Failure (progress still remembered)

Stateful attempt 2 for order: 123456
2024-11-05T10:37:07.015+05:30 ERROR 13924 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.web.client.HttpClientErrorException: 503 Failed to process order!] with root cause

org.springframework.web.client.HttpClientErrorException: 503 Failed to process order!
	at org.example.service.SampleService.processOrder(SampleService.java:22) ~[classes/:na]
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) ~[spring-aop-6.0.18.jar:6.0.18]
  • Attempt 3 result output for the API call: Success (without redoing previous work)

Stateful attempt 3 for order: 123456
Order processed successfully on attempt 3
  • The retry state (orderId) ensures that the retry behavior applies specifically to the same orderId.

  • If we were to call processOrder with a different orderId, it would treat it as a separate retry sequence and start fresh with attempt resetting to 1.

  • stateful = true is effective for cases like this where it’s critical that retries apply uniquely to specific operations, ensuring that if there's partial progress, it remembers and avoids redundant processing.

Stateful Retry with Persistence (to cover application restart scenario)

package org.example.service;

import org.example.repository.RetryStateRepository;
import org.springframework.http.HttpStatus;
import org.springframework.retry.RetryState;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;

@Service
public class SampleService {

    @Autowired
    private RetryStateRepository retryStateRepository; // Custom repository for storing retry states

    @Retryable(
        value = { HttpClientErrorException.class },
        maxAttempts = 3,
        stateful = true
    )
    public void processOrder(String orderId) throws HttpClientErrorException {
        int attempt = loadAttemptFromDB(orderId); // Load the last attempt count from DB
        attempt++;
        System.out.println("Stateful attempt " + attempt + " for order: " + orderId);

        // Simulating failure
        if (attempt < 3) {
            // Save the attempt count to DB before throwing an exception
            saveAttemptToDB(orderId, attempt);
            throw new HttpClientErrorException(HttpStatus.SERVICE_UNAVAILABLE, "Failed to process order!");
        }

        // Successful processing
        System.out.println("Order processed successfully on attempt " + attempt);
        clearRetryState(orderId); // Clear retry state on success
    }

    // Helper methods to interact with persistent storage
    private int loadAttemptFromDB(String orderId) {
        return retryStateRepository.findAttemptsByOrderId(orderId).orElse(0);
    }

    private void saveAttemptToDB(String orderId, int attempt) {
        retryStateRepository.save(new RetryState(orderId, attempt));
    }

    private void clearRetryState(String orderId) {
        retryStateRepository.deleteByOrderId(orderId);
    }
}

Stateless Retry

package org.example.service;

import org.springframework.http.HttpStatus;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;

@Service
public class SampleService {
    private int attempt = 0;

    // Stateful retry example
    @Retryable(retryFor = { HttpClientErrorException.class }, maxAttempts = 3, stateful = false)
    public void processOrder(String orderId) throws HttpClientErrorException {
        attempt++;
        System.out.println("Stateless attempt " + attempt + " for order: " + orderId);

        // Simulating failure
        if (attempt < 3) {
            // Simulate partial progress and throw exception
            throw new HttpClientErrorException(HttpStatus.SERVICE_UNAVAILABLE, "Failed to process order!");
        }

        // Successful processing (only on the 3rd attempt)
        System.out.println("Order processed successfully on attempt " + attempt);
    }
}

Result output for the API call

2024-11-05T10:44:33.302+05:30  INFO 10048 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-11-05T10:44:33.302+05:30  INFO 10048 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2024-11-05T10:44:33.303+05:30  INFO 10048 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
Stateless attempt 1 for order: 123456
Stateless attempt 2 for order: 123456
Stateless attempt 3 for order: 123456
Order processed successfully on attempt 3

6. Best Practices

  1. Retry Only for Transient Failures: Ensure that we’re only retrying for transient failures (e.g., network issues, timeouts) and not for permanent errors (e.g., validation errors).

  2. Limit Retry Attempts: Avoid indefinite retries. Always cap the number of retry attempts or use a timeout to prevent the application from being stuck in a retry loop.

  3. Backoff Strategy: Use an exponential backoff strategy to reduce load on external services during failures.

  4. Recovery Fallbacks: Always provide a recovery method for better user experience in case retries are exhausted.

PreviousRetry MechanismNextExample

Last updated 7 months ago

Was this helpful?