Hi Lucas,

I have been watching your project for a while now and have to say that you 
are dying a fantastic job! 
(...have you got a full time job as well?)

Couple of months back I decided to use jOOQ (for all the obvious reasons) 
in my project. I am now at the 
point when I have got a decent piece of functionality implemented and 
started to write some high level tests 
for my use case. Each tests runs with a simple DYI (do it yourself) 
profiler that gives me time breakdown 
for the relevant methods within the execution stack.

During these tests I have first noticed that it takes disproportionally 
long time to construct the Factory class. 
To give you an example I have a test case that inserts 12 records for 7 
different entity types (in other words 
12 records into 7 tables). The whole test runs 266 ms and out of this 140 
ms is spent to construct the Factory
(with a JDBC connection and H2 Dialect in my case). After checking the 
source code I did not find anything there 
that should take so long so I added a couple of traces there and run it 
through the debugger. I have  noticed 
that after the initial Factory constructor call there are additional 15 
calls as well. These calls come from 
the default static initialiser that is run for each database dialect. 

    static {
        for (SQLDialect dialect : SQLDialect.values()) {
            Factory.DEFAULT_INSTANCES[dialect.ordinal()] = new 
Factory(dialect);
        }
    }

All in all one could say that this is not a big deal as you construct the 
factory once (and incur this cost 
only once) but since the Factory is constructed with the requested dialect 
it seems a bit pointless to have this
done for all available dialects.


The side effect/product of the added traces in the Factory constructor 
brought up another issue. The Factory 
constructor is called multiple times after the initial instance of the 
Factory is created. In this specific test case 
there are additional 110 calls. The story starts with the construction of 
the insert statement - in my case one 
of them looks like this:

        return ftry
            .insertInto(CATEGORY, 
                        CATEGORY.ID, 
                        CATEGORY.USR, 
                        CATEGORY.VER, 
                        CATEGORY.TYPE, 
                        CATEGORY.NAME)
            .values(ent.getId(), 
                      ent.getUser(), 
                    newVer,
                    ent.getEnum(Property.CategoryType),
                    ent.getString(Property.Name));

Which eventually leads to adding key/value pairs to a linked hash map that 
calls hashCode() on TableFiedlImp class
that is implemented in the AbstractQueryPart class:
    
    @Override
    public int hashCode() {
        // This is a working default implementation. It should be 
overridden by
        // concrete subclasses, to improve performance
        return create().renderInlined(this).hashCode();
    }
 
Seems like something got forgotten here as this default implementation 
calls: 

    protected final Factory create() {
        return create(getConfiguration());
    }

On the Factory class which then calls:

    protected final Factory create(Configuration configuration) {
        return Factory.getNewFactory(configuration);
    }

And then the constructor:
    
    @SuppressWarnings("deprecation")
    final static Factory getNewFactory(Configuration configuration) {
        if (configuration == null) {
            return 
getNewFactory(DefaultConfiguration.DEFAULT_CONFIGURATION);
        }
        else {
            return new Factory(
                configuration.getDataSource(),
                configuration.getConnection(),
                configuration.getDialect(),
                configuration.getSettings(),
                configuration.getSchemaMapping(),
                configuration.getData());
        }
    }

 
 The bottom line is that the constructor is called for each column of each 
record that is constructed. 
 (see the attached snapshot for the whole stack trace). I have not profiled 
any updates as yet but it 
 seems likely that the behaviour will be the same as they most likely reuse 
the same functionality. 
 
 Regards,
 Adam
 
 PS
 It is a great shame that there is not a simple JUnit profiler add-on 
available which would show these 
 kinds of issue straight away - any takers?
 

<<attachment: stack-call.png>>

Reply via email to