2017-05-21 15 views
1

私のようなハンドラとaiohttpウェブサーバアプリケーションがあります。CancelledErrorはどのように典型的なaiohttp Webサーバーで処理する必要がありますか?

app["db"]がプールされたリソースのいくつかの種類である
async def handler(request): 
    async with request.app["db"].acquire() as db: 
     row = await query(db) 

    return aiohttp.web.json_response(row) 

aiopgaioredisを、今は関係ありません)。これは今日まで素晴らしい仕事をしていました。

File "/visio-longer/visio_longer/views/communicate/__init__.py", line 81, in legacy_communicate 
    async with request.app["db"].acquire() as db: 
:ここ

[2017-05-21 17:58:24,254] ERROR [aiohttp.server] Error handling request 
Traceback (most recent call last): 
    File "/virtualenv/lib/python3.6/site-packages/aiohttp/web_server.py", line 61, in handle_request 
    resp = yield from self._handler(request) 
    File "/virtualenv/lib/python3.6/site-packages/aiohttp/web.py", line 249, in _handle 
    resp = yield from handler(request) 
    File "/visio-longer/visio_longer/views/communicate/__init__.py", line 81, in legacy_communicate 
    async with request.app["db"].acquire() as db: 
    File "/virtualenv/src/aiopg/aiopg/utils.py", line 140, in __aenter__ 
    self._conn = yield from self._coro 
    File "/virtualenv/src/aiopg/aiopg/sa/engine.py", line 162, in _acquire 
    raw = yield from self._pool.acquire() 
    File "/virtualenv/src/aiopg/aiopg/utils.py", line 67, in __iter__ 
    resp = yield from self._coro 
    File "/virtualenv/src/aiopg/aiopg/pool.py", line 168, in _acquire 
    with (yield from self._cond): 
    File "/usr/lib/python3.6/asyncio/locks.py", line 67, in __iter__ 
    yield from self.acquire() 
    File "/usr/lib/python3.6/asyncio/locks.py", line 176, in acquire 
    yield from fut 
concurrent.futures._base.CancelledError 

キーポイントは、プールからデータベース接続を取得しながら、それが(タイムアウトによってクライアントの接続が切断)をCancelledErrorを受けていたあるように、すべてのクライアントがタイムアウトとアプリケーションログで切断開始した理由もなく痕跡を充填

私はプールの状態(sizefreesize)を5秒ごとに印刷していたコルーチンを実行していましたが、現在プールには十分な空き接続があります!

調査の時間は、プールのコンテキストマネージャ__atexit__を実行している間にCancelledErrorを受信するとプールへの接続を戻すプロセスを中止し、プールの誤動作を招いたという理論に終わりました。私はasyncpgでその動作を修正しているcommitを見つけました。aioredisには同様のコードが含まれています。私はawkward-looking fixaiopgにしました。これは役に立たなかった。私はまだaioredisaiopgの両方から同じエラーが出ていた。

asyncio.shieldで接続プールを使用するコードのすべての部分をラップすることにより、

async def handler(request): 
    async with request.app["db"].acquire() as db: 
     row = await query(db) 

    return aiohttp.web.json_response(row) 

を交換することで解決状況:

async def handler(request): 
    async def process(): 
     async with request.app["db"].acquire() as db: 
      row = await query(db) 

    return aiohttp.web.json_response(asyncio.shield(process(row))) 

中断された要求はまだ取得し帰国を含む(それらの末端に処理されたので、プールへのリソース)。

これはこの方法であるはずですか?今度は私のコードがひどく見えて、次回はプールをasyncio.shieldで包むのを忘れないという保証はありません。この問題を解決する適切な方法は何ですか(明らかにライブラリはそれ自体を修正できません)。

+0

https://vorpus.org/blog/control -c-handling-in-python-and-trio/interestingです。 –

答えて

0

これは他の例外とどのように異なるのか分かりません。 CanceledErrorは単なる別のエラーです。コンテキストマネージャのすべてのexit関数が実行されるので、リソースを解放する機会があります。 接続プールのロックに関連する未来がどうにかしてキャンセルされているような気がします。それは問題を抱えているようであり、キャンセルされるクライアント接続と無関係でなければならないようです。 私は、進行中のリクエストに対応するコルーチンが存在し、接続が失われたときにそのコルーチンが取り消されるべきだと仮定しています。 Task.cancelを呼び出すと、コルーチン内にCanceledErrorが呼び出されます。最終的にCanceledErrorがコルーチンから引き上げられたとすると、Taskの結果はCanceledErrorになります。 しかし、ロック関連の未来がどのように取り消されているのか分かりません。

コンテキストマネージャー、finallyブロック、またはその他のクリーンアップコードのexit関数で何かを待っている場合は、もっと注意が必要な場合があります。たとえば、あるリソースを返すためにexit関数でロックが必要な場合は、CanceledErrorをキャッチして(そして最終的には再起動する)whileループを使用するか、async.shieldを使用することができます。私はあなたの全体の要求の周りではなく、出口関数/クリーンナップハンドラの特定のコードの周りにのみ行うだろう。 また、リソースを返すためにコールバックを使用するか(loop.call_soon経由)、サイドクリーンアップコードで待っているよりも良い選択かもしれないと考えてください。 クリーンアップコードが待たれていない場合は、新しいCanceledErrorをその中で呼び出すことはできません。コンテキストマネージャのexit関数とfinallyブロックは、待機していなくても他の例外と同じであっても、CanceledErrorによってトリガーされる可能性があります。

+0

私は、CancelledErrorがコンテキストマネージャのexit関数の中で発生している状況を記述していました。それは、それが正常に終了し、プールを一貫した状態にするのを防ぎます。 – themylogin

+0

これはdb connection exit funcでどのように起こっていますか?プールへの接続を返すことは何も待たないだろうと私は思っています。 –

+0

ライブラリからのすべてのリターンツープール関数私は、空のプールでリソースが利用できるようになるのを待っているコルーチンに通知するために使用されているロック条件を待っています。 このコードのシールドは役に立たなかった多分あなたはhttps://github.com/themylogin/aiopg/commit/cf2580671eabd880e8826d42ac04f5a3380447a7を見て、欠けているものを考えているのでしょうか?) – themylogin

関連する問題