Spring declarative caching test only works if testing direct call to #Cacheable method - spring

I'm trying to test a #Cacheable method using the following code, but is not working, the method is not being cached, if instead of putting the #Cacheable in the getCountCache method I put it in the getCount method the test works, tried a lot of things but couldn't find the cause.
#ContextConfiguration
#ExtendWith(SpringExtension.class)
public class CacheTest {
static class MyRepoImpl {
private Count count;
public MyRepoImpl(Count count){
this.count = count;
}
public int getCount(String key){
return getCountCache(key);
}
#Cacheable(cacheNames = "sample")
public int getCountCache(String key) {
return count.getCount();
}
}
static class Count {
int i = 0;
public int getCount() {
return i++;
}
}
#EnableCaching
#Configuration
public static class Config {
#Bean CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
#Bean MyRepoImpl myRepo() {
count = Mockito.mock(Count.class);
return new MyRepoImpl(count);
}
}
static Count count;
#Autowired MyRepoImpl repo;
#Test
public void methodInvocationShouldBeCached() {
Mockito.when(count.getCount()).thenReturn(1, 2, 3, 4);
Object result = repo.getCount("foo");
assertThat(result).isEqualTo(1);
Mockito.verify(count, Mockito.times(1)).getCount();
result = repo.getCount("foo");
assertThat(result).isEqualTo(1);
Mockito.verify(count, Mockito.times(1)).getCount();
}
}

Since Spring cache works on proxy to the bean method annotated with #Cachable called from inside class won't cache. You need to call it directly from outside of the class for caching mechanism to work.
Spring cache #Cacheable method ignored when called from within the same class this explanation is really good.

Related

Transactional annotation slows down the performance than HibernateDAOSupport

I am exporting a report in my code, I am using HibernateDAOSupport and the method is not annotated with #Transactional. So when the request comes from the UI then automatically Transaction is created and the report is exported in 2 mins.
But when I try to use thread, so I had to put annotation #Transactional, otherwise I get an error of LazyInitialization as no Transcaction is present.
So when I put #Transactional then the same report takes time 2.4 mins. This time keeps increasing and sometimes it take double of the time without #Transactional
I am not sure why it takes time when I put annotation #Transactional
The main class:
CommonDAO
public class CommonDAO extends HibernateDaoSupport
private HibernateTransactionManager txnManager;
public void setTxnManager(HibernateTransactionManager txnManager) {
this.txnManager = txnManager;
}
public List executeSQLQueryPaging(final String hql,
final Object[] params, final Integer[] pagingParam) throws ServiceException {
List results = null;
results = getHibernateTemplate().executeFind(new HibernateCallback() {
public Object doInHibernate(Session session) {
SQLQuery query = session.createSQLQuery(hql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
query.setParameter(i, params[i]);
}
}
query.setFirstResult(pagingParam[0]);
query.setMaxResults(pagingParam[1]);
return query.list();
}
});
return results;
}
ModuleDAO extends CommonDAO
public List getReportData{
executeSQLQueryPaging();
...
return list}
Service
public List getReportData(){
.....
return ModuleDAO.getReportData();
}
If I put #Transactional at service layer then the performance detriorates, or else it is faster if executed from web.

How to include Method Parameters in #Timed annotation used on arbitrary method name

