Queries are counted twice when a DataSource bean delegates to another DataSource bean
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 48/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- java, spring, spring-boot
Research direction
Start with QuickPerfProxyBeanPostProcessor and ProxyDataSourceInterceptor, then reproduce the issue with the DoubleWrapTest setup and QuickPerfSqlConfig. Done means a delegated DataSource records one query once while independent DataSource beans remain recordable, with the regression covered by tests.
Written by the indexing model from the issue text.
Description
Describe the bug
When two DataSource beans exist and one delegates to the other, a single query is counted twice. LazyConnectionDataSourceProxy is a common way to end up in that setup.
QuickPerfProxyBeanPostProcessor wraps any bean of type DataSource and has no guard against one whose queries are already recorded:
if (bean instanceof DataSource && !ScopedProxyUtils.isScopedTarget(beanName)) {
final ProxyFactory factory = new ProxyFactory(bean);
factory.setProxyTargetClass(true);
factory.addAdvice(new ProxyDataSourceInterceptor((DataSource) bean));
return factory.getProxy();
}
The inner DataSource bean is created and wrapped first. The outer bean then receives the already wrapped inner one as its delegate, and gets wrapped as well because it is a DataSource too. The query is recorded once by the outer proxy and once by the inner one.
Expected behavior
One select statement is counted once and @ExpectSelect(1) passes.
Actual behavior
It is counted twice:
java.lang.AssertionError: a performance-related property is not respected
[PERF] You may think that <1> select statement was sent to the database
But there are in fact <2>...
[JDBC QUERY EXECUTION (executeQuery, executeBatch, ...)]
Time:3, Success:True, Type:Statement, Batch:False, QuerySize:1, BatchSize:0, Query:["select count(*) from member"], Params:[]
Time:4, Success:True, Type:Statement, Batch:False, QuerySize:1, BatchSize:0, Query:["select count(*) from member"], Params:[]
To Reproduce
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class App {
@Bean
public DataSource realDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.generateUniqueName(true)
.build();
}
@Bean
@Primary
public DataSource lazyDataSource(DataSource realDataSource) {
return new LazyConnectionDataSourceProxy(realDataSource);
}
}
@SpringBootTest(classes = App.class)
@Import(QuickPerfSqlConfig.class)
@QuickPerfTest
class DoubleWrapTest {
@Autowired
JdbcTemplate jdbcTemplate;
@BeforeEach
void initSchema() {
jdbcTemplate.execute("DROP TABLE IF EXISTS member");
jdbcTemplate.execute("CREATE TABLE member (id INT PRIMARY KEY, name VARCHAR(100))");
}
@Test
@ExpectSelect(1)
void oneSelectShouldBeCountedOnce() {
jdbcTemplate.queryForObject("select count(*) from member", Integer.class);
}
}
The same test passes with one recorded statement on an application that declares a single DataSource bean.
Versions
- QuickPerf: reproduced on both 1.1.0 and master (
3b96d125dacf06b74849cca62df9d60871a86464) - JDK: 17.0.20 (Temurin)
- OS: macOS
- Database: H2
- Spring Boot: 2.7.18 and 3.2.5 (same result on both)
Additional context
The count silently doubles, and the failure message gives no hint about the DataSource setup.
I have this fixed on another datasource-proxy based project: attach a marker interface to the proxy when wrapping, then walk the delegation chain before wrapping and skip a DataSource that already carries the marker. Two things need care. AbstractRoutingDataSource is not a DelegatingDataSource, and a skip condition that is too broad silently drops a second, independent DataSource.
Happy to open a pull request with tests if this direction works for you.
- Dominant language
- Java
- Stars
- 541
- Forks
- 73
- PR merge metrics
- No merged PRs in 30d
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from quick-perf/quickperf
-
:sparkles: feature
Difficulty 5/5 Over a week Newbie friendliness 25/100
quick-perf/quickperf#226 · 1 reaction ·
-
:grey_question: question
Difficulty 4/5 3-5 days Newbie friendliness 25/100
quick-perf/quickperf#221 · 2 comments ·
-
:bug: bug
Difficulty 4/5 3-5 days Newbie friendliness 35/100
quick-perf/quickperf#219 · 1 comment ·
-
Unexpected exception: /tmp/QuickPerf-7265351587637478002/allocation.ser (No such file or directory) Open:bug: bug
Difficulty 4/5 3-5 days Newbie friendliness 35/100
quick-perf/quickperf#218 · 1 comment ·
-
:grey_question: question
Difficulty 4/5 3-5 days Newbie friendliness 35/100
quick-perf/quickperf#214 · 7 comments ·
All issues in quick-perf/quickperf
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
bug needs triage
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 1/5 Under an hour Newbie friendliness 94/100
objectionary/hone-maven-plugin#1061 ·
-
type:bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100