示例#1
0
def new_revision(
    session: Session,
    tm: TransactionManager,
    content: Content,
    force_create_new_revision: bool = False,
) -> Content:
    """
    Prepare context to update a Content. It will add a new updatable revision
    to the content.
    :param session: Database _session
    :param tm: TransactionManager
    :param content: Content instance to update
    :param force_create_new_revision: Decide if new_rev should or should not
    be forced.
    :return:
    """
    with session.no_autoflush:
        try:
            if force_create_new_revision \
                    or inspect(content.revision).has_identity:
                content.new_revision()
            RevisionsIntegrity.add_to_updatable(content.revision)
            yield content
        except Exception as e:
            # INFO - GM - 14-11-2018 - rollback session and renew
            # transaction when error happened
            # This avoid bad _session data like new "temporary" revision
            # to be add when problem happen.
            session.rollback()
            tm.abort()
            tm.begin()
            raise e
        finally:
            RevisionsIntegrity.remove_from_updatable(content.revision)
    def test_explicit_mode(self):
        from .. import TransactionManager
        from ..interfaces import AlreadyInTransaction, NoTransaction

        tm = TransactionManager()
        self.assertFalse(tm.explicit)

        tm = TransactionManager(explicit=True)
        self.assertTrue(tm.explicit)
        for name in 'get', 'commit', 'abort', 'doom', 'isDoomed', 'savepoint':
            with self.assertRaises(NoTransaction):
                getattr(tm, name)()

        t = tm.begin()
        with self.assertRaises(AlreadyInTransaction):
            tm.begin()

        self.assertTrue(t is tm.get())

        self.assertFalse(tm.isDoomed())
        tm.doom()
        self.assertTrue(tm.isDoomed())
        tm.abort()

        for name in 'get', 'commit', 'abort', 'doom', 'isDoomed', 'savepoint':
            with self.assertRaises(NoTransaction):
                getattr(tm, name)()

        t = tm.begin()
        self.assertFalse(tm.isDoomed())
        with self.assertRaises(AlreadyInTransaction):
            tm.begin()
        tm.savepoint()
        tm.commit()
示例#3
0
    def test_explicit_mode(self):
        from .. import TransactionManager
        from ..interfaces import AlreadyInTransaction, NoTransaction

        tm = TransactionManager()
        self.assertFalse(tm.explicit)

        tm = TransactionManager(explicit=True)
        self.assertTrue(tm.explicit)
        for name in 'get', 'commit', 'abort', 'doom', 'isDoomed', 'savepoint':
            with self.assertRaises(NoTransaction):
                getattr(tm, name)()

        t = tm.begin()
        with self.assertRaises(AlreadyInTransaction):
            tm.begin()

        self.assertTrue(t is tm.get())

        self.assertFalse(tm.isDoomed())
        tm.doom()
        self.assertTrue(tm.isDoomed())
        tm.abort()

        for name in 'get', 'commit', 'abort', 'doom', 'isDoomed', 'savepoint':
            with self.assertRaises(NoTransaction):
                getattr(tm, name)()

        t = tm.begin()
        self.assertFalse(tm.isDoomed())
        with self.assertRaises(AlreadyInTransaction):
            tm.begin()
        tm.savepoint()
        tm.commit()
示例#4
0
def test_bigfile_filezodb_vs_conflicts():
    root = dbopen()
    conn = root._p_jar
    db   = conn.db()
    conn.close()
    del root, conn

    tm1 = TransactionManager()
    tm2 = TransactionManager()

    conn1 = db.open(transaction_manager=tm1)
    root1 = conn1.root()

    # setup zfile with fileh view to it
    root1['zfile3a'] = f1 = ZBigFile(blksize)
    tm1.commit()

    fh1 = f1.fileh_open()
    tm1.commit()

    # set zfile initial data
    vma1 = fh1.mmap(0, 1)
    Blk(vma1, 0)[0] = 1
    tm1.commit()

    # read zfile and setup fileh for it in conn2
    conn2 = db.open(transaction_manager=tm2)
    root2 = conn2.root()

    f2 = root2['zfile3a']
    fh2 = f2.fileh_open()
    vma2 = fh2.mmap(0, 1)

    assert Blk(vma2, 0)[0] == 1 # read data in conn2 + make sure read correctly

    # now zfile content is both in ZODB.Connection cache and in _ZBigFileH
    # cache for each conn1 and conn2. Modify data in both conn1 and conn2 and
    # see how it goes.

    Blk(vma1, 0)[0] = 11
    Blk(vma2, 0)[0] = 12

    # txn1 should commit ok
    tm1.commit()

    # txn2 should raise ConflictError and stay at 11 state
    raises(ConflictError, 'tm2.commit()')
    tm2.abort()

    assert Blk(vma2, 0)[0] == 11    # re-read in conn2
    Blk(vma2, 0)[0] = 13
    tm2.commit()

    assert Blk(vma1, 0)[0] == 11    # not yet propagated to conn1
    tm1.commit()                    # transaction boundary

    assert Blk(vma1, 0)[0] == 13    # re-read in conn1

    conn2.close()
    dbclose(root1)