In my application, I have a use case where I have to monitor a method by the argument value it is supplied. I have to expose the metrics to Prometheus endpoint. However, the function is a common function and is used by many different classes. I am trying to get the value passed in the method parameter to #Timed, so as to distinguish between different behaviors this function would exhibit based on the parameter value passed.
I tried using #Timed annotation but could not get the #Timed annotation expose the function parameter as a metric to Prometheus.
#Timed("getFooContent")
public void getFooContent(Arg1 arg1, Arg2 arg2) {
//some code....
}
I was able to figure this out by creating an annotation #Foo and then adding this annotation to the parameter of my function:
#Timed("getFooContent")
public void getFooContent(#Foo Arg1 arg1, Arg2 arg2) {
//some code....
}
Following is my Timed Configuration class:
#Configuration
#SuppressWarnings("unchecked")
public class TimedConfiguration {
public static final String NOT_AVAILABLE = "N/A";
Function<ProceedingJoinPoint, Iterable<Tag>> tagsBasedOnJoinPoint;
#Bean
public TimedAspect timedAspect(MeterRegistry registry) {
tagsBasedOnJoinPoint = pjp ->
Tags.of("class", pjp.getStaticPart().getSignature().getDeclaringTypeName(),
"method", pjp.getStaticPart().getSignature().getName(),
"parameter_1", getArguments(pjp));
return new TimedAspect(registry, tagsBasedOnJoinPoint);
}
private String getArguments(ProceedingJoinPoint pjp) {
Object[] args = pjp.getArgs();
String className = pjp.getStaticPart().getSignature().getDeclaringTypeName();
if(className.contains("com.example.foo")) { //Resstricting to only certain packages starting with com.example.foo
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Method method = methodSignature.getMethod();
Annotation[][] annotations = method.getParameterAnnotations();
int index = -1;
for(int i = 0; i < annotations.length; i++) {
Annotation[] annotationsArr = annotations[i];
for(Annotation annotation: annotationsArr) {
if(annotation.annotationType().getName().equals(Foo.class.getName())) {
index = i;
break;
}
}
}
if(index >= 0) {
List parameterValues = new ArrayList((List)args[index]);
if(CollectionUtils.isNotEmpty(parameterValues) && parameterValues.get(0) instanceof Byte) {
Collections.sort(parameterValues); //Sorting the paratemer values as per my use case
return String.valueOf(parameterValues.stream().collect(Collectors.toSet()));
}
}
}
return NOT_AVAILABLE;
}
I solved it with this TimedAspect configuration that I found in a PoC in a micrometer github issue:
https://github.com/jonatan-ivanov/micrometer-tags/blob/master/src/main/java/com/example/micrometertags/MetricsConfig.java
#Configuration
public class MetricsConfig {
#Bean
public TimedAspect timedAspect(MeterRegistry meterRegistry) {
return new TimedAspect(meterRegistry, this::tagFactory);
}
private Iterable<Tag> tagFactory(ProceedingJoinPoint pjp) {
return Tags.of(
"class", pjp.getStaticPart().getSignature().getDeclaringTypeName(),
"method", pjp.getStaticPart().getSignature().getName()
)
.and(getParameterTags(pjp))
.and(ExtraTagsPropagation.getTagsAndReset());
}
private Iterable<Tag> getParameterTags(ProceedingJoinPoint pjp) {
Set<Tag> tags = new HashSet<>();
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
for (Annotation annotation : parameters[i].getAnnotations()) {
if (annotation instanceof ExtraTag) {
ExtraTag extraTag = (ExtraTag) annotation;
tags.add(Tag.of(extraTag.value(), String.valueOf(pjp.getArgs()[i])));
}
}
}
return tags;
}
}
There isn't a way to include the parameters in the timer's tags using just the annotation. Micrometer provides the annotation for simple use cases, and recommends using the programmatic approach when you need something more complex.
You should use the record method on the timer and wrap your code in that.
registry.timer("myclass.getFooContent", Tags.of("arg1", arg1)).record(() -> {
//some code...
})
Add this below bean in your Configuration class and then try.
#Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
Annotate the configuration class with #EnableAspectJAutoProxy
Please read thru this link http://micrometer.io/docs/concepts#_the_timed_annotation

Spring boot cache not working in #PostConstruct

