If you need strong consistency guarantees across network requests, you are 
going to need to do a bit of extra work, since the datastore's transactions 
only work within a single request. To make this concrete:

1A Client A request: Read Work key "foo", send it to the browser.
1B Client B request: Read Work key "foo", send it to the browser

2A Client B request: Read work, Update Work setting property "something" to 
2B Client A request: Read work, Update work, setting property "other" to 

If 2A and 2B are being processed at the same time, then *one of* the 
operations will get applied, overwriting the other. That mean you will see 
"something=42" OR "other=False", while you probably want BOTH to be applied.

If you need real transactions "across" network requests (e.g. out to a 
client browser) then you will need to implement something on top of what is 
provided by the database. At a high level, I've seen people take three 

1. Optimistic: assume everything will be fine, but verify if there have 
been any updates. You can do this by keeping a "version" property that you 
increment on each update. When the client request comes back, it must 
include the version of the object it was editing. If the current version is 
not the same, you can reject the update.

2. Pessimistic: When clients start an edit, they can acquire a "lease", 
which is a lock with a time limit. You add a "locked_until" property to 
your entity. Any other client cannot make the update until this time. 
Ticketmaster and airlines use this approach when you are purchasing tickets.

3. Merge edits: In some cases, if the client request contains fine-grained 
edits, and the edits don't conflict, you might be able to merge them. This 
is much more difficult to implement though, and tends to be very 
application specific. Google docs and git use this approach.

I hope this helps! Also to be clear: this is not a datastore-specific 
issue, this happens with all data storage systems, including traditional 
relational databases.


On Thursday, October 13, 2016 at 3:19:30 AM UTC-4, Rajesh Gupta wrote:
> Dear Nicholas,
> Thank you.
> The example code shows the illustration of loading the object outside the 
> transaction block.  However, sometimes, we send the object to client, and 
> client modifies the object and sends back to server.  Here, we have to 
> directly put the object and other processed objects in the transaction 
> block.  
> Regards,
> Rajesh
> *www.VeersoftSolutions.com <http://www.veersoftsolutions.com/>*
> *www.GainERP.com <https://www.gainerp.com/>*
> *Accounting/Inventory/Orders on Google Cloud Platform and Mobile*
> On Wed, Oct 12, 2016 at 4:30 PM, Nicholas Okunew <naok...@gmail.com 
> <javascript:>> wrote:
>> Objectify requires the Work executed for a transaction to be idempotent. 
>> When a stale write occurs, the run block is executed again. 
>> In your example, this would happen if there were two concurrent writes on 
>> the entity that 'tk' points at. If you load inside the transaction, then 
>> someString will be applied on the most recent write. By loading outside the 
>> transaction you will overwrite whatever the other concurrent write put in 
>> the datastore. This is almost always bad.
>> In this example, you should definitely read inside the transaction.
>> It can get a bit hairier when you have to write in batches because of XG 
>> limits and you need, for example, reference data. At that point you're 
>> making a bit of a tradeoff between correctness, complexity and performance. 
>> On 12 October 2016 at 20:48, Rajesh Gupta <
>> rajesh...@veersoftsolutions.com <javascript:>> wrote:
>>> Hello All,
>>> Is it ok to read the object outside of the transaction block, and then 
>>> modify and save the object in the transaction block
>>> Or, should we always read and write the object with in the transaction 
>>> block for the transactions to work properly
>>>       private void prepare(Key<Trivial> tk) { final Trivial trivNew = 
>>> ofy().transactionless().load().key(tk).now(); Key<Trivial> k = 
>>> ofy().transact(new Work<Key<Trivial>>() { @Override public Key<Trivial> 
>>> run() { trivNew.setSomeString("hellofoo"); return 
>>> ofy().save().entity(trivNew).now(); } }); }
>>> -- 
>>> Regards,
>>> Rajesh
>>> *www.VeersoftSolutions.com <http://www.VeersoftSolutions.com>*
>>> *www.GainERP.com <https://www.gainerp.com>*
>>> *Accounting/Inventory/Orders on Google Cloud Platform and Mobile*

You received this message because you are subscribed to the Google Groups 
"Google App Engine" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to google-appengine+unsubscr...@googlegroups.com.
To post to this group, send email to google-appengine@googlegroups.com.
Visit this group at https://groups.google.com/group/google-appengine.
To view this discussion on the web visit 
For more options, visit https://groups.google.com/d/optout.

Reply via email to