From a8fb6c952d77cc65a23af1e824c411ad2ef04a34 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 06 2019 14:23:15 +0000 Subject: [PATCH 1/2] rollback errors in multiCall Fixes: https://pagure.io/koji/issue/1357 --- diff --git a/hub/kojixmlrpc.py b/hub/kojixmlrpc.py index 08a344b..18b11ec 100644 --- a/hub/kojixmlrpc.py +++ b/hub/kojixmlrpc.py @@ -329,11 +329,14 @@ class ModXMLRPCRequestHandler(object): results and errors, and return those as a list.""" results = [] for call in calls: + savepoint = kojihub.Savepoint('multiCall_loop') try: result = self._dispatch(call['methodName'], call['params']) except Fault as fault: + savepoint.rollback() results.append({'faultCode': fault.faultCode, 'faultString': fault.faultString}) - except: + except Exception: + savepoint.rollback() # transform unknown exceptions into XML-RPC Faults # don't create a reference to full traceback since this creates # a circular reference. From 7f64d7896fa66c251b8eb059581a6f7214c5c70a Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 06 2019 14:23:15 +0000 Subject: [PATCH 2/2] document multicall --- diff --git a/docs/source/writing_koji_code.rst b/docs/source/writing_koji_code.rst index 1106145..6aa911b 100644 --- a/docs/source/writing_koji_code.rst +++ b/docs/source/writing_koji_code.rst @@ -174,6 +174,62 @@ can fix this behavior in ``koji/__init__.py`` in the \_taskLabel function. Here you can define the string(s) to display when Koji receives status on a task. That is the return value. +Using multiCall +~~~~~~~~~~~~~~~ + +Koji supports a multicall feature where many calls are passed to the +server wrapped as a single call. This can reduce the overhead when a +large number of related calls need to be made. + +The ``ClientSession`` class provides support for this and there are several +examples in the existing client code. Some examples in the cli include: +``edit-host``, ``add-pkg``, ``disable-host``, and ``list-hosts``. + +To use the feature, you first set the ``multicall`` attribute of the session +to ``True``. Once this is done, the session will not immediately process +further calls but will instead store their parameters for later. To tell the +session to process them, use the ``multiCall()`` method (note the +capitalization). + +The ``multiCall()`` method returns a list of results, one for each call +in the multicall. Each result with either be: + +1. the result of the call wrapped in a singleton list +2. a dictionary representing the error raised by the call + +Here is a simple example from the koji-tools package: + +:: + + session.multicall = True + for host in hosts: + session.listChannels(hostID=host['id']) + for host, [channels] in zip(hosts, session.multiCall(strict=True)): + host['channels'] = channels + +Note that when using multicall for informational calls, it is important +to keep track of which result is which. Here we use the existing hosts +list as a unifying index. Python's ``zip`` function is useful here. +Also note the unpacking of the singletons. + +The ``multiCall()`` method supports a few options. Here is its signature: + +:: + + multiCall(strict=False, batch=None): + +If the strict option is set to True, then this method will raise the +first error it encounters, if any. + +If the batch option is set to a number greater than zero, the calls +will be spread across multiple multicall batches of at most this +number. + +The hub processes multicalls in a *single database transaction*. Note that if +the ``batch`` option is used, then each batch is a separate multicall in the +api and therefore a separate transaction. + + Koji-Hub --------