Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous signals with asyncio

My model post processing is using the post_save signal:

from django.core.signals import request_finished
from django.dispatch import receiver
from models import MyModel
from pipeline import this_takes_forever


@receiver(post_save, sender=MyModel)
def my_callback(sender, **kwargs):
    this_takes_forever(sender)

The this_takes_forever routine does IO so I want to defer it to avoid blocking the request too much.

I thought this was a great use case for the new asyncio module. But I have a hard time getting my mind around the whole process.

I think I should be able to adapt the signal receiver like this:

@receiver(post_save, sender=MyModel)
def my_callback(sender, **kwargs):
    loop = asyncio.get_event_loop()
    loop.run_until_complete(this_takes_forever(sender))
    loop.close()

Provided this_takes_forever is also adapted to be a coroutine.

@coroutine
def this_takes_forever(instance):
    # do something with instance
    return instance

This sounds too magical to work. And in fact it halts with an AssertionError:

AssertionError at /new/
There is no current event loop in thread 'Thread-1'.

I don't see where should I start the loop in this context. Anyone tried something like this?

like image 336
tutuca Avatar asked May 21 '14 21:05

tutuca


People also ask

Is Asyncio asynchronous?

asyncio is used as a foundation for multiple Python asynchronous frameworks that provide high-performance network and web-servers, database connection libraries, distributed task queues, etc. asyncio is often a perfect fit for IO-bound and high-level structured network code.

Does Python support asynchronous programming?

It makes use of Python async features using asyncio/await provided in Python 3. The time and queue modules have been replaced with the asyncio package. This gives your program access to asynchronous friendly (non-blocking) sleep and queue functionality.

Does twisted use Asyncio?

There are two ways, in which asyncio can be used in Twisted. One is running Twisted on top of Asyncio,and other is running asyncio on top of Twisted. While, theoretically, both are possible, only one has been implemented uptil yet. Twisted is run over asyncio, so the current blog deals with that.

Why is Asyncio better than threads?

Asyncio vs threading: Async runs one block of code at a time while threading just one line of code at a time. With async, we have better control of when the execution is given to other block of code but we have to release the execution ourselves.


1 Answers

You get no any benefit in your case:

@receiver(post_save, sender=MyModel)
def my_callback(sender, **kwargs):
    this_takes_forever(sender)

is equal to

@receiver(post_save, sender=MyModel)
def my_callback(sender, **kwargs):
    loop = asyncio.get_event_loop()
    loop.run_until_complete(this_takes_forever(sender))
    loop.close()

in terms of execution time. loop.run_until_complete waits for end of this_takes_forever(sender) coroutine call, so you get synchronous call in second case as well as in former one.

About AssertionError: you start Django app in multithreaded mode, but asyncio makes default event loop for main thread only -- you should to register new loop for every user-created thread where you need to call asyncio code.

But, say again, asyncio cannot solve your particular problem, it just incompatible with Django.

The standard way for Django is to defer long-running code into celery task (see http://www.celeryproject.org/)

like image 164
Andrew Svetlov Avatar answered Nov 12 '22 14:11

Andrew Svetlov