Кэш закрыт, что вызывает исключение при запуске набора тестов

У меня возникла проблема, аналогичная описанной в этом вопросе.

У меня есть набор тестов, который отлично работает в среде разработки. Один из тестов не выполняется при выполнении в Bitbucket Pipelines за следующим исключением:

org.springframework.dao.InvalidDataAccessApiUsageException: Cache[model.Role] is closed; nested exception is java.lang.IllegalStateException: Cache[model.Role] is closed
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:364)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:225)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527)
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
   ....

Я хочу попробовать принятое решение, но не знаю, как применить его в моем проекте. Второе решение зависит от файла ehcache.xml. У меня нет этого файла, все настраивается в JavaConfig. Как я могу принять предлагаемые решения для EhCache + JCache (JSR-107) в JavaConfig?

Конфигурация моего кеша:

@Configuration
@EnableCaching
public class CacheConfig {

    private final javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration =
            Eh107Configuration.fromEhcacheCacheConfiguration(CacheConfigurationBuilder
                    .newCacheConfigurationBuilder(Object.class, Object.class,
                            ResourcePoolsBuilder.newResourcePoolsBuilder()
                                    .heap(100, EntryUnit.ENTRIES))
                    .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(60)))
                    .build());

    @Bean
    public JCacheManagerCustomizer cacheManagerCustomizer() {
        return cm -> {
            createIfNotExists(cm, "model.Role");
            createIfNotExists(cm, "model.User.roles");
            // ...
        };
    }

    private void createIfNotExists(CacheManager cacheManager, String cacheName) {
        if (cacheManager.getCache(cacheName) == null) {
            cacheManager.createCache(cacheName, jcacheConfiguration);
        }
    }
}

Зависимости Gradle:

implementation group: 'org.springframework.boot', name: 'spring-boot-starter-cache'
implementation group: 'javax.cache', name: 'cache-api'
implementation group: 'org.ehcache', name: 'ehcache'
implementation group: 'org.hibernate', name: 'hibernate-jcache'

Неудачный тест:

@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class SecondLevelCacheTest {
    @Autowired
    private RoleRepository roleRepository;
    private CacheManager manager;

    @Before
    public void initCacheManager() {
        CachingProvider provider = Caching.getCachingProvider();
        manager = provider.getCacheManager();

        final String cacheRegion = "model.Role";
        manager.getCache(cacheRegion).clear();
    }

    @Test
    public final void givenEntityIsLoaded_thenItIsCached() {
        final String cacheRegion = "model.Role";

        boolean hasNext = manager.getCache(cacheRegion).iterator().hasNext();
        final Role role = roleRepository.findByName("USER");
        boolean hasNext2 = manager.getCache(cacheRegion).iterator().hasNext();
        final Role role2 = roleRepository.findByName("USER");

        Assert.assertFalse(hasNext);
        Assert.assertTrue(hasNext2);
    }
}

Наиболее популярное решение - «установить для общего свойства значение false в контексте тестирования». Как я могу это сделать с учетом моей конфигурации?


person naXa    schedule 10.11.2018    source источник


Ответы (3)


Предлагаемое решение, о котором вы говорите, основано на Ehcache 2. Вы используете Ehcache 3 (подходит для вас), поэтому оно недействительно.

Spring позаботится о закрытии CacheManager, поэтому обычно вам не нужно ни о чем заботиться. Кроме того, вам не нужно получать к нему доступ через CachingProvider. Вы можете @Autowired javax.cache.CacheManager и таким образом вы обязательно получите правильный.

Однако вы используете Hibernate. Вы должны убедиться, что Spring и Hibernate используют один и тот же CacheManager. Способ его настройки зависит от версии Spring и Hibernate.

Можно ли получить полную трассировку стека? Прямо сейчас кажется, что что-то закрывает CacheManager, но не отменяет его регистрацию в CachingProvider. Это невозможно, если вы не закрываете org.ehcache.CacheManager, не закрывая javax.cache.CacheManager, обертывающий его. Более позднее закрытие приведет к отмене регистрации.

person Henri    schedule 25.11.2018

Один из вариантов - предоставить пользовательский CachingProvider, который не разделяет CacheManager-s. Пример решения можно найти здесь.

person alexmagnus    schedule 03.02.2020

Добавьте аннотацию к классу неудачного теста с помощью @AutoConfigureCache. По умолчанию эта аннотация устанавливает NoOpCacheManager, то есть базовую реализацию без операций CacheManager, подходящую для отключения кэширования, обычно используемую для резервных объявлений кеша без фактического резервного хранилища. Он просто принимает любые элементы в кеш, фактически не сохраняя их.

Spring Documentation на @AutoConfigureCache

Документация Spring по @NoOpCacheManager

person Dana Georgescu    schedule 15.01.2021