简体   繁体   中英

JOOQ listeners: context data is not cleaned between two queries

In my current project, I use java 11/JOOQ 3.15/Micronaut/Micrometer. In order to have relevant SQL metrics, I would like to put a name on my JOOQ queries.

To do that, I have tried to use the ctx.data() field combined with a custom ExecuteListener.

Let's take a really simplified listener:

@Singleton
public class JooqListener extends DefaultExecuteListener {

    transient StopWatch watch;
    private final MeterRegistry meterRegistry;

    public JooqListener(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    @Override
    public void executeStart(ExecuteContext ctx) {
        watch = new StopWatch();
    }

    @Override
    public void fetchEnd(ExecuteContext ctx) {
        Tags prometheusTag = Tags.of("queryName", ctx.configuration().data("queryName").toString());
        meterRegistry.timer("sql.query.timer", prometheusTag)
            .record(watch.split(), TimeUnit.NANOSECONDS);
    }
    // I have tried to remove the data manually, but not working
    @Override
    public void end(ExecuteContext ctx) {
        ctx.configuration().data().remove("queryName");
    }
}

If I send 2 different queries from two different repositories, like for example:

DSLContext context = DSL.using(jooqConfiguration);
context.data("queryName", "query1");
return context.select(1).from("dual").fetch();

And just after, let say I'm not attentive and I forgot to name my query:

DSLContext context = DSL.using(jooqConfiguration);
return context.select(2).from("dual").fetch();

ctx.configuration().data("queryName") in my listener will always contain "query1", which I didn't expect because ExecuteListeners are listening query by query, and furthermore, I have created two different DSLContexts. It looks like the ctx.data() cannot be cleaned but just overwritten.

Is it an expected behaviour? Is there an other object/method I should use which can be limited to the query scope? (I searched a lot on google but "data" keyword is a little bit annoying...)

Thank you

A DSLContext just wraps a Configuration . It doesn't have its own lifecycle. So, if you're modifying the Configuration.data() map through DSLContext , you're modifying a globally shared object. In other words, you must not modify Configuration.data() except for when you initialise your configuration for the first time. See this section of the manual for more details .

A better way to do what you intend to do is:

// Create a "derived" configuration, which is a new, 
// independent Configuration instance
DSLContext context = DSL.using(jooqConfiguration.derive());
context.data("queryName", "query1");
return context.select(1).from("dual").fetch();

And then, in your ExecuteListener :

@Override
public void fetchEnd(ExecuteContext ctx) {
    // Reading the Configuration.data() is still fine:
    Tags prometheusTag = Tags.of("queryName", 
        ctx.configuration().data("queryName").toString());
    meterRegistry.timer("sql.query.timer", prometheusTag)
        .record(watch.split(), TimeUnit.NANOSECONDS);
}

@Override
public void end(ExecuteContext ctx) {
    // But you shouldn't modify it
    ctx.configuration().data().remove("queryName");
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM