1. 18 Aug, 2022 1 commit
  2. 15 Aug, 2022 5 commits
    • Kirill Smelkov's avatar
      racetest: Add test for disconnect / invalidation race · fc20f3aa
      Kirill Smelkov authored
      This currently fails for ZEO (see Bug2 in
      https://github.com/zopefoundation/ZEO/issues/209 for details):
      
          (z-dev) kirr@deca:~/src/wendelin/z/ZEO5$ ZEO_MTACCEPTOR=1 zope-testrunner -fvvvx --test-path=src -t check_race_external_invalidate_vs_disconnect
          /home/kirr/src/wendelin/venv/z-dev/bin/zope-testrunner traceio=True
          /home/kirr/src/wendelin/z/ZEO5/src/ZEO/StorageServer.py:51: DeprecationWarning: The mtacceptor module is deprecated and will be removed in ZEO version 6.
            'in ZEO version 6.', DeprecationWarning)
          Running tests at level 1
          Running .BlobAdaptedFileStorageTests tests:
            Set up .BlobAdaptedFileStorageTests in 0.000 seconds.
            Running:
           check_race_external_invalidate_vs_disconnect (ZEO.tests.testZEO.BlobAdaptedFileStorageTests) (1.889 s)
      
          Failure in test check_race_external_invalidate_vs_disconnect (ZEO.tests.testZEO.BlobAdaptedFileStorageTests)
          Traceback (most recent call last):
            File "/usr/lib/python2.7/unittest/case.py", line 329, in run
              testMethod()
            File "/home/kirr/src/wendelin/z/ZODB/src/ZODB/tests/racetest.py", line 357, in check_race_external_invalidate_vs_disconnect
              T2ObjectsInc2Phase())
            File "/home/kirr/src/wendelin/z/ZODB/src/ZODB/tests/util.py", line 400, in _
              return f(*argv, **kw)
            File "/home/kirr/src/wendelin/z/ZODB/src/ZODB/tests/racetest.py", line 446, in _check_race_xxx_vs_external_disconnect
              self.fail('\n\n'.join([_ for _ in failure if _]))
            File "/usr/lib/python2.7/unittest/case.py", line 410, in fail
              raise self.failureException(msg)
          AssertionError: T15: obj1 (6) - obj2(4) != phase (1)
          obj1._p_serial: 0x03ea4cc505486777  obj2._p_serial: 0x03ea4cc503413799  phase._p_serial: 0x03ea4cc505486777
          zconn_at: 0x03ea4cc505486777  # approximated as max(serials)
          zstor.loadBefore(obj1, @zconn.at)       ->  serial: 0x03ea4cc505486777  next_serial: None
          zstor.loadBefore(obj2, @zconn.at)       ->  serial: 0x03ea4cc504a74099  next_serial: None
          zstor.loadBefore(phase, @zconn.at)      ->  serial: 0x03ea4cc505486777  next_serial: None
          zstor._cache.clear()
          zstor.loadBefore(obj1, @zconn.at)       ->  serial: 0x03ea4cc505486777  next_serial: None
          zstor.loadBefore(obj2, @zconn.at)       ->  serial: 0x03ea4cc504a74099  next_serial: 0x03ea4cc506104155
          zstor.loadBefore(phase, @zconn.at)      ->  serial: 0x03ea4cc505486777  next_serial: 0x03ea4cc506104155
      
          T51: obj1 (6) - obj2(4) != phase (1)
          obj1._p_serial: 0x03ea4cc505486777  obj2._p_serial: 0x03ea4cc503413799  phase._p_serial: 0x03ea4cc505486777
          zconn_at: 0x03ea4cc505486777  # approximated as max(serials)
          zstor.loadBefore(obj1, @zconn.at)       ->  serial: 0x03ea4cc505486777  next_serial: None
          zstor.loadBefore(obj2, @zconn.at)       ->  serial: 0x03ea4cc503413799  next_serial: None
          zstor.loadBefore(phase, @zconn.at)      ->  serial: 0x03ea4cc505486777  next_serial: None
          zstor._cache.clear()
          zstor.loadBefore(obj1, @zconn.at)       ->  serial: 0x03ea4cc505486777  next_serial: None
          zstor.loadBefore(obj2, @zconn.at)       ->  serial: 0x03ea4cc504a74099  next_serial: None
          zstor.loadBefore(phase, @zconn.at)      ->  serial: 0x03ea4cc505486777  next_serial: None
      fc20f3aa
    • Kirill Smelkov's avatar
      racetest: Factor-out functionality to open another connection to the storage to common function · fa844159
      Kirill Smelkov authored
      We will need it in soon for the next test.
      fa844159
    • Kirill Smelkov's avatar
      racetest: Show relevant details about storage state on failure · c593c7d5
      Kirill Smelkov authored
      Suggested by @d-maurer.
      
      Example failure before this patch:
      
          Failure in test check_race_load_vs_external_invalidate (ZEO.tests.testZEO.BlobAdaptedFileStorageTests)
          Traceback (most recent call last):
            File "/usr/lib/python2.7/unittest/case.py", line 329, in run
              testMethod()
            File "/home/kirr/src/wendelin/z/ZODB/src/ZODB/tests/racetest.py", line 200, in check_race_load_vs_external_invalidate
              return self._check_race_load_vs_external_invalidate(T2ObjectsInc())
            File "/home/kirr/src/wendelin/z/ZODB/src/ZODB/tests/util.py", line 400, in _
              return f(*argv, **kw)
            File "/home/kirr/src/wendelin/z/ZODB/src/ZODB/tests/racetest.py", line 304, in _check_race_load_vs_external_invalidate
              self.fail([_ for _ in failure if _])
            File "/usr/lib/python2.7/unittest/case.py", line 410, in fail
              raise self.failureException(msg)
          AssertionError: ['T3: obj1 (1)  !=  obj2 (24)\n', 'T4: obj1 (1)  !=  obj2 (24)\n', 'T5: obj1 (1)  !=  obj2 (24)\n', 'T6: obj1 (1)  !=  obj2 (24)\n', 'T7: obj1 (2)  !=  obj2 (24)\n']
      
      Example failure after this patch:
      
          Failure in test check_race_load_vs_external_invalidate (ZEO.tests.testZEO.BlobAdaptedFileStorageTests)
          Traceback (most recent call last):
            File "/usr/lib/python2.7/unittest/case.py", line 329, in run
              testMethod()
            File "/home/kirr/src/wendelin/z/ZODB/src/ZODB/tests/racetest.py", line 202, in check_race_load_vs_external_invalidate
              return self._check_race_load_vs_external_invalidate(T2ObjectsInc())
            File "/home/kirr/src/wendelin/z/ZODB/src/ZODB/tests/util.py", line 400, in _
              return f(*argv, **kw)
            File "/home/kirr/src/wendelin/z/ZODB/src/ZODB/tests/racetest.py", line 307, in _check_race_load_vs_external_invalidate
              self.fail('\n\n'.join([_ for _ in failure if _]))
            File "/usr/lib/python2.7/unittest/case.py", line 410, in fail
              raise self.failureException(msg)
          AssertionError: T0: obj1 (1)  !=  obj2 (25)
          obj1._p_serial: 0x03ea4be4ea558066  obj2._p_serial: 0x03ea4be4eaac7077
          zconn_at: 0x03ea4be4eaac7077  # approximated as max(serials)
          zstor.loadBefore(obj1, @zconn.at)       ->  serial: 0x03ea4be4ea558066  next_serial: None
          zstor.loadBefore(obj2, @zconn.at)       ->  serial: 0x03ea4be4eaac7077  next_serial: None
          zstor._cache.clear()
          zstor.loadBefore(obj1, @zconn.at)       ->  serial: 0x03ea4be4eaac7077  next_serial: None
          zstor.loadBefore(obj2, @zconn.at)       ->  serial: 0x03ea4be4eaac7077  next_serial: None
      
          T2: obj1 (1)  !=  obj2 (25)
          obj1._p_serial: 0x03ea4be4ea558066  obj2._p_serial: 0x03ea4be4eaac7077
          zconn_at: 0x03ea4be4eaac7077  # approximated as max(serials)
          zstor.loadBefore(obj1, @zconn.at)       ->  serial: 0x03ea4be4ea558066  next_serial: None
          zstor.loadBefore(obj2, @zconn.at)       ->  serial: 0x03ea4be4eaac7077  next_serial: None
          zstor._cache.clear()
          zstor.loadBefore(obj1, @zconn.at)       ->  serial: 0x03ea4be4eaac7077  next_serial: None
          zstor.loadBefore(obj2, @zconn.at)       ->  serial: 0x03ea4be4eaac7077  next_serial: None
      
          T3: obj1 (26)  !=  obj2 (25)
          obj1._p_serial: 0x03ea4be4ec6af5cc  obj2._p_serial: 0x03ea4be4eaac7077
          zconn_at: 0x03ea4be4ec6af5cc  # approximated as max(serials)
          zstor.loadBefore(obj1, @zconn.at)       ->  serial: 0x03ea4be4ec6af5cc  next_serial: None
          zstor.loadBefore(obj2, @zconn.at)       ->  serial: 0x03ea4be4eaac7077  next_serial: 0x03ea4be4ec962b00
          zstor._cache.clear()
          zstor.loadBefore(obj1, @zconn.at)       ->  serial: 0x03ea4be4ec6af5cc  next_serial: 0x03ea4be4ec962b00
          zstor.loadBefore(obj2, @zconn.at)       ->  serial: 0x03ea4be4ec6af5cc  next_serial: 0x03ea4be4ec962b00
      c593c7d5
    • Kirill Smelkov's avatar
      racetest: Refactor tests to work via combining testing model with checkers · a03c4778
      Kirill Smelkov authored
      Introduce the notion of testing model to represent initial state,
      eveolution step and maintained invariant in the database.
      
      Refactor tests to accept arbitrary such model and to drive them through
      particular testing scenarious that try to hit specific concurrency
      issues.
      
      Not only this deduplicates the code a bit, but it also will allow us to
      run different models through existing and future checkers. Soon we will
      need to run a model with 3 variables (obj1, obj2 and phase) instead of
      only 2 (obj1 and obj2) for invalidate / disconnect test.
      
      Please see added module-level documentation for overview.
      a03c4778
    • Kirill Smelkov's avatar
      tests: Move tests that catch data corruption due to concurrency bugs to racetest.py · 942099c6
      Kirill Smelkov authored
      We are going to add more such tests and related infrastructure. As the
      preparatory step take tests added in
      
      - 5b4dd5f7 (tests: Add test for open vs invalidation race), and
      - e923c9a8 (tests: Add test for load vs external invalidation race)
      
      and move them to dedicated module.
      
      No functional changes, only plain code movement.
      942099c6
  3. 06 Jul, 2022 1 commit
    • Tim Gates's avatar
      docs: Fix a few typos · 0cdbebb6
      Tim Gates authored
      There are small typos in:
      - docs/articles/ZODB2.rst
      - src/ZODB/Connection.py
      - src/ZODB/FileStorage/fsdump.py
      - src/ZODB/cross-database-references.rst
      - src/ZODB/tests/testblob.py
      
      Fixes:
      - Should read `committed` rather than `commited`.
      - Should read `resetting` rather than `reseting`.
      - Should read `debugging` rather than `debuggin`.
      - Should read `constructor` rather than `contructor`.
      0cdbebb6
  4. 12 Apr, 2022 1 commit
  5. 11 Apr, 2022 2 commits
  6. 18 Mar, 2022 1 commit
  7. 17 Mar, 2022 6 commits
  8. 09 Nov, 2021 2 commits
  9. 05 Nov, 2021 2 commits
  10. 29 Oct, 2021 2 commits
  11. 28 Oct, 2021 1 commit
  12. 27 Oct, 2021 1 commit
  13. 06 Oct, 2021 1 commit
  14. 05 Oct, 2021 3 commits
  15. 03 Oct, 2021 1 commit
  16. 03 May, 2021 1 commit
  17. 23 Apr, 2021 4 commits
  18. 21 Apr, 2021 3 commits
    • Kirill Smelkov's avatar
    • Kirill Smelkov's avatar
      tests: Add test for load vs external invalidation race · e923c9a8
      Kirill Smelkov authored
      For ZEO this data corruption bug was reported at
      https://github.com/zopefoundation/ZEO/issues/155 and fixed at
      https://github.com/zopefoundation/ZEO/pull/169.
      
      Without that fix the failure shows e.g. as follows when running ZEO test
      suite:
      
          Failure in test check_race_load_vs_external_invalidate (ZEO.tests.testZEO.BlobAdaptedFileStorageTests)
          Traceback (most recent call last):
            File "/usr/lib/python2.7/unittest/case.py", line 329, in run
              testMethod()
            File "/home/kirr/src/wendelin/z/ZODB/src/ZODB/tests/BasicStorage.py", line 621, in check_race_load_vs_external_invalidate
              self.fail([_ for _ in failure if _])
            File "/usr/lib/python2.7/unittest/case.py", line 410, in fail
              raise self.failureException(msg)
          AssertionError: ['T1: obj1.value (7)  !=  obj2.value (8)']
      
      Even if added test is somewhat similar to
      check_race_loadopen_vs_local_invalidate, it is added anew without trying
      to unify code. The reason here is that the probability to catch load vs
      external invalidation race is significantly reduced when there are only
      1 modify and 1 verify workers. The unification with preserving both
      tests semantic would make test for "load vs local invalidate" harder to
      follow. Sometimes a little copying is better than trying to unify too
      much.
      
      For the test to work, test infrastructure is amended with
      ._new_storage_client() method that complements ._storage attribute:
      client-server storages like ZEO, NEO and RelStorage allow several
      storage clients to be connected to single storage server. For
      client-server storages test subclasses should implement
      _new_storage_client to return new storage client that is connected to
      the same storage server self._storage is connected to.
      
      For ZEO ._new_storage_client() is added by https://github.com/zopefoundation/ZEO/pull/170
      
      Other client-server storages can follow to implement ._new_storage_client()
      and this way automatically activate this "load vs external invalidation"
      test when their testsuite is run.
      
      Contrary to test for "load vs local invalidate" N is set to lower value (100),
      because with 8 workers the bug is usually reproduced at not-so-high iteration
      number (5-10-20).
      
      /cc @d-maurer, @jamadden, @jmuchemb
      /reviewed-on https://github.com/zopefoundation/ZODB/pull/345
      e923c9a8
    • Kirill Smelkov's avatar
      tests: Add test for open vs invalidation race · 5b4dd5f7
      Kirill Smelkov authored
      Add test that exercises open vs invalidation race condition that, if
      happen, leads to data corruption. We are seeing such race happening on
      storage level in ZEO (https://github.com/zopefoundation/ZEO/issues/166),
      and previously we've seen it also to happen on Connection level
      (https://github.com/zopefoundation/ZODB/issues/290). By adding this test
      to be exercised wrt all storages we make sure that all storages stay
      free from this race.
      
      And it payed out. Besides catching original problems from
      https://github.com/zopefoundation/ZODB/issues/290 and
      https://github.com/zopefoundation/ZEO/issues/166 , this test also
      discovered a concurrency bug in MVCCMappingStorage:
      
          Failure in test check_race_open_vs_invalidate (ZODB.tests.testMVCCMappingStorage.MVCCMappingStorageTests)
          Traceback (most recent call last):
            File "/usr/lib/python2.7/unittest/case.py", line 329, in run
              testMethod()
            File "/home/kirr/src/wendelin/z/ZODB/src/ZODB/tests/BasicStorage.py", line 492, in check_race_open_vs_invalidate
              self.fail(failure[0])
            File "/usr/lib/python2.7/unittest/case.py", line 410, in fail
              raise self.failureException(msg)
          AssertionError: T1: obj1.value (24)  !=  obj2.value (23)
      
      The problem with MVCCMappingStorage was that instance.poll_invalidations
      was correctly taking main_lock with intention to make sure main data is
      not mutated during analysis, but instance.tpc_finish and
      instance.tpc_abort did _not_ taken main lock, which was leading to
      committed data to be propagating into main storage in non-atomic way.
      
      This bug was also observable if both obj1 and obj2 in the added test
      were always loaded from the storage (added obj2._p_invalidate after
      obj1._p_invalidate).
      
      -> Fix MVCCMappingStorage by correctly locking main MVCCMappingStorage
      instance when processing transaction completion.
      
      /cc @d-maurer, @jamadden, @jmuchemb
      /reviewed-on https://github.com/zopefoundation/ZODB/pull/345
      5b4dd5f7
  19. 20 Apr, 2021 2 commits