LoginSignup
2
1

More than 3 years have passed since last update.

Django内部でThreadを使う場合に意図せぬコネクションリークが発生

Posted at

はじめに

Djangoの内部で時間のかかるリクエストを処理したり、バッチ処理拡張モジュールを自作する場合などに並列処理実装として自分でスレッドを作成し、利用することがある。
今回このケースで長期運用していると Too many connections が発生したので、その調査結果をメモ。

原因

  • DjangoのDBコネクションは スレッドごと に作成される
    • DBアクセスが発生する場合のみ。 モデルへのアクセスがない別スレッドはそもそもDBコネクションを生成しないので問題ない
    • コネクションを利用する際、スレッドローカル相当の機能内にコネクションが保持されており、ここに無い場合は初回アクセス時にコネクションを生成する
    • https://github.com/django/django/blob/master/django/db/utils.py#L136 の ConnectionHandlerが実体で self._connections 内にコネクションを格納
  • 自分で作った別スレッドの中でDjangoのモデルを使う場合にはDBコネクション管理を意識する必要がある
  • スレッドローカル実装の定期解放処理時に解放される場合もあるが、実装が入り組んでいる場合解放されないこともあるし、待っている間にも上限は近づいてくる

対処

I/Oコストはかかるものの、スレッド終了時にコネクションを close_all で明示的に閉じることでコネクションリークは防ぐことができる。
とはいえ、そもそも何らかの理由(おそらく処理時間の問題)があって別スレッドを立てているはずなので、コネクションの作成・解放はそこまで大きな問題にはならないはず。

from django.db import connections

def target():
    ''' 別スレッドで実行される関数 '''
    try:
        # 処理
    finally:
        connections.close_all()

ちなみに、close_all は "同一スレッド内の全てのDBを閉じる" 関数である(Djangoは設定に DATABASES として複数のDBを設定することが可能)。 別スレッドのDBを閉じたりはしない。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1