Question on Transactions

51 views
Skip to first unread message

Rajesh Gupta

unread,
Oct 12, 2016, 5:48:30 AM10/12/16
to objectify...@googlegroups.com, google-a...@googlegroups.com
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
Accounting/Inventory/Orders on Google Cloud Platform and Mobile

Nicholas Okunew

unread,
Oct 12, 2016, 7:04:37 AM10/12/16
to objectify...@googlegroups.com, google-a...@googlegroups.com
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. 



--
You received this message because you are subscribed to the Google Groups "objectify-appengine" group.
To unsubscribe from this group and stop receiving emails from it, send an email to objectify-appengine+unsub...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Rajesh Gupta

unread,
Oct 13, 2016, 3:19:30 AM10/13/16
to objectify...@googlegroups.com, google-a...@googlegroups.com
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
Accounting/Inventory/Orders on Google Cloud Platform and Mobile

To unsubscribe from this group and stop receiving emails from it, send an email to objectify-appengine+unsubscribe...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Evan Jones

unread,
Oct 16, 2016, 10:46:14 AM10/16/16
to Google App Engine, objectify...@googlegroups.com
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 42.
2B Client A request: Read work, Update work, setting property "other" to False


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 approaches:

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.

Evan
Reply all
Reply to author
Forward
0 new messages