[openstack-dev] [oslo] Asyncio and oslo.messaging

Nikola Đipanov ndipanov at redhat.com
Mon Jul 7 16:35:21 UTC 2014


On 07/07/2014 02:58 PM, Victor Stinner wrote:
> Hi,
> 
> Le lundi 7 juillet 2014, 12:48:59 Nikola Đipanov a écrit :
>> When I read all of this stuff and got my head around it (took some time
>> :) ), a glaring drawback of such an approach, and as I mentioned on the
>> spec proposing it [1] is that we would not really doing asyncio, we
>> would just be pretending we are by using a subset of it's APIs, and
>> having all of the really important stuff for overall design of the code
>> (code that needs to do IO in the callbacks for example) and ultimately -
>> performance, completely unavailable to us when porting.
> 
> The global plan is to:
> 
> 1. use asyncio API
> 2. detect code relying on implicit scheduling and patch it to use explicit 
> scheduling (use the coroutine syntax with yield)
> 3. "just" change the event loop from greenio to a classic "select" event loop 
> (select, poll, epoll, kqueue, etc.) of Trollius
> 
> I see asyncio as an API: it doesn't really matter which event loop is used, 
> but I want to get rid of eventlet :-)
> 

Well this is kind of a misrepresentation since with how greenio is
proposed now in the spec, we are not actually running the asyncio
eventloop, we are running the eventlet eventloop (that uses greenlet API
to switch green threads). More precisely - we will only run the
asyncio/trollius BaseEventLoop._run_once method in a green thread that
is scheduled by eventlet hub as any other.

Correct me if I'm wrong there, it's not exactly straightforward :)

And asyncio may be just an API, but it is a lower level and
fundamentally different API than what we deal with when running with
eventlet, so we can't just pretend we are not missing the code that
bridges this gap, since that's where the real 'meat' of the porting
effort lies, IMHO.

>> So in Mark's example above:
>>
>>   @asyncio.coroutine
>>   def foo(self):
>>     result = yield from some_async_op(...)
>>     return do_stuff(result)
>>
>> A developer would not need to do anything that asyncio requires like
>> make sure that some_async_op() registers a callback with the eventloop
>> (...)
> 
> It's not possible to break the world right now, some people will complain :-)
> 
> The idea is to have a smooth transition. We will write tools to detect 
> implicit scheduling and fix code. I don't know the best option for that right 
> now (monkey-patch eventlet, greenio or trollius?).
> 
>> So I hacked up together a small POC of a different approach. In short -
>> we actually use a real asyncio selector eventloop in a separate thread,
>> and dispatch stuff to it when we figure out that our callback is in fact
>> a coroutine.
> 
> See my previous attempty: the asyncio executor runs the asyncio event loop in 
> a dedicated thread:
> https://review.openstack.org/#/c/70948/
> 

Yes I spent a good chunk of time looking at that patch, that's where I
got some ideas for my attempt at it
(https://github.com/djipko/eventlet-asyncio). I left some comments there
but forgot to post them (fixed now).

The bit you miss is how to actually communicate back the result of the
dispatched methods.

> I'm not sure that it's possible to use it in OpenStack right now because the 
> whole Python standard library is monkey patched, including the threading 
> module.
> 

Like I said on the review - we unpatch Threading in the libvirt driver
in Nova for example, so it's not like it's beyond us :), and eventlet
gives you relatively good API's for dealing with what gets patched and
when - so greening a single endpoint and a listener is very much
feasible I would say - and this is what we would need to have the
'separation between the worlds' (so to speak :) ).

> The issue is also to switch the control flow between the event loop thread and 
> the main thread. There is no explicit event loop in the main thread. The most 
> obvious solution for that is to schedule tasks using eventlet...
> 
> That's exactly the purpose of greenio: glue between asyncio and greenlet. And 
> using greenio, there is no need of running a new event loop in a thread, which 
> makes the code simpler.
> 
>> (..) we would probably not be 'greening the world' but rather
>> importing patched
>> non-ported modules when we need to dispatch to them. This may sound like
>> a big deal, and it is, but it is critical to actually running ported
>> code in a real asyncio evenloop.
> 
> It will probably require a lot of work to get rid of eventlet. The greenio 
> approach is more realistic because projects can be patched one by one, one file 
> by one file. The goal is also to run projects unmodified with the greenio 
> executor.
> 

All of this would be true with the other approach as well.

>> Another interesting problem is (as I have briefly mentioned in [1]) -
>> what happens when we need to synchronize between eventlet-run and
>> asyncio-run callbacks while we are in the process of porting.
> 
> Such issue is solved by greenio. As I wrote, it's not a good idea to have two 
> event loops in the same process.
> 

Well it's not really solved - it's just hidden by the fact that you are
running evenlet and not asyncio. You would still be relying on evenlet
primitives to do the right thing which is (at least for me) totally
defeating the purpose.

As for two eventloops, I don't think that's true - actually, all of
asyncio/trollius APIs are built with having in mind more than one event
loop (you can pass a loop object to Futures, Tasks and many other
places) and it also gives you the ability to dispatch to another thread
(which you actually used as well in your patch). It all comes down to
monkey patching, but as discussed above - I think we can work around that.

Anyway - I will try to rewrite my POC as an actual oslo.messaging
Executor, as that might give people a better idea of what I am getting
at, and also add comments about where the tricky bits are.

N.



More information about the OpenStack-dev mailing list