2012-05-02 3 views
0

Это сложно описать или показать много кода, но я попробую. По сути, у меня многопоточное настольное приложение, которое часто обрабатывает добавление/удаление/изменение таблиц в потоках. Из того, что я читал, я должен использовать scoped_session и передавать это для различных потоков, чтобы выполнить работу (я думаю?). Вот некоторые примеры базового кода:Режиссированные сессии, заканчивающиеся на SQLAlchemy?

class SQL(): 
    def __init__(self):   
     self.db = create_engine('mysql+mysqldb://thesqlserver') 
     self.metadata = MetaData(self.db) 
     self.SessionObj = scoped_session(sessionmaker(bind=self.db, autoflush=True)) 

db = SQL() 
session = db.SessionObj() 
someObj = Obj(val, val2) 
session.add(someObj) 
session.commit() 

Этот класс является тем, что я использую в качестве общего доступа к SQL-материалам. После создания нового сеанса, выполняя запрос и обновление/добавление к нему, на session.commit(), я получаю следующее сообщение об ошибке:

Traceback (most recent call last): 
    File "core\taskHandler.pyc", line 42, in run 
    File "core\taskHandler.pyc", line 184, in addTasks 
    File "core\sqlHandler.pyc", line 35, in commit 
    File "sqlalchemy\orm\session.pyc", line 624, in rollback 
    File "sqlalchemy\orm\session.pyc", line 338, in rollback 
    File "sqlalchemy\orm\session.pyc", line 369, in _rollback_impl 
    File "sqlalchemy\orm\session.pyc", line 239, in _restore_snapshot 
    File "sqlalchemy\orm\state.pyc", line 252, in expire 
AttributeError: 'NoneType' object has no attribute 'expire' 

Тогда следующий, если очередная попытка SQL проходит через:

Traceback (most recent call last): 
    File "core\taskHandler.pyc", line 44, in run 
    File "core\taskHandler.pyc", line 196, in deleteTasks 
    File "sqlalchemy\orm\query.pyc", line 2164, in scalar 
    File "sqlalchemy\orm\query.pyc", line 2133, in one 
    File "sqlalchemy\orm\query.pyc", line 2176, in __iter__ 
    File "sqlalchemy\orm\query.pyc", line 2189, in _execute_and_instances 
    File "sqlalchemy\orm\query.pyc", line 2180, in _connection_from_session 
    File "sqlalchemy\orm\session.pyc", line 729, in connection 
    File "sqlalchemy\orm\session.pyc", line 733, in _connection_for_bind 
    File "sqlalchemy\orm\session.pyc", line 249, in _connection_for_bind 
    File "sqlalchemy\orm\session.pyc", line 177, in _assert_is_active 
sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back by a nested rollback() call. To begin a new transaction, issue Session.rollback() first. 

Это примерно столько, сколько я знаю, и я думаю, что лучшее, что я могу описать. Любые идеи о том, что я есть предположительно здесь делать? Это все грязь для меня. Заранее спасибо!

ответ

1

Самое смешное, что вы пропустили самую критическую часть ответа, которую вы «разорвали код из», то есть есть функция Python в середине, которая выполняет некоторую абстрактную операцию (она помечена как func()). Этот код иллюстрирует транзакционную оболочку для функции, и в приведенном выше примере вместо этого у вас есть метод объекта commit(), который иначе не вызывает никаких дополнительных операций с Session.

Здесь у вас есть объект для хранения сеанса, называемый SQL(), который на самом деле не добавляет никакой пользы в вашу программу и делает его излишне сложным и, вероятно, также является источником проблемы. Если ваше приложение не намерено подключаться ко многим различным базам данных в разное время и использовать объекты SQL() для представления этого состояния, в построении класса «SQL», на котором он «застрял», не так много пользы. Просто вставьте двигатель в модуль где-нибудь, а также ваш scoped_session().

Двигатель и scoped_session представляет собой образец под названием factory pattern - они являются объектами, которые создают какой-то другой полезный объект, в данном случае scoped_session создает Session и Engine используется внутренне Session создать Connection, с помощью которой говорить к базе данных. Не имеет смысла размещать объект Session в качестве члена брата, а также Engine и scoped_session - вы будете носить либо фабрики (Engine, и scoped_session), либо сам объект, который они создают (Session) которые все зависит от того, что вы пытаетесь сделать.

Session сам, помните, здесь мы говорим о вещи фабрики создают (Session), а не заводы сами (Engine и scoped_session), является не в малейшей Потокобезопасная. Это то, что вы обычно создаете только локально для функции - она ​​не должна быть глобальной, и если вы фактически используете один объект SQL() по потокам, это, вероятно, проблема здесь. Фактическая ошибка, которую вы получаете, я не совсем уверен, что это такое, и я мог бы только лучше понять, знаю ли я, какая именно версия SQLAlchemy используется здесь, хотя случайность ошибки говорит о том, что у вас есть какая-то где что-то становится ничем в одном потоке, поскольку другой ожидает, что тот же объект будет присутствовать.

Так что вам нужно установить в этой программе, когда начинается конкретный поток выполнения, что ему нужно делать с базой данных по мере ее поступления, а затем, когда она заканчивается. Когда вы можете установить для него согласованный шаблон, вы бы связали один Session с этим потоком, который идет на весь срок службы этого потока, и никогда не делился. Все объекты, созданные этим сеансом, также не должны использоваться для других потоков - это расширения состояния сеанса. Если у вас есть «рабочие потоки», эти рабочие потоки должны загружать свои собственные данные по мере необходимости в рамках своей собственной сессии. Сессия представляет собой транзакцию в реальном времени, и вы обычно хотите, чтобы транзакции были локальными для одного потока.

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

+0

Shucks. Ну, это потрясающий ответ, и я высоко ценю то, о чем вы сказали в пятом абзаце. Итак, чтобы подвести итог, то, что я должен делать, это создать новый сеанс для каждой серии задач, которые необходимо выполнить? В частности, я должен определить _new_ (сеанс или scoped_session?) Для каждого рабочего потока? По сути, у меня есть один большой поток, который запускает графический интерфейс и выполняет некоторые небольшие работы с SQL, а затем еще один рабочий поток, который обрабатывает более сложные/более длительные SQL-запросы/добавления/обновления. Еще раз спасибо за ответ! – Cryptite

+0

Также, используя SQLAlchemy 0.7.7. Я думал, что понял, что вы сказали, и обновил код OP, чтобы отразить то, что я сейчас пытаюсь сделать, но это непременно приводит к тому, что _Nonetype не имеет атрибута «expire» при каждом выполнении 'session.commit()'. Выполняется фактическая задача SQL, и таблица прекрасно обновляется, но фиксация по-прежнему дает ошибку истечения срока действия. – Cryptite

+0

, если у вас что-то непротиворечивое, тогда используйте его в краткой (очень короткой, очень ОЧЕНЬ короткой, не посторонней детали) тестовом случае и опубликуйте его в списке рассылки, я дам ему прогон и сделаю предложения. – zzzeek

Смежные вопросы