Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to sync_to_async(executor=gevent.threadpool.ThreadPoolExecutor)? #264

Closed
allen-munsch opened this issue May 28, 2021 · 1 comment
Closed

Comments

@allen-munsch
Copy link

allen-munsch commented May 28, 2021

Is this doable?

sync_to_async(
    Books.objects.all, 
    thread_sensitive=False, 
    executor=gevent.threadpool.ThreadPoolExecutor(max_workers=1)
)

My guess, is that doing all the messy patchwork stuff probably won't be supported officially as part of asgiref, however, it'd be nice as a user to have an interface that shims between the two worlds. However messy it is under the hood.

Use case:

Mounting an ASGI application inside of a WSGI app ( which uses psycogreen, gevent, eventlet, etc ) ensuring all the production core code for a bigger app remains performant, while at the same time enabling users to begin experimenting/mounting, newer ASGI apps, without having to re-write all of the django orm code using turtleorm, sqlalchemy, etc.

Example scenario: "How can I mount a FastAPI app to the inside of Django?"

Something really wacky along these lines:

try:
    from gevent.threadpool import ThreadPoolExecutor as GThreadPoolExecutor
    from django.conf import settings
    if settings.GEVENT_DJANGO_ASYNC_ORM:
        from gevent import monkey
        monkey.patch_all()
        def monkey_patch_the_monkey_patchers(ex):
            from .patch_gevent import _FutureProxy
            def submit(ex, fn, *args, **kwargs): # pylint:disable=arguments-differ
                print(fn, *args, **kwargs)
                with ex._shutdown_lock: # pylint:disable=not-context-manager
                    if ex._shutdown:
                        raise RuntimeError('cannot schedule new futures after shutdown')
                    future = ex._threadpool.spawn(fn, *args, **kwargs)
                    proxy_future = _FutureProxy(future)

                    # just fake it, maybe?
                    proxy_future.__class__ = concurrent.futures.Future

                    return proxy_future
            ex.submit = submit
            return ex
        MonkeyPoolExecutor = monkey_patch_the_monkey_patchers(GThreadPoolExecutor)
        conf = {"thread_sensitive": False, "executor": MonkeyPoolExecutor(max_workers=1)}
        executor_ = MonkeyPoolExecutor
except Exception as e:
    print('uhoh', e)
    pass
    
all_the_books = await sync_to_async(
    Books.objects.all, 
    **conf
)()
@andrewgodwin
Copy link
Member

Yeah, unfortunately gevent has such a different execution model that I don't think we can sensibly support interoperability with it - at least not without someone paid full-time to figure all this out.

You can likely get it working in a hacky way, but making so it works reliably, propagates exceptions properly, handles threadlocals and locking correctly? Probably a mammoth task.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants