У меня есть веб-приложение 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