This is in the appengine transaction docs...
Note: If your application receives an exception when committing a transaction, it does not always mean that the transaction failed. You can receive Timeout, TransactionFailedError, or InternalError exceptions in cases where transactions have been committed and eventually will be applied successfully...
Consider the following scenario
A within a transaction.db.get(entity_a_key_goes_here) within a transaction right after, or almost at the same time as step 2.My Question:
Is it ever possible for the db.get() operation at step 3 above to return a stale value (or not the updated value set on step 1)? Are transactional db.get() operations guaranteed to return the freshest result even if the "weird" transaction exception occurs right before it?
Thanks!
I also asked this in stackoverflow, but I didn't get an answer.
Is it ever possible for the
db.get()operation at step 3 above to return a stale value (or not the updated value set on step 1)? Are transactionaldb.get()operations guaranteed to return the freshest result even if the "weird" transaction exception occurs right before it?
I also asked this in stackoverflow, but I didn't get an answer.
For now, let's ignore the transaction and exception details. Suppose you make a simple datastore put, then immediately query the datastore for that entity. There's a good chance that the entity that you just put in won't exist, because it takes time for the datastore to commit and apply the entity (fully write the entity, including all needed indexes, etc). The time to fully write an entity differs depending on how big the entity is, how many indexes are written, etc, but I usually ballpark it at around 100-200 ms. As a side note, this is why sharding exists: because on a high traffic app, a single entity simply cannot be written to fast enough to handle all the incoming writes and not lose data.Within a transaction context, the same principles apply: depending on how soon your db.get executes after the transaction Exception, you may get stale data. The key here is the Exception: it's there to warn you that the apply phase has been delayed for some internal reason. The apply may have already occurred, it may be delayed for an unknown amount of time, or it may not be valid anymore. So just to repeat: an Exception from a transaction may cause stale data.
I don't think this is correct. According to this article[1], if the transaction has been committed but not applied, then any subsequent reads, writes, or new transactions to that entity group will cause any unapplied transactions to be applied. So you should not see stale data in this case.
From what I have read on documents on 'ndb' I understand that if you are running your get() andput() both in 'transactions' (@ndb.transactional), then you will not get stale data. 'ndb' will either serve the updated data or fail both.
Transactions either fail or succeed. Also like other dbms, ndb too maintains 'journal'.
--
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-appengi...@googlegroups.com.
To post to this group, send email to google-a...@googlegroups.com.
Visit this group at http://groups.google.com/group/google-appengine?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
Repeating what I wrote on SO:From what I have read on documents on 'ndb' I understand that if you are running your
get()andput()both in 'transactions' (@ndb.transactional), then you will not get stale data. 'ndb' will either serve the updated data or fail both.
If a transaction exception occurs though (as you asked in your original post) there is no guarantee that the transaction occurred. In that case, you may receive stale data because the transaction is delayed/never occurred.
First, thanks so much for your replies.
To add clarity, this is my situation:
I'm performing a transaction operation that's not idempotent. Therefore, I need to know whether it succeeded or not, and I have to be completely certain about it (before retrying if it failed). Fortunately, I can generate an id unique for each operation. So what I do is I run the following transaction via db.transactional(xg=True):
1. db.get(the_key_of_the_transaction_marker)
2. if the the transaction marker exists, then just return. We don't have to continue because it's already been done
3. if it doesn't exist, proceed….
4. perform non idempotent modifications here…
5. create transaction marker for this transaction via db.put(). with key set to the specific marker/id for this set of modifications
I use step 1 and step 5 to ensure that the operation can never be run twice. It's ok for my operation to fail completely. However, it's completely unacceptable for it to run twice.
--
You received this message because you are subscribed to a topic in the Google Groups "Google App Engine" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/google-appengine/rLsWMWS5Acc/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to google-appengi...@googlegroups.com.
--
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-appengi...@googlegroups.com.
To post to this group, send email to google-a...@googlegroups.com.
Visit this group at http://groups.google.com/group/google-appengine?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
Thank you Helge!
Please let me clarify…
Are you saying that even if the db.get(key_of_marker) returns None in step 1, step 5 will return an exception if an entity of the same key was created in another transaction that happened between steps 2 - 4 of the original transaction?
Is the following Transaction A doomed to fail?
Transaction A
# Check Marker to see if this transaction has been done before
if db.get(db.Key().from_path('Marker', 'abcdefg')):
return
# Perform other db operations here…
# CONCURRENTLY, Transaction B happens and completes here...
# Set Marker specific to this transaction to avoid repeats
Marker(key_name='abcdefg').put()
return
Are you saying that even if the db.get(key_of_marker) returns None in step 1, step 5 will return an exception if an entity of the same key was created in another transaction that happened between steps 2 - 4 of the original transaction?
Is the following Transaction A doomed to fail?
...
I just want to be 100% certain that my transactions either completely fail or completely succeed AND NEVER EVER repeat.
Hi Helge!
Thanks so much! I tested it, and you were right about all the points. Thanks for your time! :)
To everyone who also posted in this thread, thanks so much for your time as well. You guys really helped me, and I appreciate it.
Cheers!
Albert
--