Description
Gary Russell opened SPR-14929 and commented
Consider the following:
public class SpELTests {
@Test
public void test() {
Expression exp = new SpelExpressionParser().parseExpression(
"#target.filter(headers['dummyHeader'] != null "
+ "? headers['dummyHeader'] "
+ ": T(org.springframework.util.Assert).isTrue(false, 'required header not available: dummyHeader'))");
Message<String> message = MessageBuilder.withPayload("foo").setHeader("dummyHeader", "bar").build();
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("target", new DummyFilter());
StopWatch watch = new StopWatch();
watch.start();
for (int i = 0; i < 5000; i++) {
Object value = exp.getValue(context, message);
}
watch.stop();
System.out.println("Elapsed: " + watch.getTotalTimeSeconds());
}
public static class DummyFilter {
@Filter
public boolean filter(@Header("dummyHeader") String dummyValue) {
return true;
}
}
}
When run with Spring Framework 4.3.3 it runs in < 0.5 seconds; with 4.3.4 it takes 40+ seconds.
With YourKit, I tracked the problem down to DefaultConversionService.getConverter()
with source and target type descriptors:
java.lang.String
@org.springframework.messaging.handler.annotation.Header java.lang.String
The performance issue is because this.converterCache.get(key);
seems to miss on every iteration (which in turn causes AnnotationUtils.getDefaultValue()
which is where the cost is incurred). getDefaultConverter()
correctly returns a NoOp converter which is inserted into the cache but, for some reason, that entry is not found in later iterations.
After several iterations, this.converterCache
contains
{ConverterCacheKey [sourceType = java.lang.String, targetType = @org.springframework.messaging.handler.annotation.Header java.lang.String]=NO_OP,
ConverterCacheKey [sourceType = java.lang.String, targetType = @org.springframework.messaging.handler.annotation.Header java.lang.String]=NO_OP,
ConverterCacheKey [sourceType = java.lang.String, targetType = java.lang.String]=NO_OP,
ConverterCacheKey [sourceType = java.lang.String, targetType = @org.springframework.messaging.handler.annotation.Header java.lang.String]=NO_OP,
ConverterCacheKey [sourceType = java.lang.String, targetType = @org.springframework.messaging.handler.annotation.Header java.lang.String]=NO_OP,
ConverterCacheKey [sourceType = java.lang.Boolean, targetType = java.lang.Boolean]=NO_OP}
With 4.3.3, I this.converterCache
contains:
{ConverterCacheKey [sourceType = java.lang.String, targetType = @org.springframework.messaging.handler.annotation.Header java.lang.String]=NO_OP,
ConverterCacheKey [sourceType = java.lang.String, targetType = java.lang.String]=NO_OP,
ConverterCacheKey [sourceType = java.lang.Boolean, targetType = java.lang.Boolean]=NO_OP}
So the issue seems to be the cache entries are not found, perhaps some problem with the hash.
Affects: 4.3.4
Issue Links:
- INT-4170 The delivery of message with be much slower to downstream filter with Header parameter
- AnnotationFormatterFactory should support @AliasFor [SPR-14844] #19410 AnnotationFormatterFactory should support
@AliasFor
- Differentiate between TypeDescriptors with same annotations but different attributes [SPR-13714] #18287 Differentiate between TypeDescriptors with same annotations but different attributes
- GenericConversionService.addConverter should be able to determine generic types from target class behind proxy [SPR-14822] #19388 GenericConversionService.addConverter should be able to determine generic types from target class behind proxy
- Improve performance for conversions using a method parameter based type descriptor with annotations [SPR-14926] #19493 Improve performance for conversions using a method parameter based type descriptor with annotations
- Annotated method argument matching performance issue [SPR-15060] #19626 Annotated method argument matching performance issue
0 votes, 7 watchers