Exemple #1
0
    def testKeyError(self):
        """
        KeyError is raised as a tuple and not expection. Re-raising it
        should be aware of this fact and handled carfully.
        """
        try:
            with utils.RollbackContext():
                {}['aKey']
        except KeyError:
            return
        except Exception:
            self.fail("Wrong exception was raised")

        self.fail("Exception was not raised")
Exemple #2
0
    def testRaise(self):
        """
        Test that raising an exception in a deferred action does
        not block all subsequent actions from running
        """
        try:
            with utils.RollbackContext() as rollback:
                rollback.prependDefer(self._callDef)
                rollback.prependDefer(self._raiseDef)
                rollback.prependDefer(self._callDef)
        except Exception:
            self.assertEquals(self._called, 2)
            return

        self.fail("Exception was not raised")
Exemple #3
0
    def testFirstUndoException(self):
        """
        Test that if multiple actions raise an exception only the first one is
        raised. When performing a batch rollback operations, probably the first
        exception is the root cause.
        """
        try:
            with utils.RollbackContext() as rollback:
                rollback.prependDefer(self._callDef)
                rollback.prependDefer(self._raiseDef)
                rollback.prependDefer(self._callDef)
                rollback.prependDefer(self._raiseDef, RuntimeError())
                rollback.prependDefer(self._callDef)
        except RuntimeError:
            self.assertEquals(self._called, 3)
            return
        except Exception:
            self.fail("Wrong exception was raised")

        self.fail("Exception was not raised")
