1 2 Previous Next 20 Replies Latest reply on Oct 20, 2005 6:27 PM by epbernard

    Query performane & CascadeType.PERSIST

    jc7442

      Execution time may vary for the same query from something close to 0 to a few seconds accroding to the context (ie persistent object retrieves previously). Is it a bug ?

      Following a sample of code to reprodroduce that problem:

      @Entity()
      public class Protocol {
      ...
      @OneToMany(cascade = CascadeType.PERSIST)
      @JoinTable(table = @Table(name = "Protocol_Param"), joinColumns = { @JoinColumn(name = "relation_id") }, inverseJoinColumns = @JoinColumn(name = "position_id"))
       public List<Parameter> getParameters() {
       return parameters;
       }
      ...
      }
      @Entity()
      public class Parameter {
      ...
      }
      

      I create a Protocol containg 500 parameters. Its id is 1. Then I execute following code using a stateless session bean:
       @PersistenceContext(unitName = "myDBTest")
       private EntityManager manager;
       @TransactionAttribute(TransactionAttributeType.REQUIRED)
       public void sample() {
       long l0 = System.currentTimeMillis();
       Protocol p = manager.find(Protocol.class, 1);
       // ((InjectedEntityManager)manager).getHibernateSession().evict(relation);
       long l11 = System.currentTimeMillis();
       Query query = manager.createQuery("SELECT count(t) FROM Taxon t");
       System.out.println(query.getSingleResult());
       long l2 = System.currentTimeMillis();
       System.out.println("Find = " + (l11 - l0) / 1000.0);
       System.out.println("Query = " + (l2 - l11) / 1000.0);
       System.out.println("Total = " + (l2 - l0) / 1000.0);
       }
      

      -I invoke the session bean several time. Find is very quick (around 0.0xs), query s quite long (around 0.5s).
      -If I evict from the session the object return by the find, query becomes very fast (around 0.0xs).
      -If I change the mapping in the Protocol for the OneToMay relationship (I remove the cascade = CascadeType.PERSIST part of the annotation), query becomes very fast (around 0.0xs).
      - If I comment the find method, query becomes very fast (around 0.0xs).

      I have reproduce that with several kind of classes with a List containing a lot of objects and with CascadeType.PERSIST. In that case, no objects are persisted, they are just retrieved form DB. Moreover the fetch for the list is LAZY, consequently the size of the list should have no impact of the performance. But in that case it has a great impact (not on the find execution but on the query ???).
      It looks like a bug in some cache.
      This sample may look stupid, it is just something that I isolate from a real application.
      The same impact on the execution time of the query may be seen, if a lot of object are persisted in the same JTA transaction.

      I really suspect a bug in hibernate cache. I use JBoss4.0.3rc2, EJB3rc1 and mysql.

      I can use workaround such as not use CascadeType.PERSISTENT in my collection, but it makes thinks harder to implement.
      I can also evict objects from the session, but it is just a hook specific to hibernate (it is ,ot ejb3). Moreover it will make the source code harder to implmenent and not very reusable.

      Has someone already see that problem ? Is it a bug ?
      Due to that kind of bug my application has very poor performance even if each query execute separatly are fast.

      Regards





        • 1. Re: Query performane & CascadeType.PERSIST
          epbernard

          It's not a bug in Hibernate cache but I fixed this in CVS.

          • 2. Re: Query performane & CascadeType.PERSIST
            jc7442

            Do you have an idea when this patch will be included in an hibernate version ?

            I'm currently trying to evaluate/compare two kinds of architecture for an application (EJB2/JDO vs EJB3). None and the workaround I mentionned in the first post are acceptable for a large application. Consequently, for the moment, I have very poor performance in EJB3 compare to JDO. I suspect that with that patch, EJB3 solution will be much more effcient. Moreover, from a developper point of view, EJB3 solution is much easier to implement.

            In order that I can go further in my study, is it possible/easy to include the correction you have done in a jar file ? Which class and what should I modify ?

            Regards

            • 3. Re: Query performane & CascadeType.PERSIST
              epbernard

              You need to take Hibernate3 CVS HEAD
              Hibernate Annotations CVS HEAD
              Hibernate Entity Manager CVS HEAD
              and, if you use the JBoss EJB3 plug them in to JBoss EJB3 CVS HEAD

              It should work.

              • 4. Re: Query performane & CascadeType.PERSIST
                jc7442

                I try to get sources form CVS, I'm not pretty sure where to get it.

                I try:

                - hibernate-entitymanager.jar
                cvs.sourceforge.net:/cvsroot/hibernate HibernateExt

                - hibernate-annotations.jar
                cvs.sourceforge.net:/cvsroot/hibernate HibernateExt/metadata

                - hibernate3.jar
                cvs.sourceforge.net:/cvsroot/hibernate Hibernate3

                - jboss-ejb3.jar (It seems that classes org.jboss.ejb.* are missing)
                cvs.sourceforge.net:/cvsroot/jboss jboss-ejb3


                Could you please confirm if cvs adresses are correct and if something is missing ?

                Regards

                • 5. Re: Query performane & CascadeType.PERSIST
                  epbernard
                  • 6. Re: Query performane & CascadeType.PERSIST
                    jc7442

                    In CVS, I have downloaded those jar files (class compile 09/09/2005). It fixed the problem with the List.
                    Unfortunatelly, there is exactly the same kind of problem with single reference. Following some code to reproduce it:

                    @Entity()
                    public class Test {
                     ...
                     private Ref ref;
                     @ManyToOne(cascade = CascadeType.PERSIST)
                     public Ref getRef() {
                     return ref;
                     }
                    }
                    
                    @Entity()
                    public class Ref {
                    ...
                    }
                    
                    


                    I create 5000 Test with for each one a reference to Ref. In a stateless bean I execute the following code :
                    long l0 = System.currentTimeMillis();
                     int cpt = 0;
                     for (int i = 1; i < 5000; i++) {
                     Test t = manager.find(Test.class, i);
                     }
                     long l11 = System.currentTimeMillis();
                     Query query = manager.createQuery("SELECT count(t) FROM TaxonBO t");
                     query.getSingleResult();
                     long l2 = System.currentTimeMillis();
                     System.out.println("Find = " + (l11 - l0) / 1000.0);
                     System.out.println("Query = " + (l2 - l11) / 1000.0);
                     System.out.println("Total = " + (l2 - l0) / 1000.0);
                    


                    Query takes about 1s.

                    If I just modify the previous code to evict Test instances (using hibernate API), query takes around 0.0x s.
                    If I remove the cascade type for Test->Ref query takes around 0.0x s.


                    It looks like the same problem that I had with the previous RC with List&CascadeType.PERSISTENT. For List problem has been fixed but not for simple reference.

                    In fact in my application, objects are more complex and I reproduce the problem with much fewer instance.


                    Is it a known problem ?



                    • 7. Re: Query performane & CascadeType.PERSIST
                      epbernard

                      I did the same test as you and find no significative difference between evicting the objects or not after a decent warming up time.

                      Are you sure that your conditions are fine.

                      • 8. Re: Query performane & CascadeType.PERSIST
                        jc7442

                        My config is:
                        - JBoss 4.0.3RC2, i use the config All + EJB3 RC1
                        - MySql 3.x
                        -Hibernate3.1beta3 (dowload from CVS: jboss\repository.jboss.com\hibernate\3.1beta3\lib
                        -Hibernate-entitymanager 3.1beta3 (download from jboss\repository.jboss.com\hibernate-entitymanager\3.1beta3)
                        -Hibernate-annotations (dowload from CVS:jboss\repository.jboss.com\hibernate-annotations\3.1beta5)

                        The query without evicting objects from the Hibernate session takes in average around 0.8 s. I change the database to use postgress, it takes 0.180 s. If I evict object or if I change the cascadeType, with the 2 DB, it takes around 0 s.

                        On wich DB did you try to reproduce that problem ?

                        • 9. Re: Query performane & CascadeType.PERSIST
                          jc7442

                          Just a little correction I use mySql 4.0.20

                          • 10. Re: Query performane & CascadeType.PERSIST
                            epbernard

                            MySQL, PostgreSQL, HSQLDB, but the DB has nothing to do with evict().
                            If you see some extra interaction between your DB and hibernate (show_sql) when you don't eveit, there is something wrong elsewhere in your code.

                            If there is not extra interaction, then, your test has some warm up issues

                            Here is my test, I also switch the last two tests to be fair => no changes.

                            The first test (warm up one) is of course much much longer.

                            public void testPerfCascadeAndFetchEntity() throws Exception {
                             EntityManager em = factory.createEntityManager();
                             //init data
                             em.getTransaction().begin();
                             for (int i = 0 ; i < 5000 ; i++) {
                             Troop disney = new Troop();
                             disney.setName( "Disney" );
                             Soldier mickey = new Soldier();
                             mickey.setName( "Mickey" );
                             disney.addSoldier( mickey );
                             em.persist( disney );
                             }
                             em.getTransaction().commit();
                             em.close();
                            
                             //Warm up loop
                             em = factory.createEntityManager();
                             em.getTransaction().begin();
                             for (int i = 0 ; i < 5000 ; i++) {
                             Soldier soldier = em.find( Soldier.class, new Integer(i) );
                             //( ( HibernateEntityManager ) em ).getSession().evict(soldier);
                             }
                             long l11 = System.currentTimeMillis();
                             Query query = em.createQuery("SELECT count(t) FROM Soldier t");
                             query.getSingleResult();
                             long l2 = System.currentTimeMillis();
                             System.out.println("Query1 " + (l2 - l11));
                             em.getTransaction().commit();
                             em.close();
                            
                             //do not evict
                             em = factory.createEntityManager();
                             em.getTransaction().begin();
                             for (int i = 0 ; i < 5000 ; i++) {
                             Soldier soldier = em.find( Soldier.class, new Integer(i) );
                             //( ( HibernateEntityManager ) em ).getSession().evict(soldier);
                             }
                             l11 = System.currentTimeMillis();
                             query = em.createQuery("SELECT count(t) FROM Soldier t");
                             query.getSingleResult();
                             l2 = System.currentTimeMillis();
                             System.out.println("Query " + (l2 - l11));
                             em.getTransaction().commit();
                             em.close();
                            
                             //evict
                             em = factory.createEntityManager();
                             em.getTransaction().begin();
                             for (int i = 0 ; i < 5000 ; i++) {
                             Soldier soldier = em.find( Soldier.class, new Integer(i) );
                             ( ( HibernateEntityManager ) em ).getSession().evict(soldier);
                             }
                             l11 = System.currentTimeMillis();
                             query = em.createQuery("SELECT count(t) FROM Soldier t");
                             query.getSingleResult();
                             l2 = System.currentTimeMillis();
                             System.out.println("Query " + (l2 - l11));
                             em.getTransaction().commit();
                             em.close();
                             }


                            • 11. Re: Query performane & CascadeType.PERSIST
                              jc7442

                              My test case is quite different. If I execute your test case there is no problem. Just change

                              for (int i = 0 ; i < 5000 ; i++) {
                               Soldier soldier = em.find( Soldier.class, new Integer(i) );
                               //( ( HibernateEntityManager ) em ).getSession().evict(soldier);
                               }
                              

                              with
                              for (int i = 0 ; i < 5000 ; i++) {
                               Troop troop = em.find( Troop.class, new Integer(i) );
                               //( ( HibernateEntityManager ) em ).getSession().evict(troop);
                              }
                              


                              Problem is that Troop->Solidier is a @OneToMany(cascade = CascadeType.PERSIST). If I remove the cascadeType or if I evict Troop instance, it works quick and fine.

                              • 12. Re: Query performane & CascadeType.PERSIST
                                epbernard

                                 

                                public void testPerfCascadeAndFetchEntity() throws Exception {
                                 EntityManager em = factory.createEntityManager();
                                 //init data
                                 em.getTransaction().begin();
                                 for (int i = 0 ; i < 5000 ; i++) {
                                 Troop disney = new Troop();
                                 disney.setName( "Disney" );
                                 Soldier mickey = new Soldier();
                                 mickey.setName( "Mickey" );
                                 disney.addSoldier( mickey );
                                 em.persist( disney );
                                 }
                                 em.getTransaction().commit();
                                 em.close();
                                
                                 //Warm up loop
                                 em = factory.createEntityManager();
                                 em.getTransaction().begin();
                                 for (int i = 0 ; i < 5000 ; i++) {
                                 //Soldier soldier = em.find( Soldier.class, new Integer(i) );
                                 Troop troop = em.find( Troop.class, new Integer(i) );
                                 //( ( HibernateEntityManager ) em ).getSession().evict(soldier);
                                 }
                                 long l11 = System.currentTimeMillis();
                                 Query query = em.createQuery("SELECT count(t) FROM Soldier t");
                                 query.getSingleResult();
                                 long l2 = System.currentTimeMillis();
                                 System.out.println("Query1 " + (l2 - l11));
                                 em.getTransaction().commit();
                                 em.close();
                                
                                 //do not evict
                                 em = factory.createEntityManager();
                                 em.getTransaction().begin();
                                 for (int i = 0 ; i < 5000 ; i++) {
                                 Troop troop = em.find( Troop.class, new Integer(i) );
                                 ( ( HibernateEntityManager ) em ).getSession().evict(troop);
                                 }
                                 l11 = System.currentTimeMillis();
                                 query = em.createQuery("SELECT count(t) FROM Soldier t");
                                 query.getSingleResult();
                                 l2 = System.currentTimeMillis();
                                 System.out.println("Query " + (l2 - l11));
                                 em.getTransaction().commit();
                                 em.close();
                                
                                 //evict
                                 em = factory.createEntityManager();
                                 em.getTransaction().begin();
                                 for (int i = 0 ; i < 5000 ; i++) {
                                 //Soldier soldier = em.find( Soldier.class, new Integer(i) );
                                 Troop troop = em.find( Troop.class, new Integer(i) );
                                
                                 //( ( HibernateEntityManager ) em ).getSession().evict(troop);
                                 }
                                 l11 = System.currentTimeMillis();
                                 query = em.createQuery("SELECT count(t) FROM Soldier t");
                                 query.getSingleResult();
                                 l2 = System.currentTimeMillis();
                                 System.out.println("Query " + (l2 - l11));
                                 em.getTransaction().commit();
                                 em.close();
                                 }


                                On MySQL
                                0 ms
                                32 ms

                                On PostreSQL
                                15ms
                                16ms

                                All those values are quite not significant regarding the VM precision.


                                • 13. Re: Query performane & CascadeType.PERSIST
                                  epbernard

                                  Of course I've eliminated the first printed result (warm up)

                                  • 14. Re: Query performane & CascadeType.PERSIST
                                    jc7442

                                    OK. I'll try to investigate that a little more.

                                    I have that kind of performance only if CascadeType for the relationship between Troop->(*)Soldier is different from PERSISTENCE.

                                    With CascadeType to persist, I get:
                                    - MySQl without evict 0.8s
                                    - MySQl with evict 0.016s

                                    - Postgres without evict 0.178s
                                    - Postgreswith evict 0.016s

                                    Another difference with you test case is that I am in a SessionBean with an EntityManager get by injection.

                                    I'll try to investigate that a little more on monday.

                                    1 2 Previous Next