EntityProxy efficiency

145 views
Skip to first unread message

Jerome Thoma

unread,
May 3, 2011, 5:01:59 AM5/3/11
to google-we...@googlegroups.com
Hi everyone,
I have a question about EntityProxy vs. ValueProxy and efficiency issues. I need to load a list of SubProxy objects using RF for display in a listbox inside an editor for an encompassing SuperProxy type. No SubProxy instance is modified by the editor, but because RF does not support inheritance, I must use EntityProxy as SubProxy's super-type. This means unfortunately that on loading the list of SubProxy objects (using one efficient Hibernate call), RF launches one find operation for each objects (thus creating X more Hibernate calls) which is very inefficient. Is this desired behaviour and how do I optimise my loading code. Currently it takes several seconds to load a list of ca. 100 SubProxy objects. I have encountered this problem before and solved it by using ValueProxy objects which are not version-checked by the RF engine. Could someone please explain me why the RF engine does a separate version check for each EntityProxy that I just retrieved from the DB?

Thanks for sharing your knowledge
Jerome

Fidencio Velasquez

unread,
May 3, 2011, 5:04:12 AM5/3/11
to google-we...@googlegroups.com

From me

«Draculaboyman»*

> --
> You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
> To post to this group, send email to google-we...@googlegroups.com.
> To unsubscribe from this group, send email to google-web-tool...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.
>

Fidencio Velasquez

unread,
May 3, 2011, 5:03:45 AM5/3/11
to google-we...@googlegroups.com

«Draculaboyman»*

El may 3, 2011 2:02 a.m., "Jerome Thoma" <thom...@googlemail.com> escribió:

Colin Alworth

unread,
May 3, 2011, 11:16:38 AM5/3/11
to google-we...@googlegroups.com
My understanding is that RF assumes you will be using the same persistence session for the duration of the request to prevent any perf issues here. Those find operations are done because RF considers the ID to be the only valid way to know that it has the latest copy of the object - every time it interacts with your persistence system, it does so through find.

Make sure you are using a single hibernate session for the entire request - my understanding is that if you are doing so, hibernate will return the same instances of those objects instead of looking up and creating a new copy.

Jerome Thoma

unread,
May 4, 2011, 10:20:27 AM5/4/11
to google-we...@googlegroups.com
Thanks a lot Colin,
sounds logical to me, but unfortunately it does not fit at all into our system design. All our RF requests are piped through HttpInvoker beans to our Spring-powered backend (which is shared by many different applications, so I cannot touch it) which of course manages the Hibernate sessions. I wonder if anyone else has the same problems, because it is kind of a common structure for business applications? I'll keep looking for a simple solution. Thanks for the insights!

Jerome

Jerome Thoma

unread,
May 9, 2011, 8:34:21 AM5/9/11
to google-we...@googlegroups.com
I've been thinking some more about your remarks and all my solutions are dead-ends:
- use EntityProxies for all objects. Not possible because I cannot execute a complete RF server call in a single Hibernate session (because Hibernate runs one a different server encapsulated by web services)
- use a mix of EntityProxy (for CRUD operations on a single entity) and ValueProxy (for list operations with more than one entry. This should theoretically work but some of the ValueProxy objects are not transmitted if they are part of an EntityProxy (I think someone else also had the same problem).
- use only ValueProxies. This disregards most of the advantages that RF brings over straight RPCs.
Maybe someone can comment on these scenarios? Does anyone have a good solution for using RF with a backend that is integrated using Spring and HttpInvoker services?

Cheers
Jerome

Thomas Broyer

unread,
May 9, 2011, 8:45:47 AM5/9/11
to google-we...@googlegroups.com
Before sending the response back to the client, RF will ask each EntityProxy it has seen whether it's still "live"; and if it's not, it'll tell the client that the entity has been deleted (generating an EntityProxyChange(DELETED) event on the client-side). This is done by asking the entity's Locator#isLive, which by default does "find(entity.getClass(), getId(entity)) != null".
You can very well override this isLive method in your Locator if you can provide a more efficient check (or want to bypass the check, unconditionally returning 'true').

Jerome Thoma

unread,
May 9, 2011, 9:12:07 AM5/9/11
to google-we...@googlegroups.com
Thanks a lot Thomas!
You've just made my day. Currently we don't need the live check, because all integrity checks are already performed by the backend, so i'll just return true in isLive. Now I should be able to use a single proxy for crud and list operations in most cases.

Have a nice day
Jerome

Thomas Broyer

unread,
May 9, 2011, 10:24:25 AM5/9/11
to google-we...@googlegroups.com
Note that it's not about "integrity check", the goal is only to detect a deleted entity and thus dispatch the appropriate EntityProxyChange event on the client-side.

Jerome Thoma

unread,
May 9, 2011, 10:37:26 AM5/9/11
to google-we...@googlegroups.com
Sorry, I don't exactly understand what you mean. Deleted by whom? Let's say there is a request for a list of objects which I retrieve from my web-service. Why does RF need to check if these objects have not been deleted even though my service has just delivered them as being valid? I feel like I am missing a crucial point here ;)

