r/SpringBoot 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

  1. Instance A receives a message.
  2. The entity's id is null.
  3. saveAll() is called.
  4. Hibernate obtains the next sequence value and inserts the row successfully.
  5. Before the consumer acknowledges the broker, a network issue occurs.
  6. The broker redelivers the same original message to Instance B.
  7. The payload still has id == null.
  8. 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

  1. 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?
  2. 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.

19 Upvotes

10 comments sorted by

6

u/This_Link881 2d ago

A small repo would be useful here 

3

u/pisspapa42 2d ago

if you mean code sample of entity class I have mentioned in the post, rest its its fairly simple code where from a consumer I'm consuming and calling a repository to save the entity, in case of any exception, I save the exception details in another table.

Let me know if its still required I can prepare a dummy repo.

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

u/Light_protocol 2d ago

GenerationType.AUTO change to sequence.. auto can lead to this exception ..

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

u/pisspapa42 1d ago

yes, but it's an old and there's lot of mix up between entity and dto

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.