Size: a a a

2020 March 28

РБ

Руслан Базяк in Python Flask
Агент Печенька
Да, но я тебе писал способ без скулёвых запросов руками.
да, согласен ))
источник

РБ

Руслан Базяк in Python Flask
спасибо ещё раз )
источник

I

Ispipilyu in Python Flask
Привет))
Я зашел спросить на счет проблемы связанной с sqlalchemy, но пока я писал вопрос, до меня дошло - весь. Этот. День. Я. Обновлял. Бд. Экзепляром. Приложения. Запущенным. Утром...
Простите, крик души(((
источник

АП

Агент Печенька in Python Flask
Ispipilyu
Привет))
Я зашел спросить на счет проблемы связанной с sqlalchemy, но пока я писал вопрос, до меня дошло - весь. Этот. День. Я. Обновлял. Бд. Экзепляром. Приложения. Запущенным. Утром...
Простите, крик души(((
Классика.
источник
2020 March 29

ВГ

Владимир Гончар in Python Flask
/
источник

MB

Max Block in Python Flask
Приветствую!

Помогите настроить сессии SQLAlchemy для следующей архитектуры:

1) Имеется веб приложение. Оно на flask. В нем есть работа с PostgreSQL в виде обычных серверных запросов-ответов.
2) Помимо работы с http запросами, у приложения есть работа с БД через задачи в бегкраунде. Это никакая не Celery, это постоянно крутящийся в бесконечном цикле вызов множества методов в отдельных потоках (стандартные питонячьи потоки из пакета threading). Т.е. в одном процессе у нас есть фласк + эти вот бекграундные задачи в отдельных потоках. И это все должно использовать одну и туже сущность БД сессии
3) Поддержка транзакций в полном их смысле не нужна. Т.е. если часть данных в случае ошибки не сохранится, это не важно.
4) Работа с БД в приложении происходит внутри модулей services через глобальную переменную db_session. Выглядит это так:
from my_app import db_session

def do_work():
 db_session.add(MyModel(…))
 db_session.commit()


Я начал использовать scoped_session. И это идеальное решение для работы именно с фласком. Так как на каждый запрос у нас получается своя сессия, и мы легко через хендлеры фласка можем закрыть сессию БД и сделать rollback в случае ошибки.

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

Th
is Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (psycopg2.errors.InFailedSqlTransaction) current transaction is aborted, commands ignored until end of transaction block

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

Есть очевидное решение, это все те места, где я работаю с БД, обрабатывать на предмет ошибки:

from
 my_app import db_session

def do_work():
  try:
    db_session.add(MyModel(…))
    db_session.commit()
  except SQLAlchemyError:
   db_session.rollback()

Но
это делать не хочется, так как таких методов очень много. Хочется как-то глобально растроить так, чтобы в случае ошибки в БД, сессия смогла бы сделать rollback.

* Может быть есть какой-то флаг при настройки сессии, что-то типа: create_sesson(autorollback=True)
* Может быть есть у sqlalchemy эвенты. И может быть можно сделать хендлер на ошибки, и уже внутри этого херндлера сделать db_session.rollback()
источник

T

Tishka17 in Python Flask
Max Block
Приветствую!

Помогите настроить сессии SQLAlchemy для следующей архитектуры:

1) Имеется веб приложение. Оно на flask. В нем есть работа с PostgreSQL в виде обычных серверных запросов-ответов.
2) Помимо работы с http запросами, у приложения есть работа с БД через задачи в бегкраунде. Это никакая не Celery, это постоянно крутящийся в бесконечном цикле вызов множества методов в отдельных потоках (стандартные питонячьи потоки из пакета threading). Т.е. в одном процессе у нас есть фласк + эти вот бекграундные задачи в отдельных потоках. И это все должно использовать одну и туже сущность БД сессии
3) Поддержка транзакций в полном их смысле не нужна. Т.е. если часть данных в случае ошибки не сохранится, это не важно.
4) Работа с БД в приложении происходит внутри модулей services через глобальную переменную db_session. Выглядит это так:
from my_app import db_session

def do_work():
 db_session.add(MyModel(…))
 db_session.commit()


Я начал использовать scoped_session. И это идеальное решение для работы именно с фласком. Так как на каждый запрос у нас получается своя сессия, и мы легко через хендлеры фласка можем закрыть сессию БД и сделать rollback в случае ошибки.

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

Th
is Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (psycopg2.errors.InFailedSqlTransaction) current transaction is aborted, commands ignored until end of transaction block

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

Есть очевидное решение, это все те места, где я работаю с БД, обрабатывать на предмет ошибки:

from
 my_app import db_session

def do_work():
  try:
    db_session.add(MyModel(…))
    db_session.commit()
  except SQLAlchemyError:
   db_session.rollback()

Но
это делать не хочется, так как таких методов очень много. Хочется как-то глобально растроить так, чтобы в случае ошибки в БД, сессия смогла бы сделать rollback.

