Skip to content

SynchronizedItemStreamReader update call is not threadsafe #5201

@leonschenk

Description

@leonschenk

Bug description
JpaCursorItemReader clears the entityManager upon update. This breaks threadsafety of the update call in case of the use of SynchronizedItemStreamReader, because update method is not synchronized.

Environment
Any version of SpringBatch
JpaCursorItemReader based upon jpa implementation Hibernate 6.6
Parallel execution of a JpaCursorItemReader

Steps to reproduce
Configure a job with a step containing:

  1. synchronized JpaCursorItemReader with a jpql query, based upon a hibernate entityManager
  2. asynchronous taskExecutor with at least 2 threads
  3. a database that contains abundant items to select with the query (more items than the chunk size)
  4. Debug the testcase, set breakpoints upon read/update of the SynchronizedItemStreamReader
  5. Run the first thread until the update step, it will have read and processed chunk size of items by that time. The lock will be released.
  6. Advance the second thread until hibernate ScrollableResultImpl:131 ( currentRow = getRowReader().readRow( getRowProcessingState() );)
  7. Second thread now has registered the jdbcValuesSourceProcessingState in the persistenceContext.
  8. Advance the first thread until after update, such that the entityManager is cleared.
  9. Advance the second thread now. It will throw NoSuchElementException when trying to deregister the jdbcValuesSourceProcessingState.

Caused by: java.util.NoSuchElementException at org.hibernate.internal.util.collections.StandardStack.pop(StandardStack.java:60) ~[hibernate-core-6.6.33.Final.jar:6.6.33.Final] at org.hibernate.sql.results.spi.LoadContexts.deregister(LoadContexts.java:43) ~[hibernate-core-6.6.33.Final.jar:6.6.33.Final] at org.hibernate.internal.ScrollableResultsImpl.prepareCurrentRow(ScrollableResultsImpl.java:144) ~[hibernate-core-6.6.33.Final.jar:6.6.33.Final] at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:52) ~[hibernate-core-6.6.33.Final.jar:6.6.33.Final] at org.hibernate.query.internal.ScrollableResultsIterator.hasNext(ScrollableResultsIterator.java:33) ~[hibernate-core-6.6.33.Final.jar:6.6.33.Final] at java.base/java.util.Spliterators$IteratorSpliterator.tryAdvance(Spliterators.java:1949) ~[?:?] at java.base/java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681) ~[?:?] at org.springframework.batch.item.database.JpaCursorItemReader.doRead(JpaCursorItemReader.java:158) ~[spring-batch-infrastructure-5.2.4.jar:5.2.4] at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.read(AbstractItemCountingItemStreamItemReader.java:93) ~[spring-batch-infrastructure-5.2.4.jar:5.2.4]

Expected behavior
No error being throwed

Minimal Complete Reproducible example
Bug is not reproducible using simple tests, because it depends on timing/scheduling of multiple threads.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions