みーのぺーじ

みーが趣味でやっているPCやソフトウェアについて.Python, Javascript, Processing, Unityなど.

Djangoの開発サーバーではデータベース接続を維持してはいけない

Djangoのrunserverコマンドを使用して開発している時に,たくさんのリクエストを行うとデータベース接続数が次第に増えていき,

FATAL: sorry, too many clients already`

というエラーが発生する現象に遭遇した時のまとめです.

環境

  • Django 3.1.6
    • 'CONN_MAX_AGE': 120
    • command: python manage.py runserver
  • Postgres 12
    • max_connections 100

Postgresの状況を確認する

データベースの起動コマンドに以下のオプションを追加して,Postgresの接続ログを有効にします.

postgres -c log_destination=stderr -c log_connections=on -c log_disconnections=on

データベースの接続数を取得するには,以下のクエリを実行します.

SELECT count(*) FROM pg_stat_activity;

データベースの設定を確認するには,以下のクエリを実行します.

SELECT * FROM pg_settings;

max_connectionsという値が,最大接続数を表します.

接続ログを確認する

データベースの接続を再利用するために,Djangoの'CONN_MAX_AGE'を有効にした場合と無効にした場合を比較しました.

'CONN_MAX_AGE': 120

CONN_MAX_AGEを1以上に設定すると,以下のように指定した時間が経過すると切断されている場合もありましたが,既存の接続を再利用せずに新たな接続が徐々に作成されていく状態でした.

2021-02-19 07:24:10.978 UTC [91] LOG:  connection received: host=172.20.0.7 port=47576
2021-02-19 07:24:10.980 UTC [91] LOG:  connection authorized: user=t database=t
...
2021-02-19 07:26:11.387 UTC [91] LOG:  disconnection: session time: 0:02:00.410 user=t database=t host=172.20.0.7 port=47576

'CONN_MAX_AGE': 0

設定通り,データベースに接続した直後に切断されています.全体としてデータベースの接続数は一定でした.

2021-02-19 07:39:12.753 UTC [119] LOG:  connection received: host=172.21.0.4 port=43554
2021-02-19 07:39:12.755 UTC [119] LOG:  connection authorized: user=t database=t
2021-02-19 07:39:12.771 UTC [119] LOG:  disconnection: session time: 0:00:00.017 user=t database=t host=172.21.0.4 port=43554

runserverの仕様

Djangoのドキュメントに記載がありました.

The development server creates a new thread for each request it handles, negating the effect of persistent connections. Don’t enable them during development.

Databases | Django documentation | Django

開発サーバーの設定では,'CONN_MAX_AGE': 0と設定するべきのようです.

uWSGI

uWSGIでは,psycopg2の非同期処理に対応しているため,CONN_MAX_AGEを有効して接続を再利用することができます.

uGreen – uWSGI Green Threads — uWSGI 2.0 documentation

開発環境と本番環境で設定が異なるため,注意が必要です.