* Может быть есть какой-то флаг при настройки сессии, что-то типа: create_sesson(autorollback=True)
* Может быть есть у sqlalchemy эвенты. И может быть можно сделать хендлер на ошибки, и уже внутри этого херндлера сделать db_session.rollback()
На каждый поток держи одну сессию, ну
источник

T

Tishka17 in Python Flask
Очевидное и по моему мнению единственно правильное решение - на каждый таск получать сессию и закрывать в конце работы, при необходимости обработав ошибки
источник

T

Tishka17 in Python Flask
Закрывать == возвращать в пул в случае алхимии
источник

T

Tishka17 in Python Flask
Глобальные переменные естественно говно и надо выкинуть
источник

AM

Artur Mustafin in Python Flask
Tishka17
Глобальные переменные естественно говно и надо выкинуть
Соглашусь
источник

T

Tishka17 in Python Flask
В случае фласка вместо таска у тебя - запрос. До запроса берешь сессию из пула, после запроса - возвращаешь. scoped_session конечно помогает это юзать, но в целом не исключает необходимость закрывать её
источник

MB

Max Block in Python Flask
Tishka17
Очевидное и по моему мнению единственно правильное решение - на каждый таск получать сессию и закрывать в конце работы, при необходимости обработав ошибки
С этим не поспорить, но вот как-то в джанге ж сделали так, что там используется глобальная переменная с БД. И каким-то образом все под капотом они это обрабатывают.

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

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

С глобальными переменными — мне надо их :)

У меня не стандартные задачи. Мне не надо писать тесты, мне надо как можно быстрее сделать работающий прототип. И глобальные переменные мне при этом помогают.
источник

MB

Max Block in Python Flask
Tishka17
В случае фласка вместо таска у тебя - запрос. До запроса берешь сессию из пула, после запроса - возвращаешь. scoped_session конечно помогает это юзать, но в целом не исключает необходимость закрывать её
с scoped_session все хорошо работает когда это касается фласка. Но вот что делать в той части приложения, где в бекгранде постоянно что-то крутится в в пультипотоках.

Я конечно могу делать там такое:
@contextmanager
def get_session():
   session = get_the_session_one_way_or_another()

   try:
       yield session
   except:
       session.rollback()
       raise
   else:
       session.commit()


А потом такое:
wi
th get_session() as session:
   # do something with the session
,
но это все увеличивает кол-во кода. В джанге  можно без этого контекстного менеджера. Как-то они ж сделали так, что можно не париться по поводу psycopg2.errors.InFailedSqlTransaction
источник

T

Tishka17 in Python Flask
Max Block
С этим не поспорить, но вот как-то в джанге ж сделали так, что там используется глобальная переменная с БД. И каким-то образом все под капотом они это обрабатывают.

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

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

С глобальными переменными — мне надо их :)

У меня не стандартные задачи. Мне не надо писать тесты, мне надо как можно быстрее сделать работающий прототип. И глобальные переменные мне при этом помогают.
В джанге для решения проблем глобальных переменных ещё очень много чего сделали
источник

T

Tishka17 in Python Flask
Прям очень много костылей у них из-за этого
источник

T

Tishka17 in Python Flask
Я думаю, там так же в конце запроса сессия закрывается просто.
источник

MB

Max Block in Python Flask
Tishka17
Прям очень много костылей у них из-за этого
А может быть можно настроить саму PostgreSQL, чтобы она перестала жаловаться на то что в сесси случилась ошибка? Мне на самом деле не нужны полноценные транзакции в БД. Я мог бы очень хорошо жить на MongoDB, что я раньше и делал. Мне очень подходит для моих задач монга именно в этом плане: если там что-то случилось в одном запросе плохое, то это вообще никак не отразится на последующих запросах к монге.

Но я вынужден уйти от монги, так как мне начали требоваться миграции + данных стало много, и постгря их обрабатывает быстрее. Но вот как убрать бы мне эту проблему с psycopg2.errors.InFailedSqlTransaction

А у SQLAlchemy, есть у нее какие-то хендлеры на ошибки? Возможно ли там следать перехват psycopg2.errors.InFailedSqlTransaction, и внутри этого хендлера сделать db_session.rollback()?
источник

MB

Max Block in Python Flask
Tishka17
Я думаю, там так же в конце запроса сессия закрывается просто.
Скорее там есть еще какие-то обработчики дополнительные. Так как когда у меня в отдельных потоках (которые не связаны с циклом request-response) случаются ошибки, то они потом логируются, и я в джанге никогда не получал psycopg2.errors.InFailedSqlTransaction. Где-то там происходит под капотом rollback наверное.
источник

MB

Max Block in Python Flask
А еще, может быть в питоне появилась возможность написать глобальный обработчик ошибок? Я пару лет назад пытался это сделать по аналогии с node.js. Но была у питона какая-то проблема, по которой этот обработчик было нельзя написать. У питона есть какие-то способы ловить глобально исключения, но они не работали с мультипоточностью. Но вроде тогда я читал, что собирались добавить поддержку и мультипоточности.

Если можно написать такой глобальный хендлер, то там можно ловить эти ошибки и делать db_session.rollback()
источник