Article
· 8 hr ago 5m read

Interoperability On Python update async support

It's been a long time since I didn't write an update post on IoP.

image

So what's new since IoP command line interface was released?

Two new big features were added to IoP:
- Rebranding: the grongier.pex module was renamed to iop to reflect the new name of the project.
- Async support: IoP now supports async functions and coroutines.

Rebranding

The grongier.pex module was renamed to iop to reflect the new name of the project.

The grongier.pex module is still available for backward compatibility, but it will be removed in the future.

Async support

IoP supports async calls for a long time, but it was not possible to use async functions and coroutines directly in IoP.

Before jumping into this new feature, I will explain how async calls work in InterSystems IRIS and present two examples of how to use async calls IoP.

Legacy async calls

Let's see how legacy async calls work:

from iop import BusinessProcess
from msg import MyMessage


class MyBP(BusinessProcess):

    def on_message(self, request):
        msg_one = MyMessage(message="Message1")
        msg_two = MyMessage(message="Message2")

        self.send_request_async("Python.MyBO", msg_one,completion_key="1")
        self.send_request_async("Python.MyBO", msg_two,completion_key="2")

    def on_response(self, request, response, call_request, call_response, completion_key):
        if completion_key == "1":
            self.response_one = call_response
        elif completion_key == "2":
            self.response_two = call_response

    def on_complete(self, request, response):
        self.log_info(f"Received response one: {self.response_one.message}")
        self.log_info(f"Received response two: {self.response_two.message}")

Basically they work the same way as async call works in IRIS. The send_request_async method sends a request to a Business Operation and the on_response method is called when the response is received.

You can distinguish the responses by the completion_key parameter.

Send multiple sync requests

It's not exactly a new feature, but it's worth mentioning that you can send multiple sync requests in parallel:

from iop import BusinessProcess
from msg import MyMessage


class MyMultiBP(BusinessProcess):

    def on_message(self, request):
        msg_one = MyMessage(message="Message1")
        msg_two = MyMessage(message="Message2")

        tuple_responses = self.send_multi_request_sync([("Python.MyMultiBO", msg_one),
                                                        ("Python.MyMultiBO", msg_two)])

        self.log_info("All requests have been processed")
        for target,request,response,status in tuple_responses:
            self.log_info(f"Received response: {response.message}")

Here we are sending two requests to the same Business Operation in parallel.

The response is a tuple with the target, request, response and status of each call.

It's really useful when you need to send multiple requests and you don't care about the order of the responses.

Async functions and coroutines

Now let's see how to use async functions and coroutines in IoP:

import asyncio

from iop import BusinessProcess
from msg import MyMessage


class MyAsyncNGBP(BusinessProcess):

    def on_message(self, request):

        results = asyncio.run(self.await_response(request))

        for result in results:
            print(f"Received response: {result.message}")

    async def await_response(self, request):
        msg_one = MyMessage(message="Message1")
        msg_two = MyMessage(message="Message2")

        # use asyncio.gather to send multiple requests asynchronously
        # using the send_request_async_ng method
        tasks = [self.send_request_async_ng("Python.MyAsyncNGBO", msg_one),
                 self.send_request_async_ng("Python.MyAsyncNGBO", msg_two)]

        return await asyncio.gather(*tasks)

In this example, we are sending multiple requests to the same Business Operation in parallel using the send_request_async_ng method.

If you read this post carefully until this point, please comment "Boomerang". This is may be a detail for you, but for me it's mean a lot. Thanks!

The await_response method is a coroutine that sends multiple requests and waits for all responses to be received.
Thanks to the asyncio.gather function, we can wait for all responses to be received in parallel.

The benefits of using async functions and coroutines are:

  • Better performance: you can send multiple requests in parallel.
  • Easier to read and maintain: you can use the await keyword to wait for responses.
  • More flexibility: you can use the asyncio module to create complex workflows.
  • More control: you can use the asyncio module to handle exceptions and timeouts.

Conclusion

What are the defirences between send_request_async, send_multi_request_sync and send_request_async_ng?

  • send_request_async: sends a request to a Business Operation and waits for the response if the on_response method is implemented and the completion_key parameter is used.
    • benefit: you can use async calls the way you are used to.
    • drawback: you can be hard to maintain if you need to send multiple requests in parallel.
  • send_multi_request_sync: sends multiple requests to the same Business Operation in parallel and waits for all responses to be received.
    • benefit: it's easy to use.
    • drawback: you can control the order of the responses (i mean the list of responses is not ordered).
  • send_request_async_ng: sends multiple requests to the same Business Operation in parallel and waits for all responses to be received.
    • benefit: you can control the order of the responses.
    • drawback: you need to use async functions and coroutines.

Happy multithreading!

Discussion (2)1
Log in or sign up to continue