Развертывание Django+Channels+Heroku с Daphne: закрытие соединений Postgre

Мое веб-приложение Django развертывается нормально, однако мои подключения к базе данных достигают максимума после нескольких запросов к Postgres.

Вот мое понимание моей проблемы: соединения БД с Postgres открываются для каждого запроса, но не закрываются, и это приводит к тайм-ауту БД после достижения максимального количества подключений.

Документация Heroku по этой ошибке, но я думаю, что какое бы новое количество подключений ни было включено в результате обновления, эти подключения также быстро исчерпаются. (Или я ошибаюсь?)

pg:killall не является решением моей проблемы. Решение не должно быть ручным в этом приложении.

Я использовал документацию Heroku для параллельного доступа и подключения к БД в Django

Этот SO вопрос связан, но ответы не решают мою проблему

Ошибка отладки

FATAL: too many connections for role "DB_username"

Ошибка точки сбоя трассировки

conn = _connect(dsn, connection_factory=connection_factory, **kwasync) 

Запуск heroku pg:info --app appname в cmd

=== DATABASE_URL
Plan:                  Hobby-dev
Status:                Available
Connections:           20/20      <<<<<<<<<<<<<<<<<<<<<
PG Version:            12.4
Created:               2020-09-11 16:57 UTC
Data Size:             9.3 MB
Tables:                15
Rows:                  85/10000 (In compliance)
Fork/Follow:           Unsupported
Rollback:              Unsupported
Continuous Protection: Off
Add-on:                postgresql-infinite-00894

Вот что я пробовал:

  1. настройка dj_database_url.config(conn_max_age==0,...)
  2. Использование Pgbouncer для объединения соединений
  3. Отключение курсоров на стороне сервера

Settings.py

# PRODUCTION SETTINGS
MIDDLEWARE.append('whitenoise.middleware.WhiteNoiseMiddleware')
DATABASE_URL = os.environ['DATABASE_URL']
DISABLE_SERVER_SIDE_CURSORS = True
SECRET_KEY = os.environ.get('SECRET_KEY')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
#HEROKU SETTINGS
DATABASES['default'] = dj_database_url.config(conn_max_age=0, ssl_require=True)
django_heroku.settings(locals())

Установлен пакет сборки pgbouncer

Profile

web: bin/start-pgbouncer
web: daphne appname.asgi:application --port $PORT --bind 0.0.0.0 -v2
chatworker: python manage.py runworker --settings=appname.settings -v2

Это единственный Procfile, который работал у меня, я видел, как люди на других форумах жалуются, что вы не можете сделать 2 веб-дино, у меня это работает, pgbouncer успешно запускается при развертывании, daphne тоже работает. Использование web и web2 приводит к сбою приложения. При этом я открыт для любых предложений.

asgi.py, если вам интересно

import os
import django
from channels.routing import get_default_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Stonks.settings")
django.setup()
application = get_default_application()

Документация вокруг этого очень запутана, и другие форумы SO/code не предложили ничего, что я не пробовал. Выяснение того, является ли это проблемой кода Django, проблемой конфигурации Heroku или проблемой Daphne, также было бы чрезвычайно полезно.

образец Consumers.py

class StatsConsumer(AsyncConsumer):
    """Creates your stats websocket for each connected user"""
    async def websocket_connect(self, event):
        """Connects to HTML and JS websocket for your stats"""
        await self.send({
            "type": "websocket.accept"
        })
        player = self.scope['user']
        player_bids_list = await self.get_bids(player)
        result_dict = await self.find_stats(player_bids_list)
        await self.send({
            "type": "websocket.send",
            "text": json.dumps(result_dict),
        })

    async def websocket_receive(self, event):
        """Is called when yourStats.html pings server for updated data"""
        player = self.scope['user']
        player_bids_list = await self.get_bids(player)
        result_dict = await self.find_stats(player_bids_list)
        await self.send({
            "type": "websocket.send",
            "text": json.dumps(result_dict),
        })

    async def websocket_disconnect(self, event):
        """Is called when websocket disconnects"""
        print("disconnected", event)

    @database_sync_to_async
    def get_profile(self, user):
        """fetches user profile funds asynchronously"""
        return Profile.objects.get(user=user).funds
    
    @database_sync_to_async
    def get_bids(self, user):
        """fetches the connected user's bids and returns all bids based on id, asynchronously"""
        userid = User.objects.get(username=user).id
        result = Bid.objects.filter(username_id=userid)
        returnlist = []
        for i in result:
            returnlist.append(i.amount)
        return returnlist

person CMo    schedule 04.10.2020    source источник
comment
Вы декорируете функции, вызываемые потребителями, с помощью database_sync_to_async? Пожалуйста, покажите свой потребительский код   -  person Ken4scholars    schedule 05.10.2020
comment
Да (вопрос обновлен с Consumers.py). Я использовал database_sync_to_async для всех функций запросов. Проблема с db_connections возникает даже для страниц приложения, которые не используют веб-сокеты и не запускают Consumers.py.   -  person CMo    schedule 05.10.2020