はじめに
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を閉じたりはしない。