Is it possible for transactional db.get () to return an outdated result if a recent transaction raised a "special" exception?

These are appengine transaction documents ...

Note. If your application receives an exception while committing a transaction, this does not always mean that the transaction failed. You can get Timeout, TransactionFailedError, or InternalError exceptions when transactions have been committed and will eventually be successfully applied ...

Consider the following scenario

  • I am updating object A inside a transaction. Transaction operation
  • leads to the special “exception” described above, in which the transaction was committed and will eventually be applied.
  • I run db.get(entity_a_key_goes_here) in another transaction right after or almost simultaneously with step 2.
  • If the above step 3 returns None , I create this object by setting the key in entity_a_key_goes_here and db.put() it (step 3, and this step is executed in the same transaction).

My question is:

Is it possible for the transactional db.get() operation in step 3 above to return an obsolete value (or an un-updated value set in step 1)? Do db.get() transactional transactions guarantee the freshest result, even if an exception to the “strange” transaction occurs right in front of it?

+6
source share
3 answers

Get is not really "transactional"; if the transaction contains read only, then the transaction actually does nothing. Performing a read in a transaction guarantees only one thing: if the value returned by the read is no longer the most current value at the time of any write operations in the transaction, the transaction will stop and no records will be executed.

Thus, the following sequence of events is possible:

  • Run transaction 1.
  • Inside transaction 1, you read object A and returns state A1.
  • Inside transaction 1, you update object A from state A1 to state A2.
  • When transaction 1 is committed, it is not executed with one of the indicated exceptions.
  • Run transaction 2.
  • Inside transaction 2, you read object A and returns state A1.
  • Transaction 1 is applied to the data warehouse in the background, changing A from state A1 to state A2.
  • Complete transaction 2 (no commit because there was no write).

But this sequence of events is different:

  • Run transaction 1.
  • Inside transaction 1, you read object A and returns state A1.
  • Inside transaction 1, you update object A from state A1 to state A2.
  • When transaction 1 is committed, it is not executed with one of the indicated exceptions.
  • Run transaction 2.
  • Inside transaction 2, you read object A and returns state A1.
  • Inside transaction 2, you update object A from state A1 to state A3.
  • Transaction 1 is applied to the data warehouse in the background, changing A from state A1 to state A2.
  • Now transaction 2 will not be successfully completed , because the record that it is going to apply is based on an outdated version of object A. This will fail and A will remain in state A2.

So, adding a question to your step 4, you are in the second sequence of events here. It is possible that in step 3 None is returned, even if the entity exists, but it is impossible for the subsequent record to succeed in this case: the transaction is outdated and, therefore, cannot complete the transaction. The transaction will be repeated, and the second time get will return the object recorded in step 1 that you need.

So, a very short answer: yes, it can return an obsolete value, but it guarantees that if the result was obsolete, subsequent recording of this object in the same transaction will fail, so this should not actually cause a problem.

+1
source

I think the result of step 3 in this situation will be obsolete. In this case, the “freshest” result may not be the entity from step 1.

My suggestion is to use memcache when getting values ​​(returning to the db.get() call when memcache misses) and updating memcache when updating the object. Use the entity key as the memcache key. In fact, this is how ndb works automatically.

+1
source

From what I read in the "ndb" docs, I understand that if you use get() and put() as in a transaction ( @ndb.transactional ), you will not get stale data. 'ndb' will either serve updated data or not work.

Transactions either fail or succeed. Like other dbms, ndb also supports the "log".

Hope this helps.

0
source

Source: https://habr.com/ru/post/946844/


All Articles