Exemple #4
0
    def testPreferOriginalException(self):
        """
        Test that if an exception is raised both from the with
        statement and from the finally clause, the one from the with
        statement is the one that's actually raised.
        More info in: http://docs.python.org/
        2.6/library/stdtypes.html#contextmanager.__exit__
        """
        try:
            with utils.RollbackContext() as rollback:
                rollback.prependDefer(self._raiseDef, self.UndoException())
                raise self.OriginalException()
        except self.OriginalException:
            return
        except self.UndoException:
            self.fail("Wrong exception was raised - from undo function. \
                        should have re-raised OriginalException")
        except Exception:
            self.fail("Wrong exception was raised")

        self.fail("Exception was not raised")
    def releaseResource(self, namespace, name):
        # WARN : unlike in resource acquire the user now has the request
        #        object and can CANCEL THE REQUEST at any time. Always use
        #        request.grant between try and except to properly handle such
        #        a case
        full_name = "%s.%s" % (namespace, name)

        self._log.debug("Trying to release resource '%s'", full_name)
        with utils.RollbackContext() as contextCleanup, self._syncRoot.shared:
            try:
                namespaceObj = self._namespaces[namespace]
            except KeyError:
                raise ValueError(
                    "Namespace '%s' is not registered with this "
                    "manager", namespace)
            resources = namespaceObj.resources

            with namespaceObj.lock:
                try:
                    resource = resources[name]
                except KeyError:
                    raise ValueError("Resource '%s.%s' is not currently "
                                     "registered" % (namespace, name))

                resource.activeUsers -= 1
                self._log.debug("Released resource '%s' (%d active users)",
                                full_name, resource.activeUsers)

                # Is some one else is using the resource
                if resource.activeUsers > 0:
                    return
                self._log.debug(
                    "Resource '%s' is free, finding out if anyone "
                    "is waiting for it.", full_name)
                # Grant a request
                while True:
                    # Is there someone waiting for the resource
                    if len(resource.queue) == 0:
                        self._freeResource(resources[name])
                        del resources[name]
                        self._log.debug(
                            "No one is waiting for resource '%s', "
                            "Clearing records.", full_name)
                        return

                    self._log.debug(
                        "Resource '%s' has %d requests in queue. "
                        "Handling top request.", full_name,
                        len(resource.queue))
                    nextRequest = resource.queue.pop()
                    # We lock the request to simulate a transaction. We cannot
                    # grant the request before there is a resource switch. And
                    # we can't do a resource switch before we can guarantee
                    # that the request will be granted.
                    with nextRequest.syncRoot:
                        if nextRequest.canceled():
                            self._log.debug(
                                "Request '%s' was canceled, "
                                "Ignoring it.", nextRequest)
                            continue

                        try:
                            self._switchLockType(resource,
                                                 nextRequest.lockType)
                        except Exception:
                            self._log.warning(
                                "Resource factory failed to create "
                                "resource '%s'. Canceling request.",
                                full_name,
                                exc_info=True)
                            nextRequest.cancel()
                            continue

                        nextRequest.grant()
                        contextCleanup.defer(
                            partial(
                                nextRequest.emit,
                                ResourceRef(namespace, name, resource.realObj,
                                            nextRequest.reqID)))

                        resource.activeUsers += 1

                        self._log.debug("Request '%s' was granted",
                                        nextRequest)
                        break

                # If the lock is exclusive were done
                if resource.currentLock == EXCLUSIVE:
                    return

                # Keep granting shared locks
                self._log.debug("This is a shared lock. Granting all shared "
                                "requests")
                while len(resource.queue) > 0:

                    nextRequest = resource.queue[-1]
                    if nextRequest.canceled():
                        resource.queue.pop()
                        continue

                    if nextRequest.lockType == EXCLUSIVE:
                        break

                    nextRequest = resource.queue.pop()
                    try:
                        nextRequest.grant()
                        contextCleanup.defer(
                            partial(
                                nextRequest.emit,
                                ResourceRef(namespace, name, resource.realObj,
                                            nextRequest.reqID)))
                    except RequestAlreadyProcessedError:
                        continue

                    resource.activeUsers += 1
                    self._log.debug(
                        "Request '%s' was granted (%d "
                        "active users)", nextRequest, resource.activeUsers)
    def registerResource(self, namespace, name, lockType, callback):
        """
        Register to acquire a resource asynchronously.

        :returns: a request object that tracks the current request.
        """
        full_name = "%s.%s" % (namespace, name)

        if not self._resourceNameValidator.match(name):
            raise se.InvalidResourceName(name)

        if lockType not in (SHARED, EXCLUSIVE):
            raise InvalidLockType("Invalid locktype %r was used" % lockType)

        request = Request(namespace, name, lockType, callback)
        self._log.debug("Trying to register resource '%s' for lock type '%s'",
                        full_name, lockType)
        with utils.RollbackContext() as contextCleanup, self._syncRoot.shared:
            try:
                namespaceObj = self._namespaces[namespace]
            except KeyError:
                raise ValueError("Namespace '%s' is not registered with this "
                                 "manager" % namespace)

            resources = namespaceObj.resources
            with namespaceObj.lock:
                try:
                    resource = resources[name]
                except KeyError:
                    if not namespaceObj.factory.resourceExists(name):
                        raise KeyError("No such resource '%s'" % (full_name))
                else:
                    if len(resource.queue) == 0 and \
                            resource.currentLock == SHARED and \
                            request.lockType == SHARED:
                        resource.activeUsers += 1
                        self._log.debug(
                            "Resource '%s' found in shared state "
                            "and queue is empty, Joining current "
                            "shared lock (%d active users)", full_name,
                            resource.activeUsers)
                        request.grant()
                        contextCleanup.defer(
                            request.emit,
                            ResourceRef(namespace, name, resource.realObj,
                                        request.reqID))
                        return RequestRef(request)

                    resource.queue.insert(0, request)
                    self._log.debug(
                        "Resource '%s' is currently locked, "
                        "Entering queue (%d in queue)", full_name,
                        len(resource.queue))
                    return RequestRef(request)

                # TODO : Creating the object inside the namespace lock causes
                #        the entire namespace to lock and might cause
                #        performance issues. As this is no currently a problem
                #        I left it as it is to keep the code simple. If there
                #        is a bottleneck in the resource framework, its
                #        probably here.
                try:
                    obj = namespaceObj.factory.createResource(name, lockType)
                except:
                    self._log.warning(
                        "Resource factory failed to create resource"
                        " '%s'. Canceling request.",
                        full_name,
                        exc_info=True)
                    contextCleanup.defer(request.cancel)
                    return RequestRef(request)

                resource = resources[name] = ResourceInfo(obj, namespace, name)
                resource.currentLock = request.lockType
                resource.activeUsers += 1

                self._log.debug(
                    "Resource '%s' is free. Now locking as '%s' "
                    "(1 active user)", full_name, request.lockType)
                request.grant()
                contextCleanup.defer(
                    request.emit,
                    ResourceRef(namespace, name, resource.realObj,
                                request.reqID))
                return RequestRef(request)
Exemple #7
0
    def test(self):
        with utils.RollbackContext() as rollback:
            rollback.prependDefer(self._callDef)

        self.assertEquals(self._called, 1)