-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: many lazy OneTo* relations on one Entity expensive?!
PostPosted: Wed May 20, 2009 4:28 pm 
Beginner
Beginner

Joined: Thu Apr 13, 2006 12:56 pm
Posts: 23
I have a User entity that has 20 OneToMany and 5 OneToOne relations - all lazy. Whenever a User entity is loaded (proxy actually initialized) via DefaultLoadEventListener.loadFromSceondLevelCache(), 79% of that method's time is spent in CollectionType.assemble() (20 invocations/User) and OneToOneType.assemble (5 invocations/User). 60% of CollectionType.assemble()'s time is spent in StatefulPersistenceContext.addUninitializedCollection(). I've confirmed that Hibernate isn't trying to fetch data for these OneToX relations, but merely initializing given User properties to Collection and Entity proxies.

For example, if I want to iterate over 99 Users (which are in 2nd level cache) and get their userNames 79% of time is spend on the above, v. only 5% of time spent getting data from the 2nd level cache. This doesn't bode well for scalability. Is there something wrong with my OneToX mappings that makes initializing User entity particularly expense or is this just how costly proxying and I should rework my domain model to avoid having even 20 lazy relations off User?

Sample service method
Code:
List<Long> pkList = unrelatedService.getPKs(); //pk's of 99 cached Users

List<String> userNames = new ArrayList<String>(pkList.size());
for(Long pk : pkList)
{
  User usr = (User) Session.load(User.class, pk);
  userNames.add(usr.getUserName());
}
return userNames;


Above profiled
(Full YourKit snapshot available to those interested)
Code:

+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+-----------------+--------------------+
|                                                                                       Name                                                                                        |  Time (ms)   |  Own Time (ms)  |  Invocation Count  |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+-----------------+--------------------+
|  +---com.doppelganger.service.impl.relations.UserRelationsServiceImpl.toUserInfo(User, User, UserRelationType)                                                                    |  671  100 %  |              0  |                99  |
|    |                                                                                                                                                                              |              |                 |                    |
|    +---com.doppelganger.domain.User$$EnhancerByCGLIB$$a271e3b5.getUserName()                                                                                                      |  671  100 %  |              0  |                99  |
|      |                                                                                                                                                                            |              |                 |                    |
|      +---org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(Object, Method, Object[])                                                                                     |  671  100 %  |              0  |                99  |
|        |                                                                                                                                                                          |              |                 |                    |
|        +---org.hibernate.proxy.AbstractLazyInitializer.getImplementation()                                                                                                        |  671  100 %  |              0  |                99  |
|          |                                                                                                                                                                        |              |                 |                    |
|          +---org.hibernate.proxy.AbstractLazyInitializer.initialize()                                                                                                             |  671  100 %  |              0  |                99  |
|            |                                                                                                                                                                      |              |                 |                    |
|            +---org.hibernate.impl.SessionImpl.immediateLoad(String, Serializable)                                                                                                 |  671  100 %  |              0  |                99  |
|              |                                                                                                                                                                    |              |                 |                    |
|              +---org.hibernate.impl.SessionImpl.fireLoad(LoadEvent, LoadEventListener$LoadType)                                                                                   |  671  100 %  |              0  |                99  |
|                |                                                                                                                                                                  |              |                 |                    |
|                +---com.doppelganger.framework.hibernate.CacheUpdatingLoadEventListener.onLoad(LoadEvent, LoadEventListener$LoadType)                                              |  671  100 %  |              0  |                99  |
|                  |                                                                                                                                                                |              |                 |                    |
|                  +---org.hibernate.event.def.DefaultLoadEventListener.onLoad(LoadEvent, LoadEventListener$LoadType)                                                               |  671  100 %  |              0  |                99  |
|                    |                                                                                                                                                              |              |                 |                    |
|                    +---org.hibernate.event.def.DefaultLoadEventListener.load(LoadEvent, EntityPersister, EntityKey, LoadEventListener$LoadType)                                   |  671  100 %  |              0  |                99  |
|                      |                                                                                                                                                            |              |                 |                    |
|                      +---org.hibernate.event.def.DefaultLoadEventListener.doLoad(LoadEvent, EntityPersister, EntityKey, LoadEventListener$LoadType)                               |  671  100 %  |              0  |                99  |
|                        |                                                                                                                                                          |              |                 |                    |
|                        +---org.hibernate.event.def.DefaultLoadEventListener.loadFromSecondLevelCache(LoadEvent, EntityPersister, LoadEventListener$LoadType)                      |  671  100 %  |              0  |                99  |
|                          |                                                                                                                                                        |              |                 |                    |
|                          +---org.hibernate.event.def.DefaultLoadEventListener.assembleCacheEntry(CacheEntry, Serializable, EntityPersister, LoadEvent)                            |  578   86 %  |              0  |                99  |
|                          | |                                                                                                                                                      |              |                 |                    |
|                          | +---org.hibernate.cache.entry.CacheEntry.assemble(Object, Serializable, EntityPersister, Interceptor, EventSource)                                     |  546   81 %  |              0  |                99  |
|                          | | |                                                                                                                                                    |              |                 |                    |
|                          | | +---org.hibernate.cache.entry.CacheEntry.assemble(Serializable[], Object, Serializable, EntityPersister, Interceptor, EventSource)                   |  546   81 %  |              0  |                99  |
|                          | |   |                                                                                                                                                  |              |                 |                    |
|                          | |   +---org.hibernate.type.TypeFactory.assemble(Serializable[], Type[], SessionImplementor, Object)                                                    |  531   79 %  |              0  |                99  |
|                          | |   | |                                                                                                                                                |              |                 |                    |
|                          | |   | +---org.hibernate.type.CollectionType.assemble(Serializable, SessionImplementor, Object)                                                         |  281   42 %  |              0  |             1,980  |
|                          | |   | |                                                                                                                                                |              |                 |                    |
|                          | |   | +---org.hibernate.type.OneToOneType.assemble(Serializable, SessionImplementor, Object)                                                           |  250   37 %  |              0  |               495  |
|                          | |   |                                                                                                                                                  |              |                 |                    |
|                          | |   +---org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(Object, Object[], EntityMode)                                         |   15    2 %  |              0  |                99  |
|                          | |                                                                                                                                                      |              |                 |                    |
|                          | +---org.hibernate.impl.SessionImpl.instantiate(EntityPersister, Serializable)                                                                          |   15    2 %  |              0  |                99  |
|                          | |                                                                                                                                                      |              |                 |                    |
|                          | +---org.hibernate.engine.TwoPhaseLoad.addUninitializedCachedEntity(EntityKey, Object, EntityPersister, LockMode, boolean, Object, SessionImplementor)  |   15    2 %  |              0  |                99  |
|                          |                                                                                                                                                        |              |                 |                    |
|                          +---org.hibernate.cache.entry.StructuredCacheEntry.destructure(Object, SessionFactoryImplementor)                                                        |   62    9 %  |             15  |                99  |
|                          |                                                                                                                                                        |              |                 |                    |
|                          +---org.hibernate.cache.jbc2.entity.TransactionalAccess.get(Object, long)                                                                                |   31    5 %  |              0  |                99  |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+-----------------+--------------------+


User Class
Code:
@Entity
@org.hibernate.annotations.Entity(dynamicUpdate = true, dynamicInsert = true)
@Table(name = "users")
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@BatchSize(size = 1000)
public class User extends Persistent  {

    @Id
    @Type(type = "long")
    @GeneratedValue(generator = "hilo")
    @GenericGenerator(name = "hilo",
    strategy = "com.doppelganger.framework.hibernate.NamedHiLo",
    parameters = {
        @Parameter(name = "max_lo", value = "1")
    })           
private Serializable id;

@NotNull
private String userName;

/**
* one of 20 OneToMany's
*/
    @OneToMany(targetEntity = CustomSpace.class, mappedBy = "owner")
    @Cascade({CascadeType.REMOVE, CascadeType.DELETE_ORPHAN})
    @LazyCollection(LazyCollectionOption.TRUE)
    @BatchSize(size = 1000)
    @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
    private Set<CustomSpace> customSpaces = new HashSet<CustomSpace>();

/**
* one of 5 OneToOne's
*/
    @OneToOne(optional = false, fetch = FetchType.LAZY)
    @PrimaryKeyJoinColumn
    @Cascade({org.hibernate.annotations.CascadeType.PERSIST, org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.REMOVE})
private UserProfile userProfile;

}


Our Stack
Hibernate Core 3.3.1.GA, using CGlib (no build-time instrumentation)
Hibernate Annotations 3.4.0
JBossCache 3.0.3.GA as 2nd level cache provider
Sun Windows JVM 1.6


Top
 Profile  
 
 Post subject: Re: many lazy OneTo* relations on one Entity expensive?!
PostPosted: Wed May 20, 2009 5:07 pm 
Beginner
Beginner

Joined: Thu Apr 13, 2006 12:56 pm
Posts: 23
So essentially in the above case I am looking for a way to avoid calls to CollectionType.assemble and OneToOneType.assemble unless something is actually accessing the OneToX properties in question. Is it possible to:
* build time-instrument User
* mark all OneToX properties as lazy (as can be done with simple properties)?


Top
 Profile  
 
 Post subject: Re: many lazy OneTo* relations on one Entity expensive?!
PostPosted: Fri May 22, 2009 11:35 am 
Beginner
Beginner

Joined: Thu Apr 13, 2006 12:56 pm
Posts: 23
OneToOne resolved; OneToMany still costly
Well, it appears that the above cost can be avoided in case of OneToOne:
* turn on build-time bytecode instrumentation
* change the association annotation to NO_PROXY, so the final annotations look something like this:
Code:
    @OneToOne(optional = false)
    @LazyToOne(LazyToOneOption.NO_PROXY)
    @Fetch(FetchMode.SELECT)
    @PrimaryKeyJoinColumn
private UserProfile userProfile;

This way, when a User proxy is initialized no proxy is generated for User.userProfile and the cost of OneToOneType.assemble() is avoided.

Patch Required
Turns out Hibernate fetches all lazy properties when any one is accessed. A JIRA was filed awhile ago, but to no avail. I submitted a patch fixing this for the case where all lazy properties are OneToOne's on a shared PK.

However, to the best of my understanding, similar lazy approach w.r.t to collections (and thus avoiding CollectionType.assemble()) isn't possible. So the only thing I can think of is to artificially complicate the domain model like so:
* introduce several OneToOne children of User, whose sole purpose is to one OneToMany collections each
* distribute OneToMany collections so that no single domain object owns more than 4-5 (thereby minimizing the cumulative cost of CollectionType.assemble)

Needless to say, this is pretty ugly - if someone knows of a better solution, please chime in.

Thanks!


Top
 Profile  
 
 Post subject: Re: many lazy OneTo* relations on one Entity expensive?!
PostPosted: Mon May 25, 2009 7:39 pm 
Beginner
Beginner

Joined: Thu Apr 13, 2006 12:56 pm
Posts: 23
Declaring Collections Expensive

I was able to confirm that declaring many Collections, even lazy ones, is expensive and the expense is a function of the number of collection properties declared. The expense comes from CollectionType.assemble(). Here are two profiler snapshot of exactly the same client code (see 1st post above for pseudo-code). the only difference is, in the 1st snapshot, User class has 20 @OneToMany's, in the second - 1. Please note that in both cases the client code accesses only one collection.

User class with 20 @OneToMany's declared
Code:
Merged callees

