Skip to content

Commit

Permalink
Merge branch 'issue_23' into transaction-management
Browse files Browse the repository at this point in the history
  • Loading branch information
dataflake committed Feb 23, 2020
2 parents bbdb64e + b6e27f4 commit 8f7ae43
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 13 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Expand Up @@ -4,6 +4,9 @@ Changelog
3.5 (unreleased)
----------------

- Fixed incompatibility with ``transaction`` version 3.
(`#23 <https://github.com/zopefoundation/Products.ZSQLMethods/pull/23>`_)


3.4 (2020-02-13)
----------------
Expand Down
28 changes: 21 additions & 7 deletions src/Shared/DC/ZRDB/THUNK.py
Expand Up @@ -11,32 +11,46 @@
#
##############################################################################

import logging

from six.moves._thread import allocate_lock

import transaction
from transaction.interfaces import IDataManager
from transaction.interfaces import TransactionFailedError
from zope.interface import implementer

from . import TM
from .TM import TM
from .TM import Surrogate


LOG = logging.getLogger('Products.ZSQLMethods')
thunk_lock = allocate_lock()


class THUNKED_TM(TM.TM):
@implementer(IDataManager)
class THUNKED_TM(TM):
"""A big heavy hammer for handling non-thread safe DAs
"""

def _register(self):
if not self._registered:
thunk_lock.acquire()

try:
transaction.get().register(Surrogate(self))
self._begin()
except Exception:
thunk_lock.release()
self.transaction_manager.get().join(Surrogate(self))
except TransactionFailedError:
LOG.error('Failed to join transaction: ', exc_info=True)
# No need to raise here, the transaction is already
# broken as a whole
except ValueError:
LOG.error('Failed to join transaction: ', exc_info=True)
# Raising here, the transaction is in an invalid state
raise
else:
self._begin()
self._registered = 1
finally:
thunk_lock.release()

def tpc_finish(self, *ignored):
if self._registered:
Expand Down
25 changes: 22 additions & 3 deletions src/Shared/DC/ZRDB/TM.py
Expand Up @@ -12,10 +12,18 @@
##############################################################################
"""Provide support for linking an external transaction manager with Zope's
"""
import logging

import transaction
from transaction.interfaces import IDataManager
from transaction.interfaces import TransactionFailedError
from zope.interface import implementer


LOG = logging.getLogger('Products.ZSQLMethods')


@implementer(IDataManager)
class TM:
"""Mix-in class that provides transaction management support
Expand All @@ -32,18 +40,29 @@ class TM:

_registered = None

def __init__(self):
# Use the default thread transaction manager.
self.transaction_manager = transaction.manager

def _begin(self):
pass

def _register(self):
if not self._registered:
try:
transaction.get().join(self)
self.transaction_manager.get().join(Surrogate(self))
except TransactionFailedError:
LOG.error('Failed to join transaction: ', exc_info=True)
# No need to raise here, the transaction is already
# broken as a whole
except ValueError:
LOG.error('Failed to join transaction: ', exc_info=True)
# Raising here, the transaction is in an invalid state
raise
else:
self._begin()
self._registered = 1
self._finalize = 0
except Exception:
pass

def tpc_begin(self, *ignored):
pass
Expand Down
61 changes: 61 additions & 0 deletions src/Shared/DC/ZRDB/tests/testTHUNK.py
@@ -0,0 +1,61 @@
##############################################################################
#
# Copyright (c) 2010 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################

from unittest import TestCase
from unittest import TestSuite
from unittest import makeSuite


class TestTM(TestCase):

def _getTargetClass(self):
from ..THUNK import THUNKED_TM
return THUNKED_TM

def _makeOne(self):
return self._getTargetClass()()

def test_conformance(self):
from transaction.interfaces import IDataManager
from zope.interface.verify import verifyObject
tm = self._makeOne()
verifyObject(IDataManager, tm)

def test__register(self):
tm = self._makeOne()
self.assertFalse(tm._registered)

tm._register()
self.assertTrue(tm._registered)

# Registering again won't break anything
tm._register()
self.assertTrue(tm._registered)

def test_sortKey(self):
tm = self._makeOne()
# the default Transaction Manager should have .sortKey() of '1' for
# backward compatibility. It must be a string according to the
# ITransactionManager interface.
self.assertEqual(tm.sortKey(), '1')

# but the sortKey() should be adjustable
tm.setSortKey('2')
self.assertEqual(tm.sortKey(), '2')

tm.setSortKey([])
self.assertEqual(tm.sortKey(), '[]')


def test_suite():
return TestSuite((makeSuite(TestTM),))
28 changes: 25 additions & 3 deletions src/Shared/DC/ZRDB/tests/testTM.py
Expand Up @@ -15,13 +15,35 @@
from unittest import TestSuite
from unittest import makeSuite

from Shared.DC.ZRDB.TM import TM


class TestTM(TestCase):

def _getTargetClass(self):
from ..TM import TM
return TM

def _makeOne(self):
return self._getTargetClass()()

def test_conformance(self):
from transaction.interfaces import IDataManager
from zope.interface.verify import verifyObject
tm = self._makeOne()
verifyObject(IDataManager, tm)

def test__register(self):
tm = self._makeOne()
self.assertFalse(tm._registered)

tm._register()
self.assertTrue(tm._registered)

# Registering again won't break anything
tm._register()
self.assertTrue(tm._registered)

def test_sortKey(self):
tm = TM()
tm = self._makeOne()
# the default Transaction Manager should have .sortKey() of '1' for
# backward compatibility. It must be a string according to the
# ITransactionManager interface.
Expand Down

0 comments on commit 8f7ae43

Please sign in to comment.