2014-09-02 4 views
2

Я запускаю PostgreSQL 9.3 и SQLAlchemy 0.8.2 и испытываю утечку базы данных. После развертывания приложение потребляет около 240 подключений. В течение следующих 30 часов это число постепенно увеличивается до 500, когда PostgreSQL начнет отменять соединения.Утечка соединений с базами данных: PostgreSQL, SQLAlchemy, Flask

Я использую SQLAlchemy thread-local sessions:

from sqlalchemy import orm, create_engine 

engine = create_engine(os.environ['DATABASE_URL'], echo=False) 
Session = orm.scoped_session(orm.sessionmaker(engine)) 

Для веб-приложение Flask, то .remove() вызов Session прокси-объекта отправить во время запроса демонтажа:

@app.teardown_request 
def teardown_request(exception=None): 
    if not app.testing: 
     Session.remove() 

Это должно быть таким же, как какой Flask-SQLAlchemy делает.

У меня также есть некоторые периодические задачи, которые выполняются в цикле, и я называю .remove() для каждой итерации цикла:

def run_forever(): 
    while True: 
     do_stuff(Session) 
     Session.remove() 

Что я делаю неправильно, что может привести к утечке связи?

+1

Есть ли причина, по которой вы отметили это с помощью фляжки-sqlalchemy, но не используете фляж-sqlalchemy, чтобы сменить сеанс? – davidism

+2

Это также очень плохой способ запуска фоновых задач. Рассмотрите использование сельдерея, есть образец для получения контекста приложения и сеанса db в задаче в документах Flask. – davidism

+1

Как развертывается приложение? –

ответ

0

Прежде всего, это действительно очень плохой способ запуска фоновых задач. Попробуйте любой планировщик ASync, например, сельдерей.

Не на 100% уверен, что это немного догадка, основанная на предоставленной информации, но мне интересно, запускает ли каждая загрузка страницы новое соединение db, которое затем прослушивает уведомления. Если это так, мне интересно, действительно ли соединение db удаляется из пула и поэтому создается при загрузке следующей страницы.

Если это так, моей рекомендацией было бы иметь отдельный дескриптор базы данных DBI, предназначенный для прослушивания уведомлений, чтобы они не были активны в очереди. Это может быть сделано вне вашего рабочего процесса.

Также

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

3

Если я правильно помню из своих экспериментов с SQLAlchemy, то scoped_session() используется для создания сеансов, к которым вы можете получить доступ из нескольких мест. То есть вы создаете сеанс в одном методе и используете его в другом, не передавая явным образом объект сеанса. Он делает это, сохраняя список сеансов и связывая их с «идентификатором области». По умолчанию для получения идентификатора области видимости используется текущий идентификатор потока; поэтому у вас есть сеанс на поток. Вы можете поставить scopefunc предоставить - например - один ID по запросу:

# This is (approx.) what flask-sqlalchemy does: 
from flask import _request_ctx_stack as context_stack 

Session = orm.scoped_session(orm.sessionmaker(engine), 
          scopefunc=context_stack.__ident_func__) 

Кроме того, обратите внимание на других ответы и комментариев о делать фоновые задачи.

+1

Также обратите внимание, что из 0.9 есть '_app_ctx_stack', что больше подходит в большинстве случаев. https://github.com/mitsuhiko/flask/blob/7f3867491570746a4c14bdaa5bd59ec1b64cbfea/docs/upgrading.rst#version-09 – awsum

Смежные вопросы