示例#5
0
def test_zbigarray_vs_conflicts_metadata():
    root = testdb.dbopen()
    conn = root._p_jar
    db   = conn.db()
    conn.close()
    del root, conn

    tm1 = TransactionManager()
    tm2 = TransactionManager()

    conn1 = db.open(transaction_manager=tm1)
    root1 = conn1.root()

    # setup zarray
    root1['zarray3b'] = a1 = ZBigArray((10,), uint8)
    tm1.commit()

    # set zarray initial data
    a1[0:1] = [1]           # XXX -> [0] = 1  after BigArray can
    tm1.commit()

    # read zarray in conn2
    conn2 = db.open(transaction_manager=tm2)
    root2 = conn2.root()

    a2 = root2['zarray3b']
    assert a2[0:1] == [1]   # read data in conn2 + make sure read correctly
                            # XXX -> [0] == 1  after BigArray can

    # now zarray content is both in ZODB.Connection cache and in _ZBigFileH
    # cache for each conn1 and conn2. Resize arrays in both conn1 and conn2 and
    # see how it goes.

    a1.resize((11,))
    a2.resize((12,))

    # txn1 should commit ok
    tm1.commit()

    # txn2 should raise ConflictError and stay at 11 state
    raises(ConflictError, 'tm2.commit()')
    tm2.abort()

    assert len(a2) == 11    # re-read in conn2
    a2.resize((13,))
    tm2.commit()

    assert len(a1) == 11    # not yet propagated to conn1
    tm1.commit()            # transaction boundary

    assert len(a1) == 13    # re-read in conn1

    conn2.close()
    dbclose(root1)
