etianen / aiohttp-wsgi

WSGI adapter for aiohttp.
https://aiohttp-wsgi.readthedocs.io
BSD 3-Clause "New" or "Revised" License
232 stars 20 forks source link

Flask + ProcessPoolExecutor -> TypeError: can't pickle _thread.lock objects #28

Open codeape2 opened 3 years ago

codeape2 commented 3 years ago

To reproduce:

from concurrent.futures.process import ProcessPoolExecutor
from concurrent.futures.thread import ThreadPoolExecutor

from aiohttp import web
from aiohttp_wsgi import WSGIHandler
from flask import Flask

flaskapp = Flask(__name__)

@flaskapp.route('/')
def index():
    return 'hello from flask'

executor = ProcessPoolExecutor()  # ThreadPoolExecutor() works

aioapp = web.Application()
aioapp.router.add_route('*', '/{path_info:.*}', WSGIHandler(flaskapp, executor=executor))

if __name__ == '__main__':
    web.run_app(aioapp)

Traceback:

======== Running on http://0.0.0.0:8080 ========
(Press CTRL+C to quit)
Error handling request
concurrent.futures.process._RemoteTraceback: 
"""
Traceback (most recent call last):
  File "F:\envs\_april\lib\multiprocessing\queues.py", line 236, in _feed
    obj = _ForkingPickler.dumps(obj)
  File "F:\envs\_april\lib\multiprocessing\reduction.py", line 51, in dumps
    cls(buf, protocol).dump(obj)
TypeError: can't pickle _thread.lock objects
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "F:\envs\_april\lib\site-packages\aiohttp\web_protocol.py", line 418, in start
    resp = await task
  File "F:\envs\_april\lib\site-packages\aiohttp\web_app.py", line 458, in _handle
    resp = await handler(request)
  File "F:\envs\_april\lib\site-packages\aiohttp\web_urldispatcher.py", line 158, in handler_wrapper
    return await result
  File "F:\envs\_april\lib\site-packages\aiohttp_wsgi\wsgi.py", line 266, in handle_request
    environ,
  File "F:\envs\_april\lib\multiprocessing\queues.py", line 236, in _feed
    obj = _ForkingPickler.dumps(obj)
  File "F:\envs\_april\lib\multiprocessing\reduction.py", line 51, in dumps
    cls(buf, protocol).dump(obj)
TypeError: can't pickle _thread.lock objects
codeape2 commented 3 years ago

A minimal wsgi app also fails:

def simplest_wsgi_app(environ, start_response):
    print(environ)
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return [b"hello from wsgi!\n"]

With TypeError: can't pickle _asyncio.Future objects

codeape2 commented 3 years ago

The minimal wsgi app works if I use a modified WSGIHandler:

class MultiprocessCompatibleWSGIHandler(WSGIHandler):
    def _get_environ(self, request, body, content_length):
        environ = super()._get_environ(request, body, content_length)
        del environ['asyncio.loop']
        del environ['asyncio.executor']
        del environ['aiohttp.request']
        return environ

... but flask does not work. I guess serializing the entire flask application is not feasible. A possible approach for using a ProcessPool executor is to initialize the wsgi app in the process pool's initializer.