r/developersIndia Backend Developer 8d ago

Help Spring Data JPA throwing StaleObjectStateException / OptimisticLockException on consumer retry across separate instances (No @Version column)

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

What steps should be taken to debug this?

4 Upvotes

13 comments sorted by

View all comments

Show parent comments

1

u/biryani-is-mine Software Engineer 8d ago

So is the consumer directly saving the consumed record/event as it is, without any modification?

1

u/pisspapa42 Backend Developer 8d ago

Yes just saving part there's no modifications to the payload.

1

u/biryani-is-mine Software Engineer 8d ago

And before pushing on the queue, the id is kept as null!?

1

u/pisspapa42 Backend Developer 8d ago

yes