Example #1
0
 def setUp(self):
     TestCase.setUp(self)
     TestHelper.setUp(self)
     self.threadpool = FakeThreadPool()
     self.transaction = self.mocker.mock()
     self.transactor = Transactor(self.threadpool, self.transaction)
     self.function = self.mocker.mock()
Example #2
0
 def setUp(self):
     TestCase.setUp(self)
     TestHelper.setUp(self)
     self.threadpool = FakeThreadPool()
     self.transaction = self.mocker.mock()
     self.transactor = Transactor(self.threadpool, self.transaction)
     self.function = self.mocker.mock()
Example #3
0
def getService(config, reactor=None, web=True):
    if reactor is None:
        from twisted.internet import reactor

    root = service.MultiService()

    sm = ServerManager(reactor, config["servers"])
    smTrigId = reactor.addSystemEventTrigger("before", "shutdown",
                                             sm.loseConnections)

    tp = reactor.getThreadPool()
    root.updater = Updater(Transactor(tp), sm)

    updater = UpdaterService(
        int(config.get("workers", 10)),
        int(config.get("interval", 300)),
    )
    root.addService(updater)
    updater.parent = root

    if web:
        site = Site(getResource(config.get("rest", {}), root.updater))
        reactor.listenTCP(8080, site, interface="127.0.0.1")

    def _cleanup(res=None):
        sm.loseConnections()
        reactor.removeSystemEventTrigger(smTrigId)

    return root
Example #4
0
    def __init__(self):
        super(Model, self).__init__()

        if not self.database.started:
            self.database.start()

        self.transactor = Transactor(self.database.pool)
Example #5
0
    def __init__(self):
        super(Model, self).__init__()
        self.__mamba_async__ = getattr(self, '__mamba_async__', True)

        if not self.database.started:
            self.database.start()

        self.transactor = Transactor(self.database.pool)
Example #6
0
    def setUp(self):
        self.db = MockStore()
        GSM.registerUtility(MockZStorm(self.db))

        self.tp = ThreadPool(0, 2)
        self.sm = MockServerManager(reactor, SERVERS)
        self.updater = Updater(Transactor(self.tp), self.sm)
        self.tp.start()
Example #7
0
    def __init__(self, pool=None, testing=False):
        if pool is not None:
            self.pool = pool
            self.transactor = Transactor(pool)

        self.started = False
        self.__testing = testing

        if not self.zstorm_configured:
            provideUtility(global_zstorm, IZStorm)
            self._patch_sqlite_uris()
            self._set_zstorm_default_uris(getUtility(IZStorm))

        SQLite.register()
        MySQL.register()
        PostgreSQL.register()
Example #8
0
    def find(klass, *args, **kwargs):
        """Find an object in the underlying database

        Some examples:

            model.find(Customer.name == u"John")
            model.find(name=u"John")
            model.find((Customer, City), Customer.city_id == City.id)

        .. versionadded:: 0.3.6
        """

        obj = klass
        if len(args) > 0 and (type(args[0]) == tuple or type(args[0]) == list):
            obj = args[0]

        return Transactor(klass.database.pool).run(
            klass.database.store(klass.mamba_database()).find, obj, *args,
            **kwargs)
Example #9
0
    def __init__(self, pool=None, testing=False):
        if pool is not None:
            self.pool = pool
            self.transactor = Transactor(pool)

        self.started = False
        self.__testing = testing

        if not self.zstorm_configured:
            provideUtility(global_zstorm, IZStorm)
            zstorm = getUtility(IZStorm)
            if self.backend == 'sqlite' and sqlite_version_info >= (3, 6, 19):
                uri = '{}?foreign_keys=1'.format(config.Database().uri)
            else:
                uri = config.Database().uri
            zstorm.set_default_uri('mamba', uri)

        SQLite.register()
        MySQL.register()
        PostgreSQL.register()
Example #10
0
    def all(klass, order_by=None, desc=False, *args, **kwargs):
        """Return back all the rows in the database for this model

        :param order_by: order the resultset by the given field/property
        :type order_by: model property
        :param desc: if True, order the resultset by descending order
        :type desc: bool

        .. versionadded:: 0.3.6
        """
        def inner_transaction():
            store = klass.database.store(klass.mamba_database())
            data = store.find(klass)
            if order_by is not None:
                data.order_by(Desc(order_by) if desc else order_by)

            return data

        return Transactor(klass.database.pool).run(inner_transaction, *args,
                                                   **kwargs)
