-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Describe the bug
When using MetricsDSLContext with jOOQ 3.20.x, calling DSLContext#fetchValue(SelectField) leads to double instrumentation: time() is invoked twice along the select(...) path. This causes tag state to be reset/overwritten and results in missing or unexpected tags on the recorded jooq.query timer.
This occurs even if Micrometer is compiled against jOOQ 3.14.x, because at runtime jOOQ 3.20.x introduces a new overload that changes the call path.
Environment
- Micrometer version: 1.15.3
- jOOQ version: 3.20.6
- Java version: 21
To Reproduce
Minimal JUnit test that demonstrates the issue:
How to reproduce the bug:
@Test
void jooqMethodTest() throws SQLException {
try (Connection conn = DriverManager.getConnection("jdbc:h2:mem:test")) {
Configuration config = new DefaultConfiguration().set(conn).set(SQLDialect.H2);
MeterRegistry registry = new SimpleMeterRegistry();
MetricsDSLContext jooq =
MetricsDSLContext.withMetrics(DSL.using(config), registry, Tags.empty());
Integer result = jooq.tag("name", "checkAuthorExists")
.fetchValue(DSL.inline(123));
assertThat(registry.get("jooq.query")
.tag("name", "checkAuthorExists")
.tag("type", "read")
.timer()
.count()).isEqualTo(1);
}
}
In practice, the assertion fails because the timer is not recorded with the expected tags (or the meter cannot be found under that tag set).
Expected behavior
A single jooq.query timer is recorded once with the expected tags, e.g. name=checkAuthorExists, type=read.
Additional context
Call path (showing the double time()):
DefaultDSLContext.fetchValue(DSL.inline(123)) -> fetchValue(SelectField<T> field)
-> DefaultDSLContext.fetchValue(select(field))
-> MetricsDSLContext.select(SelectField<T1> field1) // time() #1
-> DefaultDSLContext.select(SelectField<T1> field1)
-> MetricsDSLContext.select(SelectFieldOrAsterisk... fields) // time()
Root cause (fetchValue() in jOOQ 3.20.x):
@Override
public <T> T fetchValue(SelectField<T> field) {
return field instanceof TableField
? fetchValue((TableField<?, T>) field)
: field instanceof Table<?>
? fetchValue(select(field).from((Table<?>) field))
: fetchValue(select(field));
}
Because MetricsDSLContext instruments select(...), the new delegation path results in double instrumentation and tag loss.
So, Please add a compatibility note to the official Micrometer documentation for for users running jOOQ newer(i.e., latest versions), informing users that certain newly added overloads (e.g., DefaultDSLContext#fetchValue(SelectField)) explaining that certain newly added overloads (e.g., DefaultDSLContext#fetchValue(SelectField)) may internally delegate to select(...), which can cause double instrumentation and tag loss with MetricsDSLContext unless Micrometer provides corresponding overrides.