Проблема с устаревшими данными Flask / SqlAlchemy

У меня есть следующая настройка, для которой в session.query () SqlAlchemy возвращает устаревшие данные:

Веб-приложение, работающее на Flask с супервизором Gunicorn +. одна из служб составлена ​​таким образом:

  • app.py:

    @app.route('/api/generatepoinvoice', methods=["POST"])
    @auth.login_required
    def generate_po_invoice():
    try:
       po_id = request.json['po_id']
       email = request.json['email']
       return jsonify(response=POInvoiceGenerator.get_invoice(po_id, email))
    except Exception as ex:
        app.logger.error("generate_po_invoice(): " + ex.message)
    

в другой папке у меня есть материалы, связанные с базой данных:

DatabaseModels (папка)

| -> Model.py

| -> Connection.py

это то, что содержится в файле connection.py:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine(DB_BASE_URI, isolation_level = "READ COMMITTED")
Session = scoped_session(sessionmaker(bind=engine))
session = Session()
Base = declarative_base()

и это отрывок из файла model.py:

from DatabaseModels.Connection import Base
from sqlalchemy import Column, String, etc...

class Po(Base):
    __tablename__ = 'PLC_PO'

    id = Column("POId", Integer, primary_key=True)
    code = Column("POCode", String(50))
    etc...

Тогда у меня есть еще один файл POInvoiceGenerator.py который содержит вызов базы данных для получения некоторых данных:

import DatabaseModels.Connection as connection
import DatabaseModels.model as model
def get_invoice(po_code, email):
    try:
        po_code = po_code.strip()
        PLCConnection.session.expire_all()
        po = connection.session.query(model.Po).filter(model.Po.code == po_code).first()

    except Exception as ex:
        logger.error("get_invoice(): " + ex.message)

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

Мой первый подход заключался в том, чтобы добавить изоляцию_level = "READ COMMITTED" в объявление движка, а затем создать сеанс с заданной областью действия, но чтение устаревших данных продолжает добавляться.

Есть ли кто-нибудь, кто имел какое-либо представление о том, что моя установка неверна (сеанс и модель повторно используются в нескольких методах и файлах)

Заранее спасибо.

🤔 А знаете ли вы, что...
Python популярен в анализе данных и машинном обучении с помощью библиотеки scikit-learn.


991
2

Ответы:

Решено

даже если решение, на который указывает @TonyMountax, кажется действительным и заставил меня обнаружить то, чего я не знал о SqlAlchemy, в конце концов я выбрал что-то другое.

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

Я добавил в свой код NullPool:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.pool import NullPool

engine = create_engine(DB_URI, isolation_level = "READ COMMITTED", poolclass=NullPool)
Session = scoped_session(sessionmaker(bind=engine))
session = Session()

а затем я вызываю закрытие сеанса для каждого запроса, который я делаю:

session.query("some query..")
session.close()

это заставит SqlAlchemy каждый раз создавать новое соединение и получать свежие данные из базы данных.

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


То, как вы создаете экземпляры соединений с базой данных, означает, что они повторно используются для следующего запроса, и у них есть какое-то состояние, оставшееся от предыдущего запроса. SQLAlchemy использует концепцию сеансов для взаимодействия с базой данных, так что ваши данные не изменяются резко в одном запросе, даже если вы выполняете один и тот же запрос дважды. Это имеет смысл, когда вы используете функции запросов ORM. Например, если вы дважды запросили len(User.friendlist) в течение одного сеанса, но во время запроса был принят запрос на добавление в друзья, то он все равно будет показывать тот же номер в обоих местах.

Чтобы исправить это, вы должны настроить сеанс при первом запросе, а затем разорвать его, когда запрос будет завершен. Сделать это нетривиально, но есть хорошо зарекомендовавший себя проект, который уже делает это: Flask-SQLAlchemy. Это от Pallets, разработчиков самого Flask и Jinja2.