示例#6
0
def update_schema_manager(event):
    # Use a local transaction manager to sidestep any issues
    # with an active transaction or explicit mode.
    txm = TransactionManager(explicit=True)
    txm.begin()
    with contextlib.closing(event.database.open(txm)) as conn:
        state = conn.root().get(PersistentWebhookSchemaManager.key, State())
        txm.abort()

    schema = get_schema_manager()
    schema.compareSubscriptionsAndComputeGeneration(state)
    def evolve(self):
        """Perform a requested evolution

           This method needs to use the component architecture, so
           we'll set it up:

             >>> from zope.component.testing import setUp, tearDown
             >>> setUp()

           We also need a test request:

             >>> from zope.publisher.browser import TestRequest
             >>> request = TestRequest()

           We also need to give it a publication with a database:

             >>> class Publication(object):
             ...     pass

             >>> request.setPublication(Publication())
             >>> from ZODB.tests.util import DB
             >>> db = DB()
             >>> request.publication.db = db

           We need to define some schema managers.  We'll define two
           using the demo package:

             >>> from zope.generations.generations import SchemaManager
             >>> from zope import component as ztapi
             >>> app1 = SchemaManager(0, 1, 'zope.generations.demo')
             >>> ztapi.provideUtility(app1, ISchemaManager, 'foo.app1')
             >>> app2 = SchemaManager(0, 0, 'zope.generations.demo')
             >>> ztapi.provideUtility(app2, ISchemaManager, 'foo.app2')

           And we need to record some data for them in the database.

             >>> from zope.generations.generations import evolve
             >>> evolve(db)

           This sets up the data and actually evolves app1:

             >>> conn = db.open()
             >>> conn.root()[generations_key]['foo.app1']
             1
             >>> conn.root()[generations_key]['foo.app2']
             0

           To evolve a data base schema, the user clicks on a submit
           button. If they click on the button for add1, a item will
           be added to the request, which we simulate:

             >>> request.form['evolve-app-foo.app1'] = 'evolve'

           We'll also increase the generation of app1:

             >>> app1.generation = 2

           Now we can create our view:

             >>> view = Managers(None, request)

           Now, if we call its `evolve` method, it should see that the
           app1 evolve button was pressed and evolve app1 to the next
           generation.

             >>> status = view.evolve()
             >>> conn.sync()
             >>> conn.root()[generations_key]['foo.app1']
             2

           The demo evolver just writes the generation to a database key:

             >>> from zope.generations.demo import key
             >>> conn.root()[key]
             ('installed', 'installed', 2)

           Note that, because the demo package has an install script,
           we have entries for that script.

           Which the returned status should indicate:

             >>> status['app']
             u'foo.app1'
             >>> status['to']
             2

           Now, given that the database is at the maximum generation
           for app1, we can't evolve it further.  Calling evolve again
           won't evolve anything:

             >>> status = view.evolve()
             >>> conn.sync()
             >>> conn.root()[generations_key]['foo.app1']
             2
             >>> conn.root()[key]
             ('installed', 'installed', 2)

           as the status will indicate by returning a 'to' generation
           of 0:

             >>> status['app']
             u'foo.app1'
             >>> status['to']
             0

           If the request doesn't have the key:

             >>> request.form.clear()

           Then calling evolve does nothing:

             >>> view.evolve()
             >>> conn.sync()
             >>> conn.root()[generations_key]['foo.app1']
             2
             >>> conn.root()[key]
             ('installed', 'installed', 2)

           We'd better clean upp:

             >>> db.close()
             >>> tearDown()
           """

        self.managers = managers = dict(
            zope.component.getUtilitiesFor(ISchemaManager))
        db = self._getdb()
        transaction_manager = TransactionManager()
        conn = db.open(transaction_manager=transaction_manager)
        transaction_manager.begin()
        try:
            generations = conn.root().get(generations_key, ())
            request = self.request
            for key in generations:
                generation = generations[key]
                rkey = request_key_format % key
                if rkey in request:
                    manager = managers[key]
                    if generation >= manager.generation:
                        return {'app': key, 'to': 0}

                    context = Context()
                    context.connection = conn
                    generation += 1
                    manager.evolve(context, generation)
                    generations[key] = generation
                    transaction_manager.commit()

                    return {'app': key, 'to': generation}
        finally:
            transaction_manager.abort()
            conn.close()
    def applications(self):
        """Get information about database-generation status

           This method needs to use the component architecture, so
           we'll set it up:

             >>> from zope.component.testing import setUp, tearDown
             >>> setUp()

           We also need a test request:

             >>> from zope.publisher.browser import TestRequest
             >>> request = TestRequest()

           We also need to give it a publication with a database:

             >>> class Publication(object):
             ...     pass

             >>> request.setPublication(Publication())
             >>> from ZODB.tests.util import DB
             >>> db = DB()
             >>> request.publication.db = db

           We need to define some schema managers.  We'll define two
           using the demo package:

             >>> from zope.generations.generations import SchemaManager
             >>> from zope import component as ztapi
             >>> app1 = SchemaManager(0, 1, 'zope.generations.demo')
             >>> ztapi.provideUtility(app1, ISchemaManager, 'foo.app1')
             >>> app2 = SchemaManager(0, 0, 'zope.generations.demo')
             >>> ztapi.provideUtility(app2, ISchemaManager, 'foo.app2')

           And we need to record some data for them in the database.

             >>> from zope.generations.generations import evolve
             >>> evolve(db)

           This sets up the data and actually evolves app1:

             >>> conn = db.open()
             >>> conn.root()[generations_key]['foo.app1']
             1
             >>> conn.root()[generations_key]['foo.app2']
             0

           Now, let's increment app1's generation:

             >>> app1.generation += 1

           so we can evolve it.

           Now we can create our view:

             >>> view = Managers(None, request)

           We call its applications method to get data about
           application generations. We are required to call evolve
           first:

             >>> view.evolve()
             >>> data = list(view.applications())
             >>> data.sort(key=lambda d1: d1['id'])

             >>> for info in data:
             ...     print(info['id'])
             ...     print(info['min'], info['max'], info['generation'])
             ...     print('evolve?', info['evolve'] or None)
             foo.app1
             0 2 1
             evolve? evolve-app-foo.app1
             foo.app2
             0 0 0
             evolve? None

           We'd better clean up:

             >>> db.close()
             >>> tearDown()

        """
        result = []

        db = self._getdb()
        transaction_manager = TransactionManager()
        conn = db.open(transaction_manager=transaction_manager)
        transaction_manager.begin()
        try:
            managers = self.managers
            generations = conn.root().get(generations_key, ())
            for key, generation in generations.items():
                manager = managers.get(key)
                if manager is None: # pragma: no cover
                    continue

                result.append({
                    'id': key,
                    'min': manager.minimum_generation,
                    'max': manager.generation,
                    'generation': generation,
                    'evolve': (generation < manager.generation
                               and request_key_format % key
                               or ''
                               ),
                    })

            return result
        finally:
            transaction_manager.abort()
            conn.close()