Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Caching lookups on application startup doesn't work

I am using Spring Boot 1.5.9 on Tomcat 9.0.2 and I am trying to cache lookups using spring @Cacheable scheduling a cache refresh job that runs on application startup and repeats every 24 hours as follows:

@Component
public class RefreshCacheJob {

    private static final Logger logger = LoggerFactory.getLogger(RefreshCacheJob.class);

    @Autowired
    private CacheService cacheService;

    @Scheduled(fixedRate = 3600000 * 24, initialDelay = 0)
    public void refreshCache() {
        try {
            cacheService.refreshAllCaches();
        } catch (Exception e) {
            logger.error("Exception in RefreshCacheJob", e);
        }
    }

}

and the cache service is as follows:

@Service
public class CacheService {

    private static final Logger logger = LoggerFactory.getLogger(CacheService.class);

    @Autowired
    private CouponTypeRepository couponTypeRepository;

    @CacheEvict(cacheNames = Constants.CACHE_NAME_COUPONS_TYPES, allEntries = true)
    public void clearCouponsTypesCache() {}

    public void refreshAllCaches() {
        clearCouponsTypesCache();
        List<CouponType> couponTypeList = couponTypeRepository.getCoupons();
        logger.info("######### couponTypeList: " + couponTypeList.size());
    }
}

the repository code:

public interface CouponTypeRepository extends JpaRepository<CouponType, BigInteger> {
    @Query("from CouponType where active=true and expiryDate > CURRENT_DATE order by priority")
    @Cacheable(cacheNames = Constants.CACHE_NAME_COUPONS_TYPES)
    List<CouponType> getCoupons();
}

later in my webservice, when trying to get the lookup as follows:

@GET
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
@Path("/getCoupons")
@ApiOperation(value = "")
public ServiceResponse getCoupons(@HeaderParam("token") String token, @HeaderParam("lang") String lang) throws Exception {
    try {
        List<CouponType> couponsList = couponRepository.getCoupons();
        logger.info("###### couponsList: " + couponsList.size());
        return new ServiceResponse(ErrorCodeEnum.SUCCESS_CODE, resultList, errorCodeRepository, lang);
    } catch (Exception e) {
        logger.error("Exception in getCoupons webservice: ", e);
        return new ServiceResponse(ErrorCodeEnum.SYSTEM_ERROR_CODE, errorCodeRepository, lang);
    }
}

The first call it gets the lookup from the database and the subsequent calls it gets it from the cache, while it should get it from the cache in the first call in the web service?

Why am I having this behavior, and how can I fix it?

like image 619
Mahmoud Saleh Avatar asked Feb 02 '18 10:02

Mahmoud Saleh


People also ask

How do I enable caching in spring boot?

To enable the Spring Boot caching feature, you need to add the @EnableCaching annotation to any of your classes annotated with @Configuration or to the boot application class annotated with @SpringBootApplication .

How do spring boots handle caching?

Spring Cache uses the parameters of the method as key and the return value as a value in the cache. When the method is called the first time, Spring will check if the value with the given key is in the cache. It will not be the case, and the method itself will be executed.

How to cache lookup table data?

While caching using reactive loading would be better than no caching at all, the best approach would be to proactively load the lookup table data into the cache at application startup. In this tutorial we will look at how to cache lookup table data and other static information.

Why can’t I find the startup apps in the task manager?

Let’s begin! If you cannot find the startup apps in the Task Manager, then the first thing that you need to do is restart the Explorer application. Oftentimes, Windows programs get infected with temporary bugs and corruption errors, which prevents them from working properly.

How do I cache the current user of an app?

The quick answer is to cache the current user in a global variable when the app starts. This can be done by setting the home screen’s OnStart property to this: Set (CurrentUser, User ()) Then anywhere in the app, just use CurrentUser just as you would User ()

What is the use of caching in ASP NET?

Caching Data at Application Startup (C#) In any Web application some data will be frequently used and some data will be infrequently used. We can improve the performance of our ASP.NET application by loading in advance the frequently-used data, a technique known as Caching.


2 Answers

The issue was fixed after upgrading to Tomcat 9.0.4

like image 52
Mahmoud Saleh Avatar answered Oct 25 '22 15:10

Mahmoud Saleh


While it's not affecting the scheduled task per se, when refreshAllCaches() is invoked in the CacheService, @CacheEvict on clearCouponsTypesCache() is bypassed since it's invoked from the same class (see this answer). It will lead to cache not being purged before

List<CouponType> couponTypeList = couponTypeRepository.getCoupons();

is invoked. This means that the @Cacheable getCoupons() method will not query the database, but will instead return values from the cache.

This makes the scheduled cache refresh action to do its work properly only once, when the cache is empty. After that it's useless.

The @CacheEvict annotation should be moved to refreshAllCaches() method and add beforeInvocation=true parameter to it, so the cache is purged before being populated, not after.


Also, when using Spring 4 / Spring Boot 1.X, these bugs should be taken into consideration:

  • https://github.com/spring-projects/spring-boot/issues/8331
  • https://jira.spring.io/browse/SPR-15271

While this bug doesn't seem to affect this specific program, it might be a good idea to separate @Cacheable annotation from JpaRepository interface until migration to Spring 5 / Spring Boot 2.X.

like image 41
jihor Avatar answered Oct 25 '22 15:10

jihor