Example #11
0
class TransactorTest(TestCase, TestHelper):

    def is_supported(self):
        return has_transaction and has_zope_component and has_twisted

    def setUp(self):
        TestCase.setUp(self)
        TestHelper.setUp(self)
        self.threadpool = FakeThreadPool()
        self.transaction = self.mocker.mock()
        self.transactor = Transactor(self.threadpool, self.transaction)
        self.function = self.mocker.mock()

    def test_run(self):
        """
        L{Transactor.run} executes a function in a thread, commits
        the transaction and returns a deferred firing the function result.
        """
        self.mocker.order()
        self.expect(self.function(1, arg=2)).result(3)
        self.expect(self.transaction.commit())
        self.mocker.replay()
        deferred = self.transactor.run(self.function, 1, arg=2)
        deferred.addCallback(self.assertEqual, 3)
        return deferred

    def test_run_with_async_false(self):
        self.mocker.order()
        self.expect(self.function(1, arg=2)).result(3)
        self.expect(self.transaction.commit())
        self.mocker.replay()
        kwargs = {'async': False, 'arg': 2}
        result = self.transactor.run(self.function, 1, **kwargs)
        self.assertEqual(result, 3)

    def test_run_with_function_failure(self):
        """
        If the given function raises an error, then L{Transactor.run}
        aborts the transaction and re-raises the same error.
        """
        self.mocker.order()
        self.expect(self.function()).throw(ZeroDivisionError())
        self.expect(self.transaction.abort())
        self.mocker.replay()
        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, ZeroDivisionError)
        return deferred

    def test_run_with_disconnection_error(self):
        """
        If the given function raises a L{DisconnectionError}, then a C{SELECT
        1} will be executed in each registered store such that C{psycopg}
        actually detects the disconnection.
        """
        self.transactor.retries = 0
        self.mocker.order()
        zstorm = self.mocker.mock()
        store1 = self.mocker.mock()
        store2 = self.mocker.mock()
        gu = self.mocker.replace(getUtility)
        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter((("store1", store1),
                                                      ("store2", store2))))
        self.expect(store1.execute("SELECT 1"))
        self.expect(store2.execute("SELECT 1"))
        self.expect(self.transaction.abort())
        self.mocker.replay()
        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, DisconnectionError)
        return deferred

    def test_run_with_disconnection_error_in_execute_is_ignored(self):
        """
        If the given function raises a L{DisconnectionError}, then a C{SELECT
        1} will be executed in each registered store such that C{psycopg}
        actually detects the disconnection. If another L{DisconnectionError}
        happens during C{execute}, then it is ignored.
        """
        self.transactor.retries = 0
        zstorm = self.mocker.mock()
        store1 = self.mocker.mock()
        store2 = self.mocker.mock()
        gu = self.mocker.replace(getUtility)
        self.mocker.order()
        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter((("store1", store1),
                                                      ("store2", store2))))
        self.expect(store1.execute("SELECT 1")).throw(DisconnectionError())
        self.expect(store2.execute("SELECT 1"))
        self.expect(self.transaction.abort())
        self.mocker.replay()
        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, DisconnectionError)
        return deferred

    def test_run_with_auto_commit_false(self):
        """
        If the user pass a key as "auto_commit": Fale in the kwargs for
        a mamba @transact decorated method, we don't call transaction.commit
        """

        function = self.mocker.mock()
        self.mocker.order()
        self.expect(function())
        self.mocker.replay()

        kwargs = {'auto_commit': False}
        deferred = self.transactor.run(function, **kwargs)
        return deferred

    def test_run_with_auto_commit_true(self):

        function = self.mocker.mock()
        self.mocker.order()
        self.expect(function())
        self.expect(self.transaction.commit())
        self.mocker.replay()

        kwargs = {'auto_commit': True}
        deferred = self.transactor.run(function, **kwargs)
        return deferred

    def test_run_with_commit_failure(self):
        """
        If the given function succeeds but the transaction fails to commit,
        then L{Transactor.run} aborts the transaction and re-raises
        the commit exception.
        """
        self.mocker.order()
        self.expect(self.function())
        self.expect(self.transaction.commit()).throw(ZeroDivisionError())
        self.expect(self.transaction.abort())
        self.mocker.replay()
        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, ZeroDivisionError)
        return deferred

    def test_wb_default_transaction(self):
        """
        By default L{Transact} uses the global transaction manager.
        """
        transactor = Transactor(self.threadpool)
        self.assertIdentical(transaction, transactor._transaction)

    def test_decorate(self):
        """
        A L{transact} decorator can be used with methods of an object that
        contains a L{Transactor} instance as a C{transactor} instance variable,
        ensuring that the decorated function is called via L{Transactor.run}.
        """
        self.mocker.order()
        self.expect(self.transaction.commit())
        self.mocker.replay()

        @transact
        def function(self):
            """docstring"""
            return "result"

        # Function metadata is copied to the wrapper.
        self.assertEqual("docstring", function.__doc__)
        deferred = function(self)
        deferred.addCallback(self.assertEqual, "result")
        return deferred

    def test_run_with_integrity_error_retries(self):
        """
        If the given function raises a L{IntegrityError}, then the function
        will be retried another two times before letting the exception bubble
        up.
        """
        self.transactor.sleep = self.mocker.mock()
        self.transactor.uniform = self.mocker.mock()
        self.mocker.order()

        self.expect(self.function()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2 ** 1)).result(1)
        self.expect(self.transactor.sleep(1))

        self.expect(self.function()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2 ** 2)).result(2)
        self.expect(self.transactor.sleep(2))

        self.expect(self.function()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.mocker.replay()

        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, IntegrityError)
        return deferred

    def test_run_with_transaction_rollback_error_retries(self):
        """
        If the given function raises a L{TransactionRollbackError}, then the
        function will be retried another two times before letting the exception
        bubble up.
        """
        if not has_psycopg:
            return

        self.transactor.sleep = self.mocker.mock()
        self.transactor.uniform = self.mocker.mock()
        self.mocker.order()

        self.expect(self.function()).throw(TransactionRollbackError())
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2 ** 1)).result(1)
        self.expect(self.transactor.sleep(1))

        self.expect(self.function()).throw(TransactionRollbackError())
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2 ** 2)).result(2)
        self.expect(self.transactor.sleep(2))

        self.expect(self.function()).throw(TransactionRollbackError())
        self.expect(self.transaction.abort())
        self.mocker.replay()

        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, TransactionRollbackError)
        return deferred

    def test_run_with_disconnection_error_retries(self):
        """
        If the given function raises a L{DisconnectionError}, then the
        function will be retried another two times before letting the exception
        bubble up.
        """
        zstorm = self.mocker.mock()
        gu = self.mocker.replace(getUtility)
        self.transactor.sleep = self.mocker.mock()
        self.transactor.uniform = self.mocker.mock()
        self.mocker.order()

        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter(()))
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2 ** 1)).result(1)
        self.expect(self.transactor.sleep(1))

        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter(()))
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2 ** 2)).result(2)
        self.expect(self.transactor.sleep(2))

        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter(()))
        self.expect(self.transaction.abort())
        self.mocker.replay()

        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, DisconnectionError)
        return deferred

    def test_run_with_integrity_error_on_commit_retries(self):
        """
        If the given function raises a L{IntegrityError}, then the function
        will be retried another two times before letting the exception bubble
        up.
        """
        self.transactor.sleep = self.mocker.mock()
        self.transactor.uniform = self.mocker.mock()
        self.mocker.order()

        self.expect(self.function())
        self.expect(self.transaction.commit()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2 ** 1)).result(1)
        self.expect(self.transactor.sleep(1))

        self.expect(self.function())
        self.expect(self.transaction.commit()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2 ** 2)).result(2)
        self.expect(self.transactor.sleep(2))

        self.expect(self.function())
        self.expect(self.transaction.commit()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.mocker.replay()

        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, IntegrityError)
        return deferred

    def test_run_with_on_retry_callback(self):
        """
        If a retry callback is passed with the C{on_retry} parameter, then
        it's invoked with the number of retries performed so far.
        """
        calls = []

        def on_retry(context):
            calls.append(context)

        self.transactor.on_retry = on_retry

        self.transactor.sleep = self.mocker.mock()
        self.transactor.uniform = self.mocker.mock()
        self.mocker.order()

        self.expect(self.function(1, a=2))
        error = IntegrityError()
        self.expect(self.transaction.commit()).throw(error)
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2 ** 1)).result(1)
        self.expect(self.transactor.sleep(1))

        self.expect(self.function(1, a=2))
        self.expect(self.transaction.commit())
        self.mocker.replay()

        deferred = self.transactor.run(self.function, 1, a=2)

        def check(_):
            [context] = calls

            self.assertEqual(self.function, context.function)
            self.assertEqual((1,), context.args)
            self.assertEqual({"a": 2}, context.kwargs)
            self.assertEqual(1, context.retry)
            self.assertEqual(1, context.retry)
            self.assertIs(error, context.error)

        return deferred.addCallback(check)

    def test_run_with_readonly_error(self):
        """
        If the given function raises a L{ReadOnlyError}, then a C{SELECT
        1} will be executed in each registered store such that C{psycopg}
        actually detects the disconnection.
        """
        self.transactor.retries = 0
        self.mocker.order()
        zstorm = self.mocker.mock()
        store1 = self.mocker.mock()
        store2 = self.mocker.mock()
        gu = self.mocker.replace(getUtility)
        self.expect(self.function()).throw(ReadOnlyError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter((("store1", store1),
                                                      ("store2", store2))))
        self.expect(store1.execute("SELECT 1"))
        self.expect(store2.execute("SELECT 1"))
        self.expect(self.transaction.abort())
        self.mocker.replay()
        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, ReadOnlyError)
        return deferred

    def test_run_with_readonly_error_retries(self):
        """
        If the given function raises a L{ReadOnlyError}, then the
        function will be retried another two times before letting the exception
        bubble up.
        """
        zstorm = self.mocker.mock()
        gu = self.mocker.replace(getUtility)
        self.transactor.sleep = self.mocker.mock()
        self.transactor.uniform = self.mocker.mock()
        self.mocker.order()

        self.expect(self.function()).throw(ReadOnlyError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter(()))
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2 ** 1)).result(1)
        self.expect(self.transactor.sleep(1))

        self.expect(self.function()).throw(ReadOnlyError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter(()))
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2 ** 2)).result(2)
        self.expect(self.transactor.sleep(2))

        self.expect(self.function()).throw(ReadOnlyError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter(()))
        self.expect(self.transaction.abort())
        self.mocker.replay()

        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, ReadOnlyError)
        return deferred