I'm building a "class cache", with classes I want to call later.
The main goal is that I don't want scan the context every time that a class instance is needed.
# Model / Repository classes
#Getter
#RequiredArgsConstructor
public class Block implements Serializable {
private final String className;
private final Set<String> classCandidates = new HashSet<>();
public boolean addCandidate(final String classCandidate) {
return this.classCandidates.add(classCandidate);
}
}
#Slf4j
#Component
#CacheConfig(cacheNames = ConstantsCache.CACHE_BLOCK)
public class BlockRepository {
#Cacheable(key = "#className")
public Block findByInputClass(final String className) {
log.info("---> Loading classes for class '{}'", className);
val block = new Block(className);
findCandidates(block);
return block;
}
}
First to evaluate the cache, I've put the cache method #Autowired in a #RestController, wich works fine. The cache is populated when I call the rest method.
#RestController
public class Controller {
#Autowired
BlockRepository blockRepository;
#RequestMapping("/findByInputClass")
public Block findByInputClass(#RequestParam("className") final String className) {
return blockRepository.findByInputClass(className);
}
}
After doing that, I've moved the #Autowired object to a #Service, creating a method to self-populate the cache. But this does not work. The cache is not populated when the #PostConstructor method is called.
#Slf4j
#Component
public class BlockCacheService {
#Autowired
BlockRepository blockRepository;
#PostConstruct
private void postConstruct() {
log.info("*** {} PostConstruct called.", this.getClass().getTypeName());
val block = blockRepository.findByInputClass(ConstantsGenerics.BLOCK_PARENT_CLASS);
final Set<String> inputClasses = getInputFromCandidates(block.getClassCandidates());
appendClassesToCache(inputClasses);
}
private void appendClassesToCache(final Set<String> inputClasses) {
for (val inputClass : inputClasses) {
blockRepository.findByInputClass(inputClass);
}
}
}
How can I properly populate the cache using a service or component, that must start with the application.
Thanks in advance.
EDIT:
I've found a possible solution here: https://stackoverflow.com/a/28311225/1703546
Than I've changed the #Service code to put the cache manually instead of use the #Cacheable magic abstraction.
The class now is like this.
#Slf4j
#Component
public class BlockCacheService {
#Autowired
CacheManager cacheManager;
#Autowired
BlockRepository blockRepository;
#PostConstruct
private void postConstruct() {
log.info("*** {} PostConstruct called.", this.getClass().getTypeName());
val block = blockRepository.findByInputClass(ConstantsGenerics.BLOCK_PARENT_CLASS);
final Set<String> inputClasses = getInputFromCandidates(block.getClassCandidates());
appendClassesToCache(inputClasses);
}
private void appendClassesToCache(final Set<String> inputClasses) {
for (val inputClass : inputClasses) {
val block = blockRepository.findByInputClass(inputClass);
cacheManager.getCache(ConstantsCache.CACHE_BLOCK).put(block.getClassName(), block);
}
}
}
Now the cache is populated correctly, but the question is, this is the best solution?
Thanks.
You can't use an aspect in #PostConstruct as it may not have been created yet (and that is documented by the way).
One possible way to make that work is to implement SmartInitializingBean instead as it gives a callback when all singletons have been fully initialized (including their aspect. Changing that on your original service should work.
Having said that, this code of yours has an impact on the startup time. Why don't you let your cache to be filled lazily instead?

Spring Redis Cache not evicting

The following works (results in the evict being performed):
fooController {
#ApiEndpoint
public delete(id) {
fooService.deleteFoo(id)
}
}
fooService {
#CacheEvict(value = "cache1", key = "#id")
public void deleteFoo(Long id) {
//delete logic here
}
}
But this does not work (nothing is evicted from the cache):
fooController {
#ApiEndpoint
public delete(name) {
fooService.deleteFoo2(name)
}
}
fooService {
public void deleteFoo2(String name) {
//delete logic here
deleteFoo(name.getId())
}
#CacheEvict(value = "cache1", key = "#id")
public void deleteFoo(Long id) {
//delete logic here
}
}
Why are my #CacheEvict annotations only called when the method is called straight from the controller?
I'm using Redis as the caching mechanism.
Aop is not worinking when your method is called inside the class.
It is working when method is called by another class.
So you can define deleteFoo in another service.
To make spring aspect intercept #Cache* annotations you have to make external call. If you don't like to call this method from another object, use bean self-invocation approach. In this case your class is presented as two objects and one calls the other:
#Resource private FooController thisBean;
public delete(id) {
thisBean.deleteFoo(id)
}
#CacheEvict(value = "cache1", key = "#id")
public void deleteFoo(Long id) {}

What's the best way to access your AOP proxy in spring?

Due to Spring's implementation of AOP there are times when you want to call a method within the same class where you'd like the call to pass through your advice.
Here's a quick example
#Service
public class SomeDaoClass {
public int getSomeValue() {
// do some cache look up
Integer cached = ...;
if ( cached == null ) {
cached = txnGetSomeValue();
// store in cache
}
return cached;
}
#Transactional
public int txnGetSomeValue() {
// look up in database
}
}
Now ignore for a second that in spring I could use #Cached annotations for caching, the point of my example is that getSomeValue() is accessed by other beans passing through the spring proxy and running the associated advice. The internal call to txnGetSomeValue will not and in my example will miss having the advice we apply at the #Transactional point cut.
What's the best way to get access to your proxy so you can apply the advice?
My best approach works, but is clumsy. It heavily exposes the implementation. I figured this out years ago and have just stuck with this awkward code but would like to know what is the preferred method. My hacky approach looks like this.
public interface SomeDaoClass {
public int getSomeValue();
// sometimes I'll put these methods in another interface to hide them
// but keeping them in the primary interface for simplicity.
public int txnGetSomeValue();
}
#Service
public class SomeDaoClassImpl implements SomeDaoClass, BeanNameAware, ApplicationContextAware {
public int getSomeValue() {
// do some cache look up
Integer cached = ...;
if ( cached == null ) {
cached = proxy.txnGetSomeValue();
// store in cache
}
return cached;
}
#Transactional
public int txnGetSomeValue() {
// look up in database
}
SomeDaoClass proxy = this;
String beanName = null;
ApplicationContext ctx = null;
#Override
public void setBeanName(String name) {
beanName = name;
setProxy();
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext;
setProxy();
}
private void setProxy() {
if ( beanName == null || ctx == null )
return;
proxy = (BlockDao) ctx.getBean(beanName);
beanName = null;
ctx = null;
}
}
What I've done is add BeanNameAware and SpringContextAware so that I can look up my proxy object in Spring. Ugly I know. Any advice on a cleaner way to do this would be nice.
You could inject the proxy using #Resource
#Service
public class SomeDaoClassImpl implements SomeDaoClass {
#Resource
private SomeDaoClassImpl someDaoClassImpl;
public int getSomeValue() {
// do some cache look up
Integer cached = ...;
if ( cached == null ) {
cached = somDaoClassImpl.txnGetSomeValue();
// store in cache
}
return cached;
}
#Transactional
public int txnGetSomeValue() {
// look up in database
}
Note: #Autowired will not work.

Resources