+-------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+-----------------+--------------------+
|                                                                            Name                                                                             |  Time (ms)   |  Own Time (ms)  |  Invocation Count  |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+-----------------+--------------------+
|  +---org.hibernate.event.def.DefaultLoadEventListener.loadFromSecondLevelCache(LoadEvent, EntityPersister, LoadEventListener$LoadType)                      |  984  100 %  |              0  |               199  |
|    |                                                                                                                                                        |              |                 |                    |
|    +---org.hibernate.event.def.DefaultLoadEventListener.assembleCacheEntry(CacheEntry, Serializable, EntityPersister, LoadEvent)                            |  781   79 %  |              0  |               199  |
|    | |                                                                                                                                                      |              |                 |                    |
|    | +---org.hibernate.cache.entry.CacheEntry.assemble(Object, Serializable, EntityPersister, Interceptor, EventSource)                                     |  640   65 %  |              0  |               199  |
|    | | |                                                                                                                                                    |              |                 |                    |
|    | | +---org.hibernate.cache.entry.CacheEntry.assemble(Serializable[], Object, Serializable, EntityPersister, Interceptor, EventSource)                   |  640   65 %  |             31  |               199  |
|    | |   |                                                                                                                                                  |              |                 |                    |
|    | |   +---org.hibernate.type.TypeFactory.assemble(Serializable[], Type[], SessionImplementor, Object)                                                    |  484   49 %  |              0  |               199  |
|    | |   | |                                                                                                                                                |              |                 |                    |
|    | |   | +---org.hibernate.type.CollectionType.assemble(Serializable, SessionImplementor, Object)                                                         |  453   46 %  |             31  |             2,198  |
|    | |   | |                                                                                                                                                |              |                 |                    |
|    | |   | +---org.hibernate.type.AbstractType.assemble(Serializable, SessionImplementor, Object)                                                           |   31    3 %  |              0  |             3,289  |
|    | |   |                                                                                                                                                  |              |                 |                    |
|    | |   +---org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(Object, Object[], EntityMode)                                         |  125   13 %  |              0  |               199  |
|    | |                                                                                                                                                      |              |                 |                    |
|    | +---org.hibernate.persister.entity.AbstractEntityPersister.afterInitialize(Object, boolean, SessionImplementor)                                        |   46    5 %  |              0  |               199  |
|    | |                                                                                                                                                      |              |                 |                    |
|    | +---org.hibernate.type.TypeFactory.deepCopy(Object[], Type[], boolean[], Object[], SessionImplementor)                                                 |   46    5 %  |             15  |               199  |
|    | |                                                                                                                                                      |              |                 |                    |
|    | +---org.hibernate.impl.SessionImpl.instantiate(EntityPersister, Serializable)                                                                          |   31    3 %  |              0  |               199  |
|    | |                                                                                                                                                      |              |                 |                    |
|    | +---org.hibernate.engine.TwoPhaseLoad.addUninitializedCachedEntity(EntityKey, Object, EntityPersister, LockMode, boolean, Object, SessionImplementor)  |   15    2 %  |              0  |               199  |
|    |                                                                                                                                                        |              |                 |                    |
|    +---org.hibernate.cache.jbc2.entity.TransactionalAccess.get(Object, long)                                                                                |  156   16 %  |              0  |               199  |
|    |                                                                                                                                                        |              |                 |                    |
|    +---org.hibernate.cache.entry.StructuredCacheEntry.destructure(Object, SessionFactoryImplementor)                                                        |   46    5 %  |              0  |               199  |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+-----------------+--------------------+

Generated by YourKit Java Profiler 8.0.7 May 25, 2009 4:35:43 PM

User class with 1 @OneToMany declared
Code:
Merged callees

