Hello,

> I'll answer backwards.

Yes, to add some clarity to the topic :-)
These design decisions are hard to fully grasp by e-mail...

> Synchronizing on a static object is not a problem as far as I know.

Most often, no. But static state can be a major pain in clustered
and/or distributed environments from my experience. As soon as you
cluster your J2EE application (for instance in Weblogic), and you use
static objects, you'll have to think about synchronizing them
manually. The current JooqLogger is a static *final* object. It is
thus guaranteed to always be the same in every cluster node. But a
static listener list where clients can add / remove listeners can be
problematic. I'm not saying it is wrong. In your use case, it will
work perfectly. But no one keeps you from maintaining such a static
list in your implementation (see further down).

Another example would be Weblogic's call-by-reference EJB setting.
Some sample documentation:
http://docs.oracle.com/cd/E11035_01/wls100/perform/topten.html#wp1132668

While this is not part of the EJB standard, it can be a major
performance improvement in some fine-tuned setups. Other containers
may have similar features. Unfortunately, I only know Weblogic. Here's
how jOOQ behaves with respect to this Weblogic setting. Let's assume
we use EJB SessionBeans and both ends of the bean call maintain
references to jOOQ factories.

1. static list in jOOQ
    call-by-reference=true: both ends of the EJB session bean call
would have the same list instance
    call-by-reference=false: the two ends of the bean would have
different list instances
2. instance list in Factory
    call-by-reference=true: both ends have the same list instance
    call-by-reference=false: both ends have the same list instance
(serialized with the Factory)

Another example: I've had tremendous trouble in the past with Apache
FOP 0.20's static configuration. While this is very convenient in a
PDF-generating script, it was really painful when generating PDF's in
a container. Unfortunately, I don't remember the details anymore, but
it involved quite a bit of FOP patching...

So you can call this FUD if you want. But, I try to avoid static state
as much as possible. Static is usually the path of least resistance at
design and implementation time, but the time saved in finding an
equivalent "dynamic" solution will be paid later when debugging weird
issues related to static-ness. As I said, maybe this is not true in
your case, and it's also just an opinion. :-)

>> In a J2EE context,
>> it would be very hard to get this correctly within jOOQ.
>
> I don't know what "this" refers to.

"this" refers to synchronizing updatable static state across class
loaders in a cluster / distributed J2EE application.

> It is a solution, but it is not an optimal solution for 2 reasons:
> - it reduces performance for the lazy.
> - it complexifies registration of listeners for those who want to
> maximize performance.

This is where the misunderstanding was. I provided you with a silly
sample implementation of "YourJooqAdapter", maybe I should have
elaborated more in detail. Note that I used the term "Adapter". It is
just a bridge between your tool and jOOQ. No one keeps you from
writing the adapter like this:

-------------------------------------------------------------------
class YourJooqAdapter implements JooqLoggerPlugin {
 static Object LOCK = new Object();
 static List<YourListenerType> listeners;

 // Your adapter maintains the static synchronized list,
 // Not jOOQ!
 static void register(YourListenerType listener) {
   synchronized (LOCK) {
     listeners.add(listener);
   }
 }

 static void unregister(YourListenerType listener) { ... }

 @Override // From the jOOQ Plugin interface
 void event(Query query) {
 }
}
-------------------------------------------------------------------

> Because you don't like the idea of a central place, your work around
> is to always add a listener to the factories, which would ignore all
> the events unless it "isListening". The problem is that all the
> mechanics I explained above would happen, for nothing, 99.9% of the
> time (this is what I called "reducing performance for the lazy").

Not really. Obviously, my sample was too silly. But jOOQ will not
perform any heavy load in 99.9% of the time. The heavy load is
implemented in your adapter. And your adapter knows when to run the
"heavy" code and when not to run it. By configuring your factories
accordingly, you will have full control over both jOOQ's default
logging and your data collection. Think about these methods (still
draft):

-------------------------------------------------------------------
// Maybe these could even be used to optionally
// modify rendered SQL?
void eventRenderStart(Query query);
String eventRenderEnd(Query query, String sql);

// These methods allow for wrapping the statement at any moment.
// But that decision is up to the adapter
PreparedStatement eventPrepareStart(Query query, PreparedStatement stmt);
PreparedStatement eventPrepareEnd(Query query, PreparedStatement stmt);
PreparedStatement eventBindStart(Query query, PreparedStatement stmt);
PreparedStatement eventBindEnd(Query query, PreparedStatement stmt);

// Open point: how to treat java.sql.Statement, java.sql.CallableStatement

// These are useful for measurements only
void eventExecuteStart(Query query);
void eventExecuteEnd(Query query);

// ResultSet / Result wrapping is also optional
ResultSet eventFetchStart(Query query, ResultSet rs);
org.jooq.Result eventFetchEnd(Query query, ResultSet rs, org.jooq.Result);
-------------------------------------------------------------------

Even better, this design will finally clean up the static
ConfigurationRegistry that I introduced due to a request by Sander
Plas. Note that I've had similar discussions with him. Already then, I
didn't like the idea of introducing static state in jOOQ for the same
reasons:
https://groups.google.com/d/topic/jooq-user/0z6akCcH0RE/discussion

The new solution:
-------------------------------------------------------------------
// This can be used to "patch" or re-configure a JDBC
// connection. In the original use case, this was due to
// the Connection being transient an thus lost after
// deserialisation of POJOs
Configuration eventConfigureStart(Query query, Configuration configuration);
-------------------------------------------------------------------

> Advanced Java developers who do not want that overhead would track all
> the factories that get created and lost in GC to a central place of
> theirs so that when they activate/deactivate logging they add/remove
> their listener to all of them. This latter behavior is hard to
> implement and error prone whereas the central access is easy for the
> library to implement.

Why track the factories? The callback has access to them...? You can
still add/remove your listener to your implementation of the plugin,
which, for you specifically, is the same as adding listeners to jOOQ.
But for jOOQ it is not the same.

> For me, logging is a debugging feature mainly because it can have a
> cost (in my code, I also have to aggregate parameters used in a batch
> for example). It is not something that runs permanently but rather a
> way to attach monitoring tools that can perform advanced analysis.

I agree, completely. But I don't see why my idea is so much slower or
more complex. It just helps jOOQ not making any assumptions about
concrete logging strategies and allows for a few more use-cases while
not really impacting yours. It furthermore allows for adding
"strategies" to jOOQ, not "state". Whether or not a strategy maintains
its (static or non-static) state is up to the implementation, up to
you.

What do you think, now? :-)

Cheers
Lukas

Reply via email to