Search code examples
springjpatomcatcachingeclipselink

Transactions executed in Spring using external Tomcat twice faster than Spring embeded Tomcat


A simple Spring application that uses standard Hikari connector and Eclipselink as JPA provider takes double time (aprox) if executed using embeded Tomcat. I also tried hibernate with similar result.

@Entity
@Table(name = "chats")
public class Chat{
    
    @Id    
    private String  id;

    public Chat() {    }

    public String getId() {
        return id;
    }
}


//Repo
public interface ChatRepository extends JpaRepository<Chat, String> {
}

Configuration:


@Configuration
public class EclipseLinkJpaConfigurationHikari {

    
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource);
        em.setPackagesToScan("project");        
        em.setJpaVendorAdapter(new EclipseLinkJpaVendorAdapter());
        hace que selecciones uno de persitance.xml
        Map<String, Object> properties = new HashMap<>();
        properties.put("eclipselink.weaving", "false");
        properties.put("eclipselink.logging.level", "INFO");
        properties.put("eclipselink.ddl-generation", "none");
        em.setJpaPropertyMap(properties);

        return em;
    }


    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(emf);
        return transactionManager;
    }

}

Controller

@RestController
@RequestMapping("/")
public class TestController {

    private final ChatRepository chatRepository;

    @PersistenceContext
    EntityManager em;

    public TestController(ChatRepository chatRepository) {
        this.chatRepository = chatRepository;
    }

    @Transactional
    @GetMapping("/test")
    public String test() {
        long t = System.currentTimeMillis();

        for( int x=1 ; x <1500;x++){
            List<Chat> chats = chatRepository.findAll();
            Chat c = chats.get(0);
        }

        long time = (System.currentTimeMillis() - t);
        return t + "ms";
    }

}

application.properties

spring.application.name=TestConnection


spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.connection.provider_class=com.zaxxer.hikari.hibernate.HikariConnectionProvider
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update

spring.datasource.url=jdbc:mysql://localhost/db?useSSL=false
spring.datasource.username=xxx
spring.datasource.password=yyy
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.hikari.connection-timeout=50000
spring.datasource.hikari.idle-timeout=300000
spring.datasource.hikari.max-lifetime=900000
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.pool-name=ConnPool
spring.datasource.hikari.connection-test-query=select 1 from dual

spring.datasource.hikari.data-source-properties.cachePrepStmts=true
spring.datasource.hikari.data-source-properties.prepStmtCacheSize=250
spring.datasource.hikari.data-source-properties.prepStmtCacheSqlLimit=2048
spring.datasource.hikari.data-source-properties.useServerPrepStmts=true
spring.datasource.hikari.data-source-properties.useLocalSessionState=true
spring.datasource.hikari.data-source-properties.rewriteBatchedStatements=true
spring.datasource.hikari.data-source-properties.cacheResultSetMetadata=true
spring.datasource.hikari.data-source-properties.cacheServerConfiguration=true
spring.datasource.hikari.data-source-properties.elideSetAutoCommits=true
spring.datasource.hikari.data-source-properties.maintainTimeStats=false

logging.level.com.zaxxer.hikari.HikariConfig=DEBUG
logging.level.com.zaxxer.hikari=TRACE

spring.datasource.type=com.zaxxer.hikari.HikariDataSource

If I execute this request, the processing times are:

External tomcat -> 494ms, 232msms, 217ms, 193ms, 198ms, 214ms, 155ms, 155ms, 107ms (stabilizes around 100ms any further request )

Embedded tomcat -> 363ms, 200ms, 189ms , 207ms, 201ms, 194ms, 193ms, 185ms, 192ms (stabilizes around 195ms any further request )

What is going on? I am using the same configuration, same tomcat version, same libraries from gradle. After first request cache starts working but it seems to work better for external tomcat. In general, the performance for any request that access the dabase is twice faster when using external tomcat.

Note: if I remove @Transactional from controller's method, processing time is even larger, from 200ms to 280ms (embedded) and from 100ms to 150ms (external tomcat).


Solution

  • After several days of investigation I found out the reason why the application had different performance while using external tomcat or the configuration provided by Intellij Idea.

    enter image description here

    After enabling the option Disable launch optimization (-XX:TieredStopAtLevel=1), the performance is as good as executed by external Tomcat.