Hi,I currently have some problems with entity version checks / optimistic locking using JPA on server side and I wonder how RequestFactory would handle my problem.Currently the application I am working on has an auto save mechanism. That means each time the user changed some data the application will wait a couple of milliseconds and if no additional changes are made during this time the application sends a save command (command / dispatch pattern) to the server. The server then loads the JPA entity from the database and compares its entity version with the one provided by the client entity that was send with the save command. If they match, data will be copied from client entity to the JPA entity and then saved to our database. When the entity is saved we return the new entity version to the client application which updates it in the client entity object. If they do not match, an exception will be thrown and the client application reloads the object and updates the UI because the database contains newer data. Our server is pretty stateless and we have one transaction per request.So its similar to RequestFactory. We have a client entity class and a JPA entity and copy data between them on server side (along with some computations if needed).Now our problem: As we do our auto save quite instant (about 100-300 ms after the last user input) it may happen that we fire two or more save commands without getting a response from our server because the network connection is bad (basically the round trip time would be longer as our auto save timer / 100-300ms). Thus the second save command will fail because the first one results in an updated entity version in our database but the client does not now about it yet and thus the second save command contains the same entity version as the first one.
How would RequestFactory handle such a situation? What happens if you call xxxRequest.persist().using(object).fire() twice without having received a result during both calls (what could happen if we integrate RF instead of a command pattern)?
I know that RequestFactory currently does no real optimistic lock check (http://code.google.com/p/google-web-toolkit/issues/detail?id=6046) because it somehow disappeared in the source code. But how would it handle entity versions to make sure optimistic locking works? Are entity versions stored in the generated proxy classes or is there a server side map that remembers all entity proxys with their entity versions that have been delivered to the client?
How do you do optimistic checking in your application and how do you have solved this issue?
Maybe you should delay autosave requests to until the previous one has returned?
How would RequestFactory handle such a situation? What happens if you call xxxRequest.persist().using(object).fire() twice without having received a result during both calls (what could happen if we integrate RF instead of a command pattern)?Well, first, you can only edit() a proxy once at a time (i.e. in a single RequestContext). Second, when you fire() a RequestContext, the edit()ed proxies are locked until the response comes back.This basically means that if you have to lock the form while you autosave it.I'd suggest you file an enhancement request so you could, e.g. flush the editor to a different object/proxy than was passed to initialize it. That way, you could display() the proxy (so it's not edit()ed), and then flush into an edit()ed version. You'd still have to block until the response come before doing another autosave; and worse, before doing a save(), which means the user would have to wait for the autosave to finish before it can explicitly save the form and go away!
Entity versions are sent to/from the client along with the ID. It's a serialized and encoded version though, because a version can be any kind of object (RequestFactory does a toString() on it, and then base64-encode it).RequestFactory would have to either: find() with the ID and version (in String form) and let you "parse" the version back into you object type (but that would lock RF into always transforming the version to a String), or find() the entity as today and then ask for its version, serialize it and compare with the version sent by the client (same comparison as is done before returning objects to the client, to "generate" the appropriate EntityProxyChange events on the client).
How do you do optimistic checking in your application and how do you have solved this issue?We don't actually; but we're in a special case that users first have to explicitly lock an object (actually, create a private working copy) before they can edit it; so a given user can only be in conflict with "himself"; and using the application in multiple tabs/windows is an officially unsupported use case (yes, we're lucky ;-) ).
On Friday, March 11, 2011 3:50:35 PM UTC+1, Thomas Broyer wrote:Well, first, you can only edit() a proxy once at a time (i.e. in a single RequestContext). Second, when you fire() a RequestContext, the edit()ed proxies are locked until the response comes back.This basically means that if you have to lock the form while you autosave it.I'd suggest you file an enhancement request so you could, e.g. flush the editor to a different object/proxy than was passed to initialize it. That way, you could display() the proxy (so it's not edit()ed), and then flush into an edit()ed version. You'd still have to block until the response come before doing another autosave; and worse, before doing a save(), which means the user would have to wait for the autosave to finish before it can explicitly save the form and go away!Hmm why do I have to lock the form while auto save the data? Is it because I have to call edit() again after receiving the RF response in order to be able to call flush().fire() again? And calling edit() would overwrite any changes in the form the user could have made if I do not lock the form in the mean time?
Ah ok, so each proxy instance holds its serialized version in a field somewhere in the generated autobean and just don't expose it so the developer do not have to bother with it? If so then its basically the same what we do and I guess thats the reason why RF locks the proxy until the response is received. That way RF avoids the issue we currently have by design.
We don't actually; but we're in a special case that users first have to explicitly lock an object (actually, create a private working copy) before they can edit it; so a given user can only be in conflict with "himself"; and using the application in multiple tabs/windows is an officially unsupported use case (yes, we're lucky ;-) ).Yeah really lucky. Having a separate edit/cancel/save button where edit also means "lock that object until save or cancel is called" is always a nice luxury :)
The question is "what would you do of other changes made by the user in the mean time?"Because the proxies are locked, you cannot flush() the driver. And because you cannot edit() a given proxy twice at the same time, you cannot do a "flush().fire() + edit()".But thinking a bit more about it, there might be a solution: instead of edit()ing the proxy, you'd create a copy of it that you give to the editor driver. Then you periodically flush() the driver, but never fire() its context. After the flush(), you copy the proxy values into the proxy you'll send to the server.The problem is: how to clone the proxy? See http://code.google.com/p/google-web-toolkit/issues/detail?id=5794Ah ok, so each proxy instance holds its serialized version in a field somewhere in the generated autobean and just don't expose it so the developer do not have to bother with it? If so then its basically the same what we do and I guess thats the reason why RF locks the proxy until the response is received. That way RF avoids the issue we currently have by design.The reason RF locks the proxy until the response is received is that it will unlock them in case of constraint violations (so you could correct your proxies and fire the same RequestContext again).
It's even better than that ;-)The user has to click a "lock" button to be able to edit the objects (creates a working copy). The form then switches to read/write mode and the "save" button appears. But the save button only saves changes to the working copy; there's a distinct "unlock" button that overwrites the "public copy" with the "working copy" (and another "free" button to "unlock" the "public copy" and simply discards the "working copy").A user can keep his working copy for days, or even weeks or months.
--
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.
Sorry, but I must disagree with the assertion that a database
transaction unconditionally provides "... the "Edit" function to more
than one..." A transaction is not a "postmortem action"; although I have
to admit that I'm unclear on the definition of that term.
An exclusive write lock on a row helps implement the behavior desired by
the OP. Given two cooperating processes, A and B, if A holds an
exclusive write lock on one or more rows, then the DBMS will refuse any
request by B for a proper subset of those rows in an incompatible lock
mode. Such refusals provide information to the the client so that it can
then "... preclude "Edit" function being made available to more than one
OK
> But I think in
> most common ajax web applications you do one transaction per request and
> that way you can not hold a lock for a longer period of time if you use
> DBMS read/write locks.
I disagree with that assertion of the linkage between transaction count
on the client and transaction count on the server. However, this is a
GWT forum, not a DBMS forum, and I'll end the discussion here.