From c62e98cebd461ceac73ac06d32ea6f87568163bb Mon Sep 17 00:00:00 2001 From: mhyeon-lee Date: Sat, 22 Feb 2020 14:54:20 +0900 Subject: [PATCH] DATAJDBC-493 Add failing test deadlocks with update and delete. --- ...RepositoryConcurrencyIntegrationTests.java | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryConcurrencyIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryConcurrencyIntegrationTests.java index 71520eb1d4..ac38a53b6f 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryConcurrencyIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryConcurrencyIntegrationTests.java @@ -20,12 +20,14 @@ import lombok.Getter; import lombok.With; import org.junit.ClassRule; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.dao.IncorrectUpdateSemanticsDataAccessException; import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.jdbc.testing.DatabaseProfileValueSource; @@ -123,6 +125,71 @@ public void updateConcurrencyWithEmptyReferences() throws Exception { assertThat(exceptions).isEmpty(); } + @Test // DATAJDBC-493 + @Ignore("failing test") + public void updateConcurrencyWithDelete() throws Exception { + + DummyEntity entity = createDummyEntity(); + entity = repository.save(entity); + + Long targetId = entity.getId(); + assertThat(targetId).isNotNull(); + + List concurrencyEntities = createEntityStates(entity); + + TransactionTemplate transactionTemplate = new TransactionTemplate(this.transactionManager); + + List exceptions = new CopyOnWriteArrayList<>(); + CountDownLatch startLatch = new CountDownLatch(concurrencyEntities.size() + 1); // latch for all threads to wait on. + CountDownLatch doneLatch = new CountDownLatch(concurrencyEntities.size() + 1); // latch for main thread to wait on until all threads are done. + + // update + concurrencyEntities.stream() // + .map(e -> new Thread(() -> { + + try { + + startLatch.countDown(); + startLatch.await(); + + transactionTemplate.execute(status -> repository.save(e)); + } catch (Exception ex) { + // When the delete execution is complete, the Update execution throws an IncorrectUpdateSemanticsDataAccessException. + if (ex.getCause() instanceof IncorrectUpdateSemanticsDataAccessException) { + return; + } + + exceptions.add(ex); + } finally { + doneLatch.countDown(); + } + })) // + .forEach(Thread::start); + + // delete + new Thread(() -> { + try { + + startLatch.countDown(); + startLatch.await(); + + transactionTemplate.execute(status -> { + repository.deleteById(targetId); + return null; + }); + } catch (Exception ex) { + exceptions.add(ex); + } finally { + doneLatch.countDown(); + } + }).start(); + + doneLatch.await(); + + assertThat(exceptions).isEmpty(); + assertThat(repository.findById(entity.id)).isEmpty(); + } + private List createEntityStates(DummyEntity entity) { List concurrencyEntities = new ArrayList<>();