Flask и SQLAlchemy вызывают много IDLE в транзакционных связях в PostgreSQL

У меня есть веб-приложение Flask, которое использует SQLAlchemy для доступа к базе данных PostgreSQL.

Когда я запускаю приложение, в PostgreSQL сразу создается соединение "в транзакции".

Когда приложение используется некоторое время, некоторые из этих соединений появляются в pg_stat_activity.

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

Я прочитал, что это может произойти, если я вернусь из функции view, которая использует базу данных, прежде чем закрывать сеанс db. Поэтому, чтобы избежать этой проблемы, я создал следующий декоратор:

@app.teardown_appcontext
def shotdown_session(exception=None):
    db.session.remove()

Это должно привести к закрытию всех сеансов после каждого запроса и эффективно избежать проблем с подключением "в транзакции".

К сожалению, это не имеет никакого эффекта.

Итак, как я действительно решаю эту проблему?

ОБНОВЛЕНИЕ:

Я должен добавить, что я подтвердил, что моя функция декоратора фактически запущена. Я проверил это, добавив к нему печать:

@app.teardown_appcontext
def shotdown_session(exception=None):
    print "@app.teardown_appcontext: shotdown_session()"
    db.session.remove()

Я также проверил, что он действительно запускается ПОСЛЕ возврата функции просмотра, добавляя печать к функции просмотра:

[...]
products = db.session.query(...).all()
print "BEFORE RETURN"
return render_template("show_products.html", products=products)

Это создает строки журнала, подобные этим:

 * Running on http://0.0.0.0:5000/
 * Restarting with reloader
BEFORE RETURN
@app.teardown_appcontext: shotdown_session()
10.0.0.100 - - [03/Dec/2014 13:41:30] "GET /product/51 HTTP/1.1" 200 -

Я также просмотрел код и добавил вызов db.session.remove() перед каждым возвратом в каждой функции, используя db.session. Это избавляет от транзакции, однако это также вызывает проблемы. Я передаю объекты модели SQLAlchemy из базы данных вместе с шаблонами. Некоторые шаблоны затем выполняют некоторые операции над этими объектами модели, что приводит к сбою приложения, поскольку объект больше не привязан к сеансу.

EDIT 2014-12-08:

Настройка соединения приведена здесь:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from flask_sqlalchemy import SQLAlchemy

from config import cfg

engine = create_engine(cfg["db_uri"], echo=False, pool_size=10)
db = SQLAlchemy()
Base = db.Model
Session = scoped_session(sessionmaker(bind=engine))

Структура всего приложения можно найти здесь: http://paste.yt/p3219.html

Ответ 1

Я видел такую ситуацию, когда вы запускаете Flask в режиме отладки. Если ваш код выдает исключение и включается отладчик, транзакция никогда не будет "откатана" или "удалена". В результате сеанс, который использовался для неудавшегося запроса, никогда не возвращается в пул.

Решение - отключить режим отладки.

EDIT:

Есть еще одно обстоятельство, когда я видел, как это произошло. Если у вас есть код, который выполняется автономно (то есть не является частью транзакции HTTP - как независимый поток, запускаемый и порождаемый при запуске приложения Flask), он обычно включает в себя спящий режим. Если вы получите доступ к сеансу перед сном, то во время сна вы закончите с зависшей транзакцией.

Другая возможность - доступ к сеансу из функции создания приложения. Если вы это сделаете, убедитесь, что .remove() это. В противном случае этот сеанс может остаться в основном потоке в приложении gevent.

Ответ 2

from sqlalchemy.pool import NullPool

использовать NullPoll, поскольку poolclass решил проблему для меня. Не уверен почему.