|
13 | 13 | ##############################################################################
|
14 | 14 | from persistent import Persistent
|
15 | 15 | from persistent.mapping import PersistentMapping
|
16 |
| -from ZODB.POSException import ReadConflictError |
17 | 16 | from ZODB.POSException import TransactionFailedError
|
18 | 17 |
|
19 | 18 | import doctest
|
@@ -445,156 +444,6 @@ def checkMultipleUndoInOneTransaction(self):
|
445 | 444 | transaction.abort()
|
446 | 445 | conn.close()
|
447 | 446 |
|
448 |
| -class ReadConflictTests(ZODB.tests.util.TestCase): |
449 |
| - |
450 |
| - def setUp(self): |
451 |
| - ZODB.tests.utils.TestCase.setUp(self) |
452 |
| - self._storage = ZODB.MappingStorage.MappingStorage() |
453 |
| - |
454 |
| - def readConflict(self, shouldFail=True): |
455 |
| - # Two transactions run concurrently. Each reads some object, |
456 |
| - # then one commits and the other tries to read an object |
457 |
| - # modified by the first. This read should fail with a conflict |
458 |
| - # error because the object state read is not necessarily |
459 |
| - # consistent with the objects read earlier in the transaction. |
460 |
| - |
461 |
| - tm1 = transaction.TransactionManager() |
462 |
| - conn = self._db.open(transaction_manager=tm1) |
463 |
| - r1 = conn.root() |
464 |
| - r1["p"] = self.obj |
465 |
| - self.obj.child1 = P() |
466 |
| - tm1.get().commit() |
467 |
| - |
468 |
| - # start a new transaction with a new connection |
469 |
| - tm2 = transaction.TransactionManager() |
470 |
| - cn2 = self._db.open(transaction_manager=tm2) |
471 |
| - # start a new transaction with the other connection |
472 |
| - r2 = cn2.root() |
473 |
| - |
474 |
| - self.assertEqual(r1._p_serial, r2._p_serial) |
475 |
| - |
476 |
| - self.obj.child2 = P() |
477 |
| - tm1.get().commit() |
478 |
| - |
479 |
| - # resume the transaction using cn2 |
480 |
| - obj = r2["p"] |
481 |
| - # An attempt to access obj should fail, because r2 was read |
482 |
| - # earlier in the transaction and obj was modified by the othe |
483 |
| - # transaction. |
484 |
| - if shouldFail: |
485 |
| - self.assertRaises(ReadConflictError, lambda: obj.child1) |
486 |
| - # And since ReadConflictError was raised, attempting to commit |
487 |
| - # the transaction should re-raise it. checkNotIndependent() |
488 |
| - # failed this part of the test for a long time. |
489 |
| - self.assertRaises(ReadConflictError, tm2.get().commit) |
490 |
| - |
491 |
| - # And since that commit failed, trying to commit again should |
492 |
| - # fail again. |
493 |
| - self.assertRaises(TransactionFailedError, tm2.get().commit) |
494 |
| - # And again. |
495 |
| - self.assertRaises(TransactionFailedError, tm2.get().commit) |
496 |
| - # Etc. |
497 |
| - self.assertRaises(TransactionFailedError, tm2.get().commit) |
498 |
| - |
499 |
| - else: |
500 |
| - # make sure that accessing the object succeeds |
501 |
| - obj.child1 |
502 |
| - tm2.get().abort() |
503 |
| - |
504 |
| - |
505 |
| - def checkReadConflict(self): |
506 |
| - self.obj = P() |
507 |
| - self.readConflict() |
508 |
| - |
509 |
| - def checkReadConflictIgnored(self): |
510 |
| - # Test that an application that catches a read conflict and |
511 |
| - # continues can not commit the transaction later. |
512 |
| - root = self._db.open().root() |
513 |
| - root["real_data"] = real_data = PersistentMapping() |
514 |
| - root["index"] = index = PersistentMapping() |
515 |
| - |
516 |
| - real_data["a"] = PersistentMapping({"indexed_value": 0}) |
517 |
| - real_data["b"] = PersistentMapping({"indexed_value": 1}) |
518 |
| - index[1] = PersistentMapping({"b": 1}) |
519 |
| - index[0] = PersistentMapping({"a": 1}) |
520 |
| - transaction.commit() |
521 |
| - |
522 |
| - # load some objects from one connection |
523 |
| - tm = transaction.TransactionManager() |
524 |
| - cn2 = self._db.open(transaction_manager=tm) |
525 |
| - r2 = cn2.root() |
526 |
| - real_data2 = r2["real_data"] |
527 |
| - index2 = r2["index"] |
528 |
| - |
529 |
| - real_data["b"]["indexed_value"] = 0 |
530 |
| - del index[1]["b"] |
531 |
| - index[0]["b"] = 1 |
532 |
| - transaction.commit() |
533 |
| - |
534 |
| - del real_data2["a"] |
535 |
| - try: |
536 |
| - del index2[0]["a"] |
537 |
| - except ReadConflictError: |
538 |
| - # This is the crux of the text. Ignore the error. |
539 |
| - pass |
540 |
| - else: |
541 |
| - self.fail("No conflict occurred") |
542 |
| - |
543 |
| - # real_data2 still ready to commit |
544 |
| - self.assertTrue(real_data2._p_changed) |
545 |
| - |
546 |
| - # index2 values not ready to commit |
547 |
| - self.assertTrue(not index2._p_changed) |
548 |
| - self.assertTrue(not index2[0]._p_changed) |
549 |
| - self.assertTrue(not index2[1]._p_changed) |
550 |
| - |
551 |
| - self.assertRaises(ReadConflictError, tm.get().commit) |
552 |
| - self.assertRaises(TransactionFailedError, tm.get().commit) |
553 |
| - tm.get().abort() |
554 |
| - |
555 |
| - def checkReadConflictErrorClearedDuringAbort(self): |
556 |
| - # When a transaction is aborted, the "memory" of which |
557 |
| - # objects were the cause of a ReadConflictError during |
558 |
| - # that transaction should be cleared. |
559 |
| - root = self._db.open().root() |
560 |
| - data = PersistentMapping({'d': 1}) |
561 |
| - root["data"] = data |
562 |
| - transaction.commit() |
563 |
| - |
564 |
| - # Provoke a ReadConflictError. |
565 |
| - tm2 = transaction.TransactionManager() |
566 |
| - cn2 = self._db.open(transaction_manager=tm2) |
567 |
| - r2 = cn2.root() |
568 |
| - data2 = r2["data"] |
569 |
| - |
570 |
| - data['d'] = 2 |
571 |
| - transaction.commit() |
572 |
| - |
573 |
| - try: |
574 |
| - data2['d'] = 3 |
575 |
| - except ReadConflictError: |
576 |
| - pass |
577 |
| - else: |
578 |
| - self.fail("No conflict occurred") |
579 |
| - |
580 |
| - # Explicitly abort cn2's transaction. |
581 |
| - tm2.get().abort() |
582 |
| - |
583 |
| - # cn2 should retain no memory of the read conflict after an abort(), |
584 |
| - # but 3.2.3 had a bug wherein it did. |
585 |
| - data_conflicts = data._p_jar._conflicts |
586 |
| - data2_conflicts = data2._p_jar._conflicts |
587 |
| - self.assertFalse(data_conflicts) |
588 |
| - self.assertFalse(data2_conflicts) # this used to fail |
589 |
| - |
590 |
| - # And because of that, we still couldn't commit a change to data2['d'] |
591 |
| - # in the new transaction. |
592 |
| - cn2.sync() # process the invalidation for data2['d'] |
593 |
| - data2['d'] = 3 |
594 |
| - tm2.get().commit() # 3.2.3 used to raise ReadConflictError |
595 |
| - |
596 |
| - cn2.close() |
597 |
| - |
598 | 447 | class PoisonedError(Exception):
|
599 | 448 | pass
|
600 | 449 |
|
|
0 commit comments