Skip to content

Commit 41230aa

Browse files
committed
Adding system test for datastore failed transactions.
This intentionally forces data contention in a transaction (by updating the entity outside of the transaction). This is to prevent regressions like #903.
1 parent 2d017de commit 41230aa

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

system_tests/datastore.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from gcloud import datastore
2020
from gcloud.datastore import client
2121
from gcloud.environment_vars import TESTS_DATASET
22+
from gcloud.exceptions import Conflict
2223
# This assumes the command is being run via tox hence the
2324
# repository root is the current directory.
2425
from system_tests import populate_datastore
@@ -341,3 +342,45 @@ def test_transaction(self):
341342
retrieved_entity = CLIENT.get(entity.key)
342343
self.case_entities_to_delete.append(retrieved_entity)
343344
self.assertEqual(retrieved_entity, entity)
345+
346+
def test_failure_with_contention(self):
347+
CONTENTION_KEY = 'baz'
348+
349+
# Insert an entity which will be retrieved in a transaction
350+
# and updated outside it with a contentious value.
351+
key = CLIENT.key('BreakTxn', 1234)
352+
orig_entity = datastore.Entity(key=key)
353+
orig_entity['foo'] = u'bar'
354+
CLIENT.put(orig_entity)
355+
self.case_entities_to_delete.append(orig_entity)
356+
357+
# Make sure we are not yet in a transaction.
358+
self.assertEqual(CLIENT.current_transaction, None)
359+
360+
# Artificially set a transaction on the client's stack, but don't
361+
# enter the context manager (we want to make an upsert outside
362+
# of the transaction).
363+
txn = CLIENT.transaction()
364+
CLIENT._push_batch(txn)
365+
self.assertEqual(CLIENT.current_transaction, txn)
366+
# Since current_transaction == txn, this will occur transactionally.
367+
txn.begin()
368+
entity_in_txn = CLIENT.get(key)
369+
370+
# Update the original entity outside the transaction.
371+
CLIENT._pop_batch()
372+
self.assertEqual(CLIENT.current_transaction, None)
373+
orig_entity[CONTENTION_KEY] = u'outside'
374+
CLIENT.put(orig_entity)
375+
376+
# Put the transaction back in context and try to update the entity
377+
# which we already updated outside the transaction.
378+
CLIENT._push_batch(txn)
379+
self.assertEqual(CLIENT.current_transaction, txn)
380+
entity_in_txn[CONTENTION_KEY] = u'inside'
381+
txn.put(entity_in_txn)
382+
383+
with self.assertRaises(Conflict):
384+
txn.commit()
385+
386+
CLIENT._pop_batch()

0 commit comments

Comments
 (0)