dimanche 28 juin 2020

How to manually create a new transaction for each spawned thread in Springboot @DataJpaTest?

First of all, please be aware that this question is closely related to this other one. I have a springboot application with a @Transactional(propagation=Propagation.REQUIRES_NEW) CreateUser service. Then, I have @DataJpaTest integration test in which I intend to test the system's behavior under a highly concurrent scenario. However, the test is not passing and the problem seems to be related with the fact that spawned threads (I'm using a FixedThreadPool with 10 threads) cannot see each other's DB modifications. As far as I know, every @DataJpaTest creates its own transaction, and I tried to create a new transaction for each concurrent executed task without any success so far...

Could someone please help with this?

SpringBoot Application:

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

    // ...

    @Bean
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public CreateUser createUserService(@Qualifier("JpaUserRepository") UserRepository userRepository,
                                        @Qualifier("JpaReferralRepository") ReferralRepository referralRepository,
                                        RegisterReferredUser registerReferredUser) {
        return new CreateUser(userRepository, referralRepository, registerReferredUser);
    }
}

Test:

@DataJpaTest(includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Repository.class))
public class JpaTest {

    // ...
        
    @Test
    void createUser_shouldWorksProperlyUnderConcurrentExecution() throws InterruptedException {

        EntityManager entityManager = this.entityManager.getEntityManager();
        ExecutorService executor = Executors.newFixedThreadPool(10);
        EmailAddress referrerUserEmail = EmailAddress.of("john.doe@sample.com");
        User referrerUser = createUser.execute(new CreateUserCommand(referrerUserEmail.getValue(), null));
        String referralCode = referrerUser.getReferralCode().getValue();
        entityManager.getTransaction().commit();
        int maxIterations = 1000;

        for (int i = 0; i < maxIterations; i++) {
            int emailSeed = i;
            executor.submit(() -> {
                // I'd like to start a new transaction for each concurrent executed task here
                createUser.execute(new CreateUserCommand(anEmailAddress(emailSeed), referralCode));
            });
        }

        executor.shutdown();
        if (!executor.awaitTermination(20, TimeUnit.SECONDS)) {
            fail("Executor didn't finish in time");
        }

        assertThat(this.entityManager.getEntityManager().createQuery("from JpaUser").getResultList().size())
                .isEqualTo(maxIterations + 1);
        referrerUser = getUser.execute(referrerUser.getId());

        assertThat(referrerUser.getRewardsCounter()).isEqualTo(RewardsCounter.ZERO);
        assertThat(referrerUser.getCredit()).isEqualTo(Credit.of(1000));
    }

Aucun commentaire:

Enregistrer un commentaire