• Kirill Smelkov's avatar
    bigfile/zodb: ZBlk._p_invalidate() can be called more than once, in particular in case of conflicts · 800c14a9
    Kirill Smelkov authored
    When there is a conflict (on any object, but on ZBlk in particular) ZODB
    machinery calls its ._p_invalidate() twice:
    
      File ".../wendelin.core/bigfile/tests/test_filezodb.py", line 661, in test_bigfile_filezodb_vs_conflicts
        tm2.commit()    # this should raise ConflictError and stay at 11 state
      File ".../transaction/_manager.py", line 111, in commit
        return self.get().commit()
      File ".../transaction/_transaction.py", line 271, in commit
        self._commitResources()
      File ".../transaction/_transaction.py", line 414, in _commitResources
        self._cleanup(L)
      File ".../transaction/_transaction.py", line 426, in _cleanup
        rm.abort(self)
      File ".../ZODB/Connection.py", line 436, in abort
        self._abort()
      File ".../ZODB/Connection.py", line 479, in _abort
        self._cache.invalidate(oid)
      File ".../wendelin.core/bigfile/file_zodb.py", line 148, in _p_invalidate
        traceback.print_stack()
    
    and
    
      File ".../wendelin.core/bigfile/tests/test_filezodb.py", line 661, in test_bigfile_filezodb_vs_conflicts
        tm2.commit()    # this should raise ConflictError and stay at 11 state
      File ".../transaction/_manager.py", line 111, in commit
        return self.get().commit()
      File ".../transaction/_transaction.py", line 271, in commit
        self._commitResources()
      File ".../transaction/_transaction.py", line 416, in _commitResources
        self._synchronizers.map(lambda s: s.afterCompletion(self))
      File ".../transaction/weakset.py", line 59, in map
        f(elt)
      File ".../transaction/_transaction.py", line 416, in <lambda>
        self._synchronizers.map(lambda s: s.afterCompletion(self))
      File ".../ZODB/Connection.py", line 831, in _storage_sync
        self._flush_invalidations()
      File ".../ZODB/Connection.py", line 539, in _flush_invalidations
        self._cache.invalidate(invalidated)
      File ".../wendelin.core/bigfile/file_zodb.py", line 148, in _p_invalidate
        traceback.print_stack()
    
    i.e. first invalidation is done by commit cleanup:
    
        https://github.com/zopefoundation/transaction/blob/1.4.4/transaction/_transaction.py#L414
        https://github.com/zopefoundation/ZODB/blob/3.10/src/ZODB/Connection.py#L479
    
    and then Connection.afterCompletion() flushes invalidation again:
    
        https://github.com/zopefoundation/transaction/blob/1.4.4/transaction/_transaction.py#L416
        https://github.com/zopefoundation/ZODB/blob/3.10/src/ZODB/Connection.py#L833
        https://github.com/zopefoundation/ZODB/blob/3.10/src/ZODB/Connection.py#L539
    
    If there was no conflict - there will be no ConflictError raised and
    thus no Transaction._cleanup() done in its ._commitResources() ->
    invalidation called only once. But with ConflictError - it is twice.
    
    Adjust ZBlk._p_invalidate() not to delve into real invalidation more
    than once - else we will fail, as ZBlk._v_zfile becomes unbound after
    invalidation done the first time.
    800c14a9
test_filezodb.py 18.5 KB