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 решил проблему для меня. Не уверен почему.