squeaky-pl / japronto

Screaming-fast Python 3.5+ HTTP toolkit integrated with pipelining HTTP server based on uvloop and picohttpparser.
MIT License
8.61k stars 581 forks source link

Worker crashed on signal SIGABRT! #127

Open habibutsu opened 6 years ago

habibutsu commented 6 years ago

The following script cause of crashing of server:

import sys
import asyncio
import asyncpg
import japronto

PG_DSN = 'postgres://postgres@localhost:5432'

app = japronto.Application()

async def init_db():
    app.db = await asyncpg.connect(dsn=PG_DSN, loop=app.loop)
    print('=> db connected')

async def hello(request):
    return request.Response(text='Hello world!')

if __name__ == '__main__':
    print(
        '=> versions:\n'
        f'python: {sys.version}\n'
        f'japronto: {japronto.__version__}'
    )
    app.router.add_route('/', hello)
    app.loop.run_until_complete(init_db())
    print('=> running')
    app.run(debug=True)

Output:

=> versions:
python: 3.6.1 (default, Apr 28 2017, 21:58:47)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)]
japronto: 0.1.1
=> db connected
=> running
Fatal Python error: Aborted

Current thread 0x00007fff99cbd380 (most recent call first):
  File "/Users/habibutsu/.pyenv/versions/pylibs3.6.1/lib/python3.6/site-packages/japronto/app/__init__.py", line 169 in serve
  File "/Users/habibutsu/.pyenv/versions/3.6.1/lib/python3.6/multiprocessing/process.py", line 93 in run
  File "/Users/habibutsu/.pyenv/versions/3.6.1/lib/python3.6/multiprocessing/process.py", line 249 in _bootstrap
  File "/Users/habibutsu/.pyenv/versions/3.6.1/lib/python3.6/multiprocessing/popen_fork.py", line 74 in _launch
  File "/Users/habibutsu/.pyenv/versions/3.6.1/lib/python3.6/multiprocessing/popen_fork.py", line 20 in __init__
  File "/Users/habibutsu/.pyenv/versions/3.6.1/lib/python3.6/multiprocessing/context.py", line 277 in _Popen
  File "/Users/habibutsu/.pyenv/versions/3.6.1/lib/python3.6/multiprocessing/context.py", line 223 in _Popen
  File "/Users/habibutsu/.pyenv/versions/3.6.1/lib/python3.6/multiprocessing/process.py", line 105 in start
  File "/Users/habibutsu/.pyenv/versions/pylibs3.6.1/lib/python3.6/site-packages/japronto/app/__init__.py", line 228 in _run
  File "/Users/habibutsu/.pyenv/versions/pylibs3.6.1/lib/python3.6/site-packages/japronto/app/__init__.py", line 264 in run
  File "hello_db.py", line 30 in <module>
Worker crashed on signal SIGABRT!

Note after deleting string with initialization of database everything works fine

habibutsu commented 6 years ago

This can be produced even more simple way with using init_connection instead init_db

async def init_connection():
    app.reader, app.writer = await asyncio.open_connection(
        '127.0.0.1', 9000, loop=app.loop)
squeaky-pl commented 6 years ago

In asyncio it's catastrophic to try to share event loop between processes.

Japronto is a forking server, as in, to server http requests it would spawn a pool of processes each with it's own asyncio loop.

By running app.loop.run_until_complete in the parent process you initialize loop in the parent process and at the time you run app.run(debug=True) you gonna spawn a child process (to server requests) which would share the loop with the parent and lead to an error you are seeing.

Probably I could detect this common mistake and give a better error message.

Also this is a big limitation in design. There should be a post_fork call for each of the workers to open your connections, do per worker initialization. You could hacck one by looking in the code of run.

Also depending on what you wanna do temporarily to overcome this you could connect to the database when the first request comes in (in the child process).

Some of the related issues

https://github.com/squeaky-pl/japronto/issues/81