Example #12
0
class TransactorTest(TestCase, TestHelper):
    def is_supported(self):
        return has_transaction and has_zope_component and has_twisted

    def setUp(self):
        TestCase.setUp(self)
        TestHelper.setUp(self)
        self.threadpool = FakeThreadPool()
        self.transaction = self.mocker.mock()
        self.transactor = Transactor(self.threadpool, self.transaction)
        self.function = self.mocker.mock()

    def test_run(self):
        """
        L{Transactor.run} executes a function in a thread, commits
        the transaction and returns a deferred firing the function result.
        """
        self.mocker.order()
        self.expect(self.function(1, arg=2)).result(3)
        self.expect(self.transaction.commit())
        self.mocker.replay()
        deferred = self.transactor.run(self.function, 1, arg=2)
        deferred.addCallback(self.assertEqual, 3)
        return deferred

    def test_run_with_function_failure(self):
        """
        If the given function raises an error, then L{Transactor.run}
        aborts the transaction and re-raises the same error.
        """
        self.mocker.order()
        self.expect(self.function()).throw(ZeroDivisionError())
        self.expect(self.transaction.abort())
        self.mocker.replay()
        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, ZeroDivisionError)
        return deferred

    def test_run_with_disconnection_error(self):
        """
        If the given function raises a L{DisconnectionError}, then a C{SELECT
        1} will be executed in each registered store such that C{psycopg}
        actually detects the disconnection.
        """
        self.transactor.retries = 0
        self.mocker.order()
        zstorm = self.mocker.mock()
        store1 = self.mocker.mock()
        store2 = self.mocker.mock()
        gu = self.mocker.replace(getUtility)
        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(
            iter((("store1", store1), ("store2", store2))))
        self.expect(store1.execute("SELECT 1"))
        self.expect(store2.execute("SELECT 1"))
        self.expect(self.transaction.abort())
        self.mocker.replay()
        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, DisconnectionError)
        return deferred

    def test_run_with_disconnection_error_in_execute_is_ignored(self):
        """
        If the given function raises a L{DisconnectionError}, then a C{SELECT
        1} will be executed in each registered store such that C{psycopg}
        actually detects the disconnection. If another L{DisconnectionError}
        happens during C{execute}, then it is ignored.
        """
        self.transactor.retries = 0
        zstorm = self.mocker.mock()
        store1 = self.mocker.mock()
        store2 = self.mocker.mock()
        gu = self.mocker.replace(getUtility)
        self.mocker.order()
        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(
            iter((("store1", store1), ("store2", store2))))
        self.expect(store1.execute("SELECT 1")).throw(DisconnectionError())
        self.expect(store2.execute("SELECT 1"))
        self.expect(self.transaction.abort())
        self.mocker.replay()
        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, DisconnectionError)
        return deferred

    def test_run_with_commit_failure(self):
        """
        If the given function succeeds but the transaction fails to commit,
        then L{Transactor.run} aborts the transaction and re-raises
        the commit exception.
        """
        self.mocker.order()
        self.expect(self.function())
        self.expect(self.transaction.commit()).throw(ZeroDivisionError())
        self.expect(self.transaction.abort())
        self.mocker.replay()
        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, ZeroDivisionError)
        return deferred

    def test_wb_default_transaction(self):
        """
        By default L{Transact} uses the global transaction manager.
        """
        transactor = Transactor(self.threadpool)
        self.assertIdentical(transaction, transactor._transaction)

    def test_decorate(self):
        """
        A L{transact} decorator can be used with methods of an object that
        contains a L{Transactor} instance as a C{transactor} instance variable,
        ensuring that the decorated function is called via L{Transactor.run}.
        """
        self.mocker.order()
        self.expect(self.transaction.commit())
        self.mocker.replay()

        @transact
        def function(self):
            """docstring"""
            return "result"

        # Function metadata is copied to the wrapper.
        self.assertEqual("docstring", function.__doc__)
        deferred = function(self)
        deferred.addCallback(self.assertEqual, "result")
        return deferred

    def test_run_with_integrity_error_retries(self):
        """
        If the given function raises a L{IntegrityError}, then the function
        will be retried another two times before letting the exception bubble
        up.
        """
        self.transactor.sleep = self.mocker.mock()
        self.transactor.uniform = self.mocker.mock()
        self.mocker.order()

        self.expect(self.function()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2**1)).result(1)
        self.expect(self.transactor.sleep(1))

        self.expect(self.function()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2**2)).result(2)
        self.expect(self.transactor.sleep(2))

        self.expect(self.function()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.mocker.replay()

        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, IntegrityError)
        return deferred

    def test_run_with_transaction_rollback_error_retries(self):
        """
        If the given function raises a L{TransactionRollbackError}, then the
        function will be retried another two times before letting the exception
        bubble up.
        """
        if not has_psycopg:
            return

        self.transactor.sleep = self.mocker.mock()
        self.transactor.uniform = self.mocker.mock()
        self.mocker.order()

        self.expect(self.function()).throw(TransactionRollbackError())
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2**1)).result(1)
        self.expect(self.transactor.sleep(1))

        self.expect(self.function()).throw(TransactionRollbackError())
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2**2)).result(2)
        self.expect(self.transactor.sleep(2))

        self.expect(self.function()).throw(TransactionRollbackError())
        self.expect(self.transaction.abort())
        self.mocker.replay()

        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, TransactionRollbackError)
        return deferred

    def test_run_with_disconnection_error_retries(self):
        """
        If the given function raises a L{DisconnectionError}, then the
        function will be retried another two times before letting the exception
        bubble up.
        """
        zstorm = self.mocker.mock()
        gu = self.mocker.replace(getUtility)
        self.transactor.sleep = self.mocker.mock()
        self.transactor.uniform = self.mocker.mock()
        self.mocker.order()

        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter(()))
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2**1)).result(1)
        self.expect(self.transactor.sleep(1))

        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter(()))
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2**2)).result(2)
        self.expect(self.transactor.sleep(2))

        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter(()))
        self.expect(self.transaction.abort())
        self.mocker.replay()

        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, DisconnectionError)
        return deferred

    def test_run_with_integrity_error_on_commit_retries(self):
        """
        If the given function raises a L{IntegrityError}, then the function
        will be retried another two times before letting the exception bubble
        up.
        """
        self.transactor.sleep = self.mocker.mock()
        self.transactor.uniform = self.mocker.mock()
        self.mocker.order()

        self.expect(self.function())
        self.expect(self.transaction.commit()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2**1)).result(1)
        self.expect(self.transactor.sleep(1))

        self.expect(self.function())
        self.expect(self.transaction.commit()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2**2)).result(2)
        self.expect(self.transactor.sleep(2))

        self.expect(self.function())
        self.expect(self.transaction.commit()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.mocker.replay()

        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, IntegrityError)
        return deferred

    def test_run_with_on_retry_callback(self):
        """
        If a retry callback is passed with the C{on_retry} parameter, then
        it's invoked with the number of retries performed so far.
        """
        calls = []

        def on_retry(context):
            calls.append(context)

        self.transactor.on_retry = on_retry

        self.transactor.sleep = self.mocker.mock()
        self.transactor.uniform = self.mocker.mock()
        self.mocker.order()

        self.expect(self.function(1, a=2))
        error = IntegrityError()
        self.expect(self.transaction.commit()).throw(error)
        self.expect(self.transaction.abort())
        self.expect(self.transactor.uniform(1, 2**1)).result(1)
        self.expect(self.transactor.sleep(1))

        self.expect(self.function(1, a=2))
        self.expect(self.transaction.commit())
        self.mocker.replay()

        deferred = self.transactor.run(self.function, 1, a=2)

        def check(_):
            [context] = calls

            self.assertEqual(self.function, context.function)
            self.assertEqual((1, ), context.args)
            self.assertEqual({"a": 2}, context.kwargs)
            self.assertEqual(1, context.retry)
            self.assertEqual(1, context.retry)
            self.assertIs(error, context.error)

        return deferred.addCallback(check)
