|
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