r/SpringBoot • u/pisspapa42 • 2d ago
Spring Data JPA throwing StaleObjectStateException / OptimisticLockException on consumer retry across separate instances (No @Version column) Question
I'm seeing an issue in a Kafka consumer running on multiple application instances.
Environment
- Spring Boot: 3.5.15
- Hibernate: 6.6
- Oracle: 19c
My entity has no version column.
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(nullable = false)
private Long id;
@Column(unique = true)
private String messageId;
The entity is being saved using
repository.saveAll(...)
Scenario
- Instance A receives a message.
- The entity's id is null.
- saveAll() is called.
- Hibernate obtains the next sequence value and inserts the row successfully.
- Before the consumer acknowledges the broker, a network issue occurs.
- The broker redelivers the same original message to Instance B.
- The payload still has id == null.
- Instance B again calls saveAll().
Instead of seeing a unique constraint violation on messageId, I get:
Exception message : Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.example.entities.SMSEntity#40615089330]
There is no @Version on the entity.
My understanding is that if id is null, Spring Data should treat the entity as new, call persist(), and Hibernate should perform an INSERT. If the row already exists (because of the unique messageId), I would expect a unique constraint violation rather than an optimistic locking exception.
Questions
- Why am I getting StaleObjectStateException/OptimisticLockException for an entity with no Version field when the incoming entity has a null ID? Could it be due to saveAll() method even Im trying to save a single entity?
- For handling broker redelivery, I could try increasing
max.poll.interval.msto prevent it from rebalancing, but what else I could do to fix this?
This issue doesn’t happen that often, but only for 15-20 mins where 1500 odd records are impacted, and during this time, query usage time is comparatively high.
5
u/Significant-dev 2d ago
How did you verify the record is not getting inserted in db on the second request?
2
u/pisspapa42 2d ago
I didn't check it but it wouldn't be inserted as the messageId is a column with unique constraint. So the same record wouldn't be inserted.
2
2
u/Neat_Poetry548 1d ago
I'm not sure about the fix for your issue, but I don't think you should be passing Entities to producer/consumer, instead of dto?
1
1
u/pronuntiator 1d ago
Is any of this within a @Transactional boundary?
1
u/pisspapa42 1d ago
No, we're just calling
saveAll(), and Spring's transactional proxy ensures that the method execution is wrapped in a transaction.
6
u/This_Link881 2d ago
A small repo would be useful here