Spring Data rollback transaction on retry
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
In Spring Data services, retry and transaction behavior must be designed together. If retry attempts run inside the same failed transaction context, rollback behavior becomes confusing and side effects may leak. A reliable setup starts a fresh transaction per attempt, defines rollback rules explicitly, and keeps retried operations idempotent.
Understand Transaction and Retry Boundaries
Retries should generally happen around transactional method execution, not inside one long transaction.
Typical intent:
- Attempt one runs in transaction one.
- Failure triggers rollback of transaction one.
- Attempt two runs in new transaction two.
This model keeps failures isolated.
Basic Annotation Setup
Use @Retryable with @Transactional on service entry method.
Also enable retry support in configuration with @EnableRetry.
Configure Rollback Rules Explicitly
Spring rolls back on unchecked exceptions by default. If checked exceptions should roll back, configure rollbackFor.
Mismatch between thrown exception types and rollback rules is a common cause of partial commits.
Handle Proxy Boundaries Correctly
Retry and transaction annotations rely on Spring proxies. Internal method calls inside same class can bypass proxies and disable expected behavior.
Move annotated methods to separate beans or call through proxied components when necessary.
This is critical in legacy service classes with self-invocation patterns.
Keep Retried Operations Idempotent
Retries can duplicate writes or external calls if operation is not idempotent. Protect with:
- Unique business keys.
- State transition guards.
- Outbox deduplication identifiers.
Idempotency is required even when rollback works correctly because external systems may not roll back with your database.
Isolate External Side Effects
Do not combine non-idempotent external API calls and DB writes in one naive retry loop. Use outbox or compensating workflows so retries do not duplicate external side effects.
A practical pattern:
- Commit business event to outbox in transaction.
- Publish externally from outbox worker.
- Retry publish with idempotency key.
This keeps transaction and integration concerns separated.
Add Observability for Retry Behavior
Without telemetry, retry issues are hard to debug. Log attempt number and exception class, and expose metrics such as retry count and recover count.
Alerting on retry spikes helps detect dependency instability early.
Integration Tests for Correctness
Test matrix should include:
- One transient failure then success.
- Repeated transient failure reaching recover path.
- Non-retryable exception path.
- Checked exception rollback behavior.
These tests confirm both retry semantics and final database state.
Operational Tuning
Set conservative max attempts and backoff. Over-aggressive retries can create retry storms during outages.
Combine retries with circuit breakers and timeout policies. Retry is one resilience tool, not full failure strategy.
Document retryable exceptions in one shared policy to keep behavior consistent across services.
Common Pitfalls
- Retrying inside one transaction instead of per-attempt transaction scope.
- Forgetting rollback rules for checked exceptions.
- Self-invocation bypassing proxy-based retry behavior.
- Retrying operations that are not idempotent.
- Missing logs and metrics for retry outcomes.
Summary
- Design retry and transaction boundaries as one unit.
- Prefer fresh transaction per retry attempt.
- Configure rollback rules to match actual exception types.
- Ensure retried operations are idempotent.
- Validate behavior with integration tests and production telemetry.
Related reading
- Spring data with cassandra giving IllegalStateException
- Spring embeddeb db table already exists error
- Spring Hibernate Query Plan Cache Memory usage
- Spring is losing connection to the DB and does not recover or reconnect
- Spring Embedded Kafka + Mock Schema Registry State Store ChangeLog Schema not registered
- Spring Expression Language SpEL check empty string?
- Spring JPA selecting specific columns
- Spring nested transactions

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.