Cheers
Jerome

Thomas Broyer

unread,
May 9, 2011, 10:55:01 AM5/9/11
to google-we...@googlegroups.com
RF processes "invocations" (method calls) without knowledge of what they do, and their can be several such invocations in the same request (RequestContext).

Imagine I do:
  ctx.foo(entity).fire(...);
RF will check whether 'entity' is still 'live', because it cannot know what 'foo' made of it: updated it? (version check) deleted it? (liveness check) or maybe just read it or simply ignored it?
Now with:
  ctx.findAll().to(...); // will return the entity in the list
  ctx.delete(entity).to(...);
  ctx.fire();
Or:
  ctx.saveAndCommit(entity).fire(...); // where the "working copy" will be removed after the "commit"

In all cases, RF will do the "liveness check" and then the "version check" to inform the client of which EntityProxyChange event to fire (WriteOperation.UPDATE or WriteOperation.DELETE).
Those checks are *only* there to inform the client of what RF thinks has happened (from its small knowledge, e.g. it won't tell you other entities –that the client already knows– have been updated/deleted unless they appear as arguments or return values, possibly nested in other entities/values).

sheamus

unread,
May 9, 2011, 11:37:41 AM5/9/11
to Google Web Toolkit
Thanks Thomas, that's the solution I was looking for too.

The 'solution' I am using now to prevent the lookup before returning
each object was to populate an 'ephemeral' object. I simply create a
duplicate object using the same EntityProxy class and it returns to
the client without doing the lookup.

Because the duplicate object is only returned for list searches I only
partially populate it (to make it lighter across the wire) and it is
stored in memcache so the performance hit is only for the first user.
But it is a bit of a hack, so thanks again.

Event ev = pm.getObjectById(Event.class, key);
Event ephemeral = makeEphemeral(ev);
returnEvents.add(ephemeral);
searchCache.put(key, ephemeral);

JoseM

unread,
Feb 21, 2012, 6:31:52 PM2/21/12
to google-we...@googlegroups.com
Is there a way for us to easily do this? In other words is there one method which I can mark which starts and ends the session for one request?

Thomas Broyer

unread,
Feb 22, 2012, 5:44:52 AM2/22/12
to google-we...@googlegroups.com
I never used JPA or JDO with RF, and I don't know those technologies that much, but I think what you need is the "open session in view" pattern [1] to maintain a single session for the whole HTTP request, and then use one transaction per service method.

Alex opn

unread,
Feb 22, 2012, 8:48:25 AM2/22/12
to google-we...@googlegroups.com
See also:

http://code.google.com/p/google-guice/wiki/JPA

guice-persist supports the "open session in view" pattern, too

JoseM

unread,
Feb 22, 2012, 10:02:23 AM2/22/12
to google-we...@googlegroups.com
Thanks for the reply.  I was thinking of doing something similar but the problem I can see is that the session could possible encapsulate several different unrelated transactions because of the way RequestFactory handles appending of different .fire() calls (at least from what I understand of the code).  I see that in one place RF is calling the actual locator methods for all of the different fire()s and then in another separate piece of code it calls the isLive method.

Thomas Broyer

unread,
Feb 22, 2012, 11:19:10 AM2/22/12
to google-we...@googlegroups.com
One .fire() equates one HTTP request. You can .append() request contexts, but then only one of them can be fire()d (you'll have an exception otherwise).

Otherwise, yes, one session will have several transactions, possibly unrelated, but that's the whole point of "batching" things in a RequestContext in Request Factory; and AFAICT it doesn't matter much wrt JPA (on the contrary, the goal of sharing the session is to share its entity cache so that your isLive calls could be possibly be cheap).
Reply all
Reply to author
Forward
0 new messages