|
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,45 @@ 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 | + |
| 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