javaspringtransactionstransactionalspring-orm

Spring Transactional slows down complete process


I am trying to analyze a situation where I have two classes. One class is ProcessImpl which is starting point and internally calls other child transactions.I dont know whats going wrong. The processImpl is importing some stuff and writing related data to database.

Specs

Spring-orm version : 3.2.18.RELEASE.

JDK version : 1.8.

Db : H2 (on any db same performance is recorded).

Issue

If I remove @Transactional from ProcessImpl.processStage() the process takes ~ 50 seconds If I keep @Transactional from ProcessImpl.processStage() the process takes ~ 15 minutes. Dont know why this is happening. I have been trying to solve this issue since long but no luck. Please have a look at code below.

Requirement: The complete processStage() should complete or rollback completely, even if one of child transactions fail.

Fyi : I also get lot of messages like : "Participating in existing transaction". Tried to get over this by adding propagation=Propagation.NESTED to processStage() but did not work.

ProcessImpl Class.

public class ProcessImpl {

    /*This is the big transaction that calls other transactional stuff from MyServiceImpl
    * This is starting point you can say for the process...
    * 
    * If we remove @Transactional from here the process is lightning fast 
    * With transactional : 15minutes
    * Without transactional : 50 seconds
    * */
    @Transactional
    public void processStage(){
        MyServiceImpl mp = new MyServiceImpl();
        //do some stuff
        mp.doWork1();

        //do more work
        mp.doWork2();

    }

}

MyServiceImpl Class

class MyServiceImpl{

    @Transactional
    public void doWork1(){
        Object o = doChildWork();
        // and more stuff
        //calls other class services and dao layers
    }

    @Transactional
    public void doWork2(){
        //some stuff
        doChildWork2();
        doChildWork();
        //more work
    }

    @Transactional
    public Object doChildWork(){
        return new Object(); //hypothetical, I am returning list and other collection stuff
    }

    @Transactional
    public Object doChildWork2(){
        return new Object(); //hypothetical, I am returning list and other collection stuff
    }

}

Also, here will I get self invocation issue, which is not advisable in Transactional?


Solution

  • It is hard to guess what exactly is happening in your code, however these are the possible problems:

    Lock on DB level. This could happen when you update the same DB object within doWork1() and doWork2(). Since both of the methods are performed within one transaction the updates done inside doWork1() will not be committed until doWork2() is completed. Both the methods might try to lock the same DB object and wait for it. Technically it could be any DB object: row in a table, index, whole table, etc.

    Analise your code and try to find what could be locked. You can also look into DB transaction log while the method is running. All popular DBs provide functionality that helps to find problematic places.

    Slow down during Hibernate context refresh. In case when you update too many objects ORM engine (lets say Hibernate) has to sink them and keep them in memory. Literally Hibernate must have all old states and all new states of updated objects. Sometimes it does this not in an optimal way.

    You can indicate this using debug. Try to find the slowest place and check what exactly is being invoked there. I might guess that it slows down when hibernate updates state of the cache.

    One more issue. I see that you create MyServiceImpl using constructor during processStage(). I'd recommend you to replace this code by spring autowiring. First of all the way you're using it is not the way it was designed to be used, but theoretically that could also somehow influence on the execution.

    will I get self invocation issue, which is not advisable in Transactional?

    No, it will work just fine ignoring all annotations. Calls of doChildWork() and doChildWork2() inside doWork2() will be treated as standard java calls (spring is not able to add any "magic" to them as long as you're invoking them directly).