+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+-----------------+--------------------+
|                                                                                       Name                                                                                       |  Time (ms)   |  Own Time (ms)  |  Invocation Count  |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+-----------------+--------------------+
|  +---org.hibernate.event.def.DefaultLoadEventListener.loadFromSecondLevelCache(LoadEvent, EntityPersister, LoadEventListener$LoadType)                                           |  546  100 %  |              0  |               199  |
|    |                                                                                                                                                                             |              |                 |                    |
|    +---org.hibernate.event.def.DefaultLoadEventListener.assembleCacheEntry(CacheEntry, Serializable, EntityPersister, LoadEvent)                                                 |  281   51 %  |              0  |               199  |
|    | |                                                                                                                                                                           |              |                 |                    |
|    | +---org.hibernate.cache.entry.CacheEntry.assemble(Object, Serializable, EntityPersister, Interceptor, EventSource)                                                          |  171   31 %  |              0  |               199  |
|    | | |                                                                                                                                                                         |              |                 |                    |
|    | | +---org.hibernate.cache.entry.CacheEntry.assemble(Serializable[], Object, Serializable, EntityPersister, Interceptor, EventSource)                                        |  171   31 %  |             15  |               199  |
|    | |   |                                                                                                                                                                       |              |                 |                    |
|    | |   +---org.hibernate.type.TypeFactory.assemble(Serializable[], Type[], SessionImplementor, Object)                                                                         |   93   17 %  |              0  |               199  |
|    | |   | |                                                                                                                                                                     |              |                 |                    |
|    | |   | +---org.hibernate.type.CollectionType.assemble(Serializable, SessionImplementor, Object)                                                                              |   62   11 %  |              0  |               298  |
|    | |   | |                                                                                                                                                                     |              |                 |                    |
|    | |   | +---org.hibernate.type.AbstractType.assemble(Serializable, SessionImplementor, Object)                                                                                |   31    6 %  |              0  |             3,289  |
|    | |   |                                                                                                                                                                       |              |                 |                    |
|    | |   +---org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(Object, Object[], EntityMode)                                                              |   62   11 %  |              0  |               199  |
|    | |                                                                                                                                                                           |              |                 |                    |
|    | +---org.hibernate.impl.SessionImpl.instantiate(EntityPersister, Serializable)                                                                                               |   31    6 %  |              0  |               199  |
|    | |                                                                                                                                                                           |              |                 |                    |
|    | +---org.hibernate.persister.entity.AbstractEntityPersister.afterInitialize(Object, boolean, SessionImplementor)                                                             |   15    3 %  |              0  |               199  |
|    | |                                                                                                                                                                           |              |                 |                    |
|    | +---org.hibernate.engine.TwoPhaseLoad.addUninitializedCachedEntity(EntityKey, Object, EntityPersister, LockMode, boolean, Object, SessionImplementor)                       |   15    3 %  |              0  |               199  |
|    | |                                                                                                                                                                           |              |                 |                    |
|    | +---org.slf4j.impl.Log4jLoggerAdapter.isTraceEnabled()                                                                                                                      |   15    3 %  |             15  |               398  |
|    | |                                                                                                                                                                           |              |                 |                    |
|    | +---org.hibernate.engine.StatefulPersistenceContext.addEntry(Object, Status, Object[], Object, Serializable, Object, LockMode, boolean, EntityPersister, boolean, boolean)  |   15    3 %  |              0  |               199  |
|    | |                                                                                                                                                                           |              |                 |                    |
|    | +---org.hibernate.type.TypeFactory.deepCopy(Object[], Type[], boolean[], Object[], SessionImplementor)                                                                      |   15    3 %  |              0  |               199  |
|    |                                                                                                                                                                             |              |                 |                    |
|    +---org.hibernate.cache.jbc2.entity.TransactionalAccess.get(Object, long)                                                                                                     |  234   43 %  |              0  |               199  |
|    |                                                                                                                                                                             |              |                 |                    |
|    +---org.hibernate.cache.CacheKey.<init>(Serializable, Type, String, EntityMode, SessionFactoryImplementor)                                                                    |   15    3 %  |             15  |               199  |
|    |                                                                                                                                                                             |              |                 |                    |
|    +---org.hibernate.stat.StatisticsImpl.secondLevelCacheHit(String)                                                                                                             |   15    3 %  |              0  |               199  |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+-----------------+--------------------+

Generated by YourKit Java Profiler 8.0.7 May 25, 2009 4:38:19 PM

So what now?

Don't really know - but the fact that 2.5x more is spent in CollectionType.assemble for collections that don't end up getting used in a service method then in fetching the data from 2nd level cache is a critical Hibernate perf problem IMO. The ideal would be to:
* stash serialized states representing collection properties during assembleCacheEntry
* call CollectionType.assemble lazily when collection property field is actually accessed (a la lazy properties)

Does anyone have other suggestions?

thanks!


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.