Beispiel #1
0
    def wu_participate(self, transaction_start, participant_index,
                       resource_name, attrs, args, kwargs, res_args, res_kwargs):

        # see whether the request by which this transaction was created
        # has expired in the meantime, and if it has, simply bail out
        # because the transaction should have long been perished

        # no attempt to execute the request is taken and no result
        # is delivered, simply because the transaction is assumed
        # to already be aborted, nowhere to report the result

        if pmnc.request.expired:
            pmnc.log.error("execution of resource {0:s} in transaction "
                           "{1:s} was late".format(resource_name, self))
            return

        try:

            pmnc.log.debug("resource {0:s} joins transaction {1:s}".\
                           format(resource_name, self))

            resource_instance = None        # no instance has been allocated yet
            resource_in_transaction = False # no transaction has been started on the instance
            resource_failed = True          # (preventive) request execution has been a failure

            while True: # breaks when the result is obtained, either value or exception

                # any failure prior to actual resource allocation results
                # in a recoverable ResourceError, pointlessly terminal

                try:

                    # the pending interval is measured from the beginning of the
                    # transaction, not from the beginning of the request

                    pending_ms = int((time() - transaction_start) * 1000)
                    pmnc.performance.sample("resource.{0:s}.pending_time".\
                                            format(resource_name), pending_ms)

                    # allocate a resource instance from a specific resource pool

                    resource_pool = pmnc.shared_pools.get_resource_pool(resource_name)
                    resource_instance = resource_pool.allocate()

                except: # tested
                    result = ResourceError.snap_exception(
                                    participant_index = participant_index,
                                    recoverable = True, terminal = True) # but not really terminal,
                    break # while True                                   # no instance to terminate

                # some resource instance has been allocated, beginning a transaction,
                # a failure would result in a ResourceError, recoverable yet terminal
                # unless explicitly specified otherwise

                try:

                    # see if a transaction should be started in as much time as the request has left

                    if pmnc.request.remain < resource_instance.min_time:
                        raise ResourceError(description = "transaction {0:s} is declined by resource instance " # tested
                                                          "{1:s}".format(self, resource_instance.name),
                                            recoverable = True, terminal = False) # the instance stays alive

                    pmnc.log.debug("resource instance {0:s} is used in transaction {1:s}, {2:s}".\
                                   format(resource_instance.name, self, self._resource_ttl(resource_instance)))

                    # begin a new transaction, this is presumably reversible operation

                    resource_instance.begin_transaction(self._xid,
                                                        source_module_name = self._source_module_name,
                                                        transaction_options = self._options,
                                                        resource_args = res_args,
                                                        resource_kwargs = res_kwargs)

                except ResourceError as e:
                    result = self._apply_error(participant_index, resource_instance, e)
                    break # while True
                except: # tested
                    result = ResourceError.snap_exception(
                                    participant_index = participant_index,
                                    recoverable = True, terminal = True)
                    resource_instance.expire()
                    break # while True
                else:
                    resource_in_transaction = True

                # resource instance is now in transaction, executing the request,
                # a failure would result in a ResourceError, unrecoverable and
                # terminal unless explicitly specified otherwise

                try:

                    # replay attribute accesses to obtain the actual target method

                    target_method = resource_instance
                    for attr in attrs:
                        target_method = getattr(target_method, attr)

                    # execute the request, registering the execution time

                    with pmnc.performance.timing("resource.{0:s}.processing_time".format(resource_name)):
                        result = target_method(*args, **kwargs)

                except ResourceError as e:
                    result = self._apply_error(participant_index, resource_instance, e)
                    break # while True
                except Exception: # tested
                    result = ResourceError.snap_exception(
                                    participant_index = participant_index,
                                    recoverable = False, terminal = True)
                    resource_instance.expire()
                    break # while True
                else:
                    resource_instance.reset_idle_timeout()
                    resource_failed = False
                    break # while True

            # we got an intermediate result, possibly an exception

            try:

                self._results.push((participant_index, result)) # deliver the result to the pending transaction

                # register the actual result of this participant

                pmnc.performance.event("resource.{0:s}.transaction_rate.{1:s}".\
                                       format(resource_name, resource_failed and "failure" or "success"))

                if not resource_in_transaction: # as we couldn't begin a transaction,
                    return "failure"            # we are not interested in the decision

                pmnc.log.debug("resource instance {0:s} is waiting for decision in "
                               "transaction {1:s}".format(resource_instance.name, self))

                # figure out whether the resource has to commit or rollback

                commit_transaction = False

                if pmnc.request.wait(self._decision): # wait for transaction's decision
                    if self._commit.is_set():
                        if not resource_failed:
                            commit_transaction = True
                            pmnc.log.debug("resource instance {0:s} decided to commit in transaction "
                                           "{1:s}".format(resource_instance.name, self))
                        else:
                            pmnc.log.warning("resource instance {0:s} had to rollback despite decision to commit "
                                             "in transaction {1:s}".format(resource_instance.name, self))
                    else:
                        pmnc.log.debug("resource instance {0:s} decided to rollback in transaction "
                                       "{1:s}".format(resource_instance.name, self))
                else:
                    pmnc.log.warning("resource instance {0:s} had to abandon waiting for decision and "
                                     "rollback in transaction {1:s}".format(resource_instance.name, self))

                # complete the transaction and return the final outcome

                if commit_transaction:
                    try:
                        resource_instance.commit()
                    except:
                        pmnc.log.error("resource instance {0:s} failed to commit in transaction {1:s}: "
                                       "{2:s}".format(resource_instance.name, self, exc_string())) # this is a severe problem
                        resource_instance.expire()
                        return "failure"
                    else:
                        pmnc.log.debug("resource instance {0:s} committed in transaction "
                                       "{1:s}".format(resource_instance.name, self))
                        return "commit"
                else:
                    try:
                        resource_instance.rollback()
                    except:
                        pmnc.log.warning("resource instance {0:s} failed to rollback in transaction {1:s}: "
                                         "{2:s}".format(resource_instance.name, self, exc_string())) # this is not a big deal
                        resource_instance.expire()
                        return "failure"
                    else:
                        pmnc.log.debug("resource instance {0:s} rolled back in transaction "
                                       "{1:s}".format(resource_instance.name, self))
                        return "rollback"

            finally:
                if resource_instance:
                    pmnc.log.debug("resource instance {0:s} is being released, {1:s}".\
                                   format(resource_instance.name, self._resource_ttl(resource_instance)))
                    resource_pool.release(resource_instance)

        except:
            pmnc.log.error(exc_string()) # this should not normally happen, but do
            raise                        # not allow such exception to be silenced