| 
19 | 19 | from gcloud import datastore  | 
20 | 20 | from gcloud.datastore import client  | 
21 | 21 | from gcloud.environment_vars import TESTS_DATASET  | 
 | 22 | +from gcloud.exceptions import Conflict  | 
22 | 23 | # This assumes the command is being run via tox hence the  | 
23 | 24 | # repository root is the current directory.  | 
24 | 25 | from system_tests import populate_datastore  | 
@@ -341,3 +342,31 @@ def test_transaction(self):  | 
341 | 342 |  retrieved_entity = CLIENT.get(entity.key)  | 
342 | 343 |  self.case_entities_to_delete.append(retrieved_entity)  | 
343 | 344 |  self.assertEqual(retrieved_entity, entity)  | 
 | 345 | + | 
 | 346 | + def test_failure_with_contention(self):  | 
 | 347 | + contention_key = 'baz'  | 
 | 348 | + # Fool the Client constructor to avoid creating a new connection.  | 
 | 349 | + local_client = datastore.Client(dataset_id=CLIENT.dataset_id,  | 
 | 350 | + http=object())  | 
 | 351 | + local_client.connection = CLIENT.connection  | 
 | 352 | + | 
 | 353 | + # Insert an entity which will be retrieved in a transaction  | 
 | 354 | + # and updated outside it with a contentious value.  | 
 | 355 | + key = local_client.key('BreakTxn', 1234)  | 
 | 356 | + orig_entity = datastore.Entity(key=key)  | 
 | 357 | + orig_entity['foo'] = u'bar'  | 
 | 358 | + local_client.put(orig_entity)  | 
 | 359 | + self.case_entities_to_delete.append(orig_entity)  | 
 | 360 | + | 
 | 361 | + with self.assertRaises(Conflict):  | 
 | 362 | + with local_client.transaction() as txn:  | 
 | 363 | + entity_in_txn = local_client.get(key)  | 
 | 364 | + | 
 | 365 | + # Update the original entity outside the transaction.  | 
 | 366 | + orig_entity[contention_key] = u'outside'  | 
 | 367 | + CLIENT.put(orig_entity)  | 
 | 368 | + | 
 | 369 | + # Try to update the entity which we already updated outside the  | 
 | 370 | + # transaction.  | 
 | 371 | + entity_in_txn[contention_key] = u'inside'  | 
 | 372 | + txn.put(entity_in_txn)  | 
0 commit comments