Example #13
0
 def test_wb_default_transaction(self):
     """
     By default L{Transact} uses the global transaction manager.
     """
     transactor = Transactor(self.threadpool)
     self.assertIdentical(transaction, transactor._transaction)
Example #14
0
class Database(object):
    """
    Storm ORM database provider for Mamba.

    :param pool: the thrad pool for this database
    :type pool: :class:`twisted.python.threadpool.ThreadPool`
    """

    monkey_patched = False
    pool = ThreadPool(
        config.Database().min_threads,
        config.Database().max_threads,
        'DatabasePool'
    )
    zstorm_configured = False
    transactor = Transactor(pool)

    def __init__(self, pool=None, testing=False):
        if pool is not None:
            self.pool = pool
            self.transactor = Transactor(pool)

        self.started = False
        self.__testing = testing

        if not self.zstorm_configured:
            provideUtility(global_zstorm, IZStorm)
            zstorm = getUtility(IZStorm)
            if self.backend == 'sqlite' and sqlite_version_info >= (3, 6, 19):
                uri = '{}?foreign_keys=1'.format(config.Database().uri)
            else:
                uri = config.Database().uri
            zstorm.set_default_uri('mamba', uri)

        SQLite.register()
        MySQL.register()
        PostgreSQL.register()

    def start(self):
        """Starts the Database (and the threadpool)
        """

        if self.started:
            return

        if self.__testing is True:
            self.pool.start()

        self.started = True

    def stop(self):
        """Stops the Database (and the threadpool)
        """

        if not self.started:
            return

        self.started = False

    def adjust_poolsize(self, min_threads=None, max_threads=None):
        """
        Adjusts the underlying threadpool size

        :param min: minimum number of threads
        :type min: int
        :param max: maximum number of threads
        :type max: int
        """

        self.pool.adjustPoolsize(min_threads, max_threads)

    def store(self):
        """
        Returns a Store per-thread through :class:`storm.zope.zstorm.ZStorm`
        """

        if not self.started:
            self.start()

        zstorm = getUtility(IZStorm)
        return zstorm.get('mamba')

    def dump(self, model_manager, full=False):
        """
        Dumps the full database

        :param model_manager: the model manager from mamba application
        :type model_manager: :class:`~mamba.application.model.ModelManager`
        :param full: should be dumped full?
        :type full: bool
        """

        references = []
        indexes = []
        sql = [
            '--',
            '-- Mamba SQL dump {}'.format(version.short()),
            '--',
            '-- Database Backend: {}'.format(self.backend),
            '-- Host: {}\tDatabase: {}'.format(self.host, self.database)
        ]
        app = config.Application('config/application.json')
        try:
            sql += [
                '-- Application: {}'.format(app.name.decode('utf-8')),
                '-- Application Version: {}'.format(app.version),
                '-- Application Description: {}'.format(
                    app.description.encode('utf-8')
                )
            ]
        except AttributeError:
            pass

        sql += [
            '-- ---------------------------------------------------------',
            '-- Dumped on: {}'.format(datetime.datetime.now().isoformat()),
            '--'
        ]

        if self.backend == 'mysql':
            sql += [
                '-- Disable foreign key checks for table creation',
                '--',
                'SET FOREIGN_KEY_CHECKS = 0;'
            ]

        if full is False:
            sql.append('')
            for model in model_manager.get_models().values():
                if not model.get('object').on_schema():
                    continue
                if self.backend == 'postgres':
                    references.append(model.get('object').dump_references())

                if self.backend in ('postgres', 'sqlite'):
                    if model.get('object').dump_indexes():
                        indexes.append(model.get('object').dump_indexes())

                sql += [model.get('object').dump_table() + '\n']
        else:
            for model in model_manager.get_models().values():
                model_object = model.get('object')

                if not model_object.on_schema():
                    continue

                sql.append('--')
                sql.append('-- Table structure for table {}'.format(
                    model_object.__storm_table__
                ))
                sql.append('--\n')
                sql.append(model_object.dump_table())
                sql.append('--')
                sql.append('-- Dumping data for table {}'.format(
                    model_object.__storm_table__
                ))
                sql.append('--\n')
                sql.append(model_object.dump_data())

                if self.backend == 'postgres':
                    references.append(model_object.dump_references())

                if self.backend in ('postgres', 'sqlite'):
                    if model.get('object').dump_indexes():
                        indexes.append(model_object.dump_indexes())

        if self.backend == 'mysql':
            sql += [
                '--',
                '-- Enable foreign key checks',
                '--',
                'SET FOREIGN_KEY_CHECKS = 1;'
            ]

        for reference in references:
            sql.append(reference)

        for index in indexes:
            sql.append(index)

        return '\n'.join(sql)

    def reset(self, model_manager):
        """
        Delete all the data in the database and return it to primitive state

        :param model_manager: the model manager from mamba application
        :type model_manager: :class:`~mamba.application.model.ModelManager`
        """

        cfg = config.Database()
        cfg.create_table_behaviours['create_table_if_not_exists'] = False
        cfg.create_table_behaviours['drop_table'] = True

        sql = [
            model.get('object').dump_table()
            for model in model_manager.get_models().values()
            if model.get('object').on_schema() is True
        ]

        return '\n'.join(sql)

    @property
    def backend(self):
        """Return the type of backend this databse is using
        """

        return URI(config.Database().uri).scheme

    @property
    def host(self):
        """Return the hostname this database is using
        """

        return URI(config.Database().uri).host

    @property
    def database(self):
        """Return the database name we are using
        """

        return URI(config.Database().uri).database
