|
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