'Hibernate ehcache/jcache custom expiry policy hangs after a few hundred select queries

The getResultList call in the findAll_test method is hanging after many hundreds of requests.

public Session openSession() {
    return this.sessionFactory.openSession(); // org.hibernate.SessionFactory
}

public <R> R with(Function<Session, R> function) throws SqlException {
    try {
        Session session = this.openSession();
        R result = function.apply(session);

        if (session.isDirty())
            session.flush();

        if (session.isOpen())
            session.close();

        return result;
    } catch (Exception exception) {
        throw new SqlException(exception); // SqlException extends RuntimeException
    }
}

public final ConcurrentList<T> findAll_test() {
    return this.with(session -> {
        Class<T> tClass = this.getTClass();
        // This is the database model/entity class,
        // it's passed in the constructor,
        // it's the class with @Entity, @Table annotations

        CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
        CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(tClass);
        Root<T> rootEntry = criteriaQuery.from(tClass);
        CriteriaQuery<T> all = criteriaQuery.select(rootEntry);

        // Concurrent.newList is just a thread-safe list,
        // it's not being used downstream for the purposes of this example,
        return Concurrent.newList(
            session.createQuery(all)
                .setCacheable(true)
                .getResultList() // This works ~1245 times, then hangs
        );
    });
}

If I run the above method approximately 1,245 times total, across tens of models/entities, it hangs inside getResultList.

Here are my Hibernate/HikariCP/ehcache properties.

Properties properties = new Properties() {{
    // Connection
    put("hibernate.dialect", config.getDatabaseDriver().getDialectClass());
    put("hibernate.connection.driver_class", config.getDatabaseDriver().getDriverClass());
    put("hibernate.connection.url", config.getDatabaseDriver().getConnectionUrl(config.getDatabaseHost(), config.getDatabasePort(), config.getDatabaseSchema()));
    put("hibernate.connection.username", config.getDatabaseUser());
    put("hibernate.connection.password", config.getDatabasePassword());
    put("hibernate.connection.provider_class", "org.hibernate.hikaricp.internal.HikariCPConnectionProvider");

    // SQL
    put("hibernate.generate_statistics", config.isDatabaseDebugMode());
    put("hibernate.show_sql", false);
    put("hibernate.format_sql", false); // Log Spam
    put("hibernate.use_sql_comments", true);
    put("hibernate.order_inserts", true);
    put("hibernate.order_updates", true);
    put("hibernate.globally_quoted_identifiers", true);

    // Prepared Statements
    put("hikari.cachePrepStmts", true);
    put("hikari.prepStmtCacheSize", 256);
    put("hikari.prepStmtCacheSqlLimit", 2048);
    put("hikari.useServerPrepStmts", true);

    // Caching
    put("hibernate.cache.use_second_level_cache", true);
    put("hibernate.cache.use_query_cache", true);
    put("hibernate.cache.region.factory_class", "org.hibernate.cache.jcache.JCacheRegionFactory");
    put("hibernate.cache.provider_class", "org.ehcache.jsr107.EhcacheCachingProvider");
    put("hibernate.cache.use_structured_entries", config.isDatabaseDebugMode());
}};

Here are two scenarios that "hide" the problem:

  • If I remove hibernate.cache.use_query_cache (or set it to false), my application gets significantly slower, but it no longer encounters this problem.
  • If I change the expiry policy from a duration of one minute, to five minutes then it also prolongs how long I can run the above method by a factor of 5.

The problem lies with the hibernate query cache. When disabled, this problem does not occur. There appears to be a deadlock when querying the way I do (for all entities) as the cache expires.

Dependencies:

  • Hibernate and HikariCP 5.5.7.Final
  • ehcache 3.9.9
  • mariadb-java-client 2.7.4
  • MariaDB server 10.3.28


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source