Example #15
0
class Database(object):
    """
    Storm ORM database provider for Mamba.

    :param pool: the thrad pool for this database
    :type pool: :class:`twisted.python.threadpool.ThreadPool`
    """

    monkey_patched = False
    pool = ThreadPool(
        config.Database().min_threads,
        config.Database().max_threads,
        'DatabasePool'
    )
    zstorm_configured = False
    transactor = Transactor(pool)

    def __init__(self, pool=None, testing=False):
        if pool is not None:
            self.pool = pool
            self.transactor = Transactor(pool)

        self.started = False
        self.__testing = testing

        if not self.zstorm_configured:
            provideUtility(global_zstorm, IZStorm)
            self._patch_sqlite_uris()
            self._set_zstorm_default_uris(getUtility(IZStorm))

        SQLite.register()
        MySQL.register()
        PostgreSQL.register()

    @property
    def backend(self):
        """Return the type or types of backends this databse is using
        """

        return self._parse_uri('scheme')

    @property
    def host(self):
        """Return the hostname or hostnames this database is using
        """

        return self._parse_uri('host')

    @property
    def database(self):
        """Return the database name or names we are using
        """

        return self._parse_uri('database')

    def start(self):
        """Starts the Database (and the threadpool)
        """

        if self.started:
            return

        if self.__testing is True:
            self.pool.start()

        self.started = True

    def stop(self):
        """Stops the Database (and the threadpool)
        """

        if not self.started:
            return

        self.started = False

    def adjust_poolsize(self, min_threads=None, max_threads=None):
        """
        Adjusts the underlying threadpool size

        :param min: minimum number of threads
        :type min: int
        :param max: maximum number of threads
        :type max: int
        """

        self.pool.adjustPoolsize(min_threads, max_threads)

    def store(self, database='mamba', ensure_connect=False):
        """
        Returns a Store per-thread through :class:`storm.zope.zstorm.ZStorm`
        """

        if not self.started:
            self.start()

        if ensure_connect is True:
            self._ensure_connect()

        zstorm = getUtility(IZStorm)
        return zstorm.get(database)

    def dump(self, model_manager, scheme=None, full=False):
        """
        Dumps the full database

        :param model_manager: the model manager from mamba application
        :type model_manager: :class:`~mamba.application.model.ModelManager`
        :param scheme: dump which scheme? if None just everything
        :type scheme: str
        :param full: should be dumped full?
        :type full: bool
        """

        references = []
        indexes = []
        backend, host, database = self._parse_config_scheme(scheme)
        sql = [
            '--',
            '-- Mamba SQL dump {}'.format(version.short()),
            '--',
            '-- Database Backend: {}'.format(backend),
            '-- Host: {}\tDatabase: {}'.format(host, database)
        ]
        app = config.Application('config/application.json')
        try:
            sql += [
                '-- Application: {}'.format(app.name.decode('utf-8')),
                '-- Application Version: {}'.format(app.version),
                '-- Application Description: {}'.format(
                    app.description.encode('utf-8')
                )
            ]
        except AttributeError:
            pass

        sql += [
            '-- ---------------------------------------------------------',
            '-- Dumped on: {}'.format(datetime.datetime.now().isoformat()),
            '--'
        ]

        if self.backend == 'mysql':
            sql += [
                '-- Disable foreign key checks for table creation',
                '--',
                'SET FOREIGN_KEY_CHECKS = 0;'
            ]

        if full is False:
            self._dump_scheme(sql, references, indexes, model_manager, scheme)
        else:
            self._dump_data(sql, references, indexes, model_manager, scheme)

        if self.backend == 'mysql':
            sql += [
                '--',
                '-- Enable foreign key checks',
                '--',
                'SET FOREIGN_KEY_CHECKS = 1;'
            ]

        for reference in references:
            sql.append(reference)

        for index in indexes:
            sql.append(index)

        return '\n'.join(sql)

    def reset(self, model_manager, scheme=None):
        """
        Delete all the data in the database and return it to primitive state

        :param model_manager: the model manager from mamba application
        :type model_manager: :class:`~mamba.application.model.ModelManager`
        :param scheme: the specific scheme to reset (if any)
        :type scheme: str
        """

        sql = []
        for model in model_manager.get_models().values():
            if model.get('object').on_schema() is not True:
                continue

            if scheme is not None:
                if model.get('object').mamba_database() != scheme:
                    continue

            sql.append(
                model.get('object').drop_table(script=True, async=False))
            sql.append(model.get('object').dump_table())
Example #16
0
            del self[key]
        except KeyError, k:
            raise AttributeError, k

    def __repr__(self):
        return '<Storage ' + dict.__repr__(self) + '>'

    def __getstate__(self):
        return dict(self)

    def __setstate__(self, value):
        for (k, v) in value.items():
            self[k] = v

def randomStr(length, num=True):
    """
    Returns a random a mixed lowercase, uppercase, alfanumerical (if num True)
    string long length
    """
    chars = string.ascii_lowercase + string.ascii_uppercase
    if num:
        chars += string.digits
    return ''.join(random.choice(chars) for x in range(length))

from oonib import config

database = SQLite(URI(config.main.database_uri))
db_threadpool = ThreadPool(0, config.main.db_threadpool_size)
db_threadpool.start()
transactor = Transactor(db_threadpool)
Example #17
0
class TransactorTest(TestCase, TestHelper):

    def is_supported(self):
        return has_transaction and has_zope_component and has_twisted

    def setUp(self):
        TestCase.setUp(self)
        TestHelper.setUp(self)
        self.threadpool = FakeThreadPool()
        self.transaction = self.mocker.mock()
        self.transactor = Transactor(self.threadpool, self.transaction)
        self.function = self.mocker.mock()

    def test_run(self):
        """
        L{Transactor.run} executes a function in a thread, commits
        the transaction and returns a deferred firing the function result.
        """
        self.mocker.order()
        self.expect(self.function(1, arg=2)).result(3)
        self.expect(self.transaction.commit())
        self.mocker.replay()
        deferred = self.transactor.run(self.function, 1, arg=2)
        deferred.addCallback(self.assertEqual, 3)
        return deferred

    def test_run_with_function_failure(self):
        """
        If the given function raises an error, then L{Transactor.run}
        aborts the transaction and re-raises the same error.
        """
        self.mocker.order()
        self.expect(self.function()).throw(ZeroDivisionError())
        self.expect(self.transaction.abort())
        self.mocker.replay()
        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, ZeroDivisionError)
        return deferred

    def test_run_with_disconnection_error(self):
        """
        If the given function raises a L{DisconnectionError}, then a C{SELECT
        1} will be executed in each registered store such that C{psycopg}
        actually detects the disconnection.
        """
        self.transactor.retries = 0
        self.mocker.order()
        zstorm = self.mocker.mock()
        store1 = self.mocker.mock()
        store2 = self.mocker.mock()
        gu = self.mocker.replace(getUtility)
        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter((("store1", store1),
                                                      ("store2", store2))))
        self.expect(store1.execute("SELECT 1"))
        self.expect(store2.execute("SELECT 1"))
        self.expect(self.transaction.abort())
        self.mocker.replay()
        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, DisconnectionError)
        return deferred

    def test_run_with_disconnection_error_in_execute_is_ignored(self):
        """
        If the given function raises a L{DisconnectionError}, then a C{SELECT
        1} will be executed in each registered store such that C{psycopg}
        actually detects the disconnection. If another L{DisconnectionError}
        happens during C{execute}, then it is ignored.
        """
        self.transactor.retries = 0
        zstorm = self.mocker.mock()
        store1 = self.mocker.mock()
        store2 = self.mocker.mock()
        gu = self.mocker.replace(getUtility)
        self.mocker.order()
        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter((("store1", store1),
                                                      ("store2", store2))))
        self.expect(store1.execute("SELECT 1")).throw(DisconnectionError())
        self.expect(store2.execute("SELECT 1"))
        self.expect(self.transaction.abort())
        self.mocker.replay()
        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, DisconnectionError)
        return deferred

    def test_run_with_commit_failure(self):
        """
        If the given function succeeds but the transaction fails to commit,
        then L{Transactor.run} aborts the transaction and re-raises
        the commit exception.
        """
        self.mocker.order()
        self.expect(self.function())
        self.expect(self.transaction.commit()).throw(ZeroDivisionError())
        self.expect(self.transaction.abort())
        self.mocker.replay()
        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, ZeroDivisionError)
        return deferred

    def test_wb_default_transaction(self):
        """
        By default L{Transact} uses the global transaction manager.
        """
        transactor = Transactor(self.threadpool)
        self.assertIdentical(transaction, transactor._transaction)

    def test_decorate(self):
        """
        A L{transact} decorator can be used with methods of an object that
        contains a L{Transactor} instance as a C{transactor} instance variable,
        ensuring that the decorated function is called via L{Transactor.run}.
        """
        self.mocker.order()
        self.expect(self.transaction.commit())
        self.mocker.replay()

        @transact
        def function(self):
            """docstring"""
            return "result"

        # Function metadata is copied to the wrapper.
        self.assertEqual("docstring", function.__doc__)
        deferred = function(self)
        deferred.addCallback(self.assertEqual, "result")
        return deferred

    def test_run_with_integrity_error_retries(self):
        """
        If the given function raises a L{IntegrityError}, then the function
        will be retried another two times before letting the exception bubble
        up.
        """
        sleep = self.mocker.replace(time.sleep)
        uniform = self.mocker.replace(random).uniform
        self.mocker.order()

        self.expect(self.function()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.expect(uniform(1, 2 ** 1)).result(1)
        self.expect(sleep(1))

        self.expect(self.function()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.expect(uniform(1, 2 ** 2)).result(2)
        self.expect(sleep(2))

        self.expect(self.function()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.mocker.replay()

        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, IntegrityError)
        return deferred

    def test_run_with_transaction_rollback_error_retries(self):
        """
        If the given function raises a L{TransactionRollbackError}, then the
        function will be retried another two times before letting the exception
        bubble up.
        """
        if not has_psycopg:
            return

        sleep = self.mocker.replace(time.sleep)
        uniform = self.mocker.replace(random).uniform
        self.mocker.order()

        self.expect(self.function()).throw(TransactionRollbackError())
        self.expect(self.transaction.abort())
        self.expect(uniform(1, 2 ** 1)).result(1)
        self.expect(sleep(1))

        self.expect(self.function()).throw(TransactionRollbackError())
        self.expect(self.transaction.abort())
        self.expect(uniform(1, 2 ** 2)).result(2)
        self.expect(sleep(2))

        self.expect(self.function()).throw(TransactionRollbackError())
        self.expect(self.transaction.abort())
        self.mocker.replay()

        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, TransactionRollbackError)
        return deferred

    def test_run_with_disconnection_error_retries(self):
        """
        If the given function raises a L{DisconnectionError}, then the
        function will be retried another two times before letting the exception
        bubble up.
        """
        zstorm = self.mocker.mock()
        gu = self.mocker.replace(getUtility)
        sleep = self.mocker.replace(time.sleep)
        uniform = self.mocker.replace(random).uniform
        self.mocker.order()

        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter(()))
        self.expect(self.transaction.abort())
        self.expect(uniform(1, 2 ** 1)).result(1)
        self.expect(sleep(1))

        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter(()))
        self.expect(self.transaction.abort())
        self.expect(uniform(1, 2 ** 2)).result(2)
        self.expect(sleep(2))

        self.expect(self.function()).throw(DisconnectionError())
        self.expect(gu(IZStorm)).result(zstorm)
        self.expect(zstorm.iterstores()).result(iter(()))
        self.expect(self.transaction.abort())
        self.mocker.replay()

        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, DisconnectionError)
        return deferred

    def test_run_with_integrity_error_on_commit_retries(self):
        """
        If the given function raises a L{IntegrityError}, then the function
        will be retried another two times before letting the exception bubble
        up.
        """
        sleep = self.mocker.replace(time.sleep)
        uniform = self.mocker.replace(random).uniform
        self.mocker.order()

        self.expect(self.function())
        self.expect(self.transaction.commit()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.expect(uniform(1, 2 ** 1)).result(1)
        self.expect(sleep(1))

        self.expect(self.function())
        self.expect(self.transaction.commit()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.expect(uniform(1, 2 ** 2)).result(2)
        self.expect(sleep(2))

        self.expect(self.function())
        self.expect(self.transaction.commit()).throw(IntegrityError())
        self.expect(self.transaction.abort())
        self.mocker.replay()

        deferred = self.transactor.run(self.function)
        self.assertFailure(deferred, IntegrityError)
        return deferred