Мы используем CherryPy и SQLAlchemy для создания нашего веб-приложения, и все было хорошо, пока мы не проверили с двумя одновременно работающими пользователями - тогда все пошло неправильно Не очень хорошо для веб-приложения, поэтому я буду очень признателен, если кто-нибудь сможет пролить свет на это.
TL; DR
Мы получаем следующую ошибку примерно в 10% случаев, когда два пользователя используют наш сайт (но обращаются к разным базам данных) одновременно:
ProgrammingError: (ProgrammingError) (1146, "Table 'test_one.other_child_entity' doesn't exist")
Эта таблица отсутствует в этой базе данных, поэтому ошибка имеет смысл, но проблема в том, что SQLAlchemy не должен искать таблицу в этой базе данных.
Я воспроизвел ошибку в примере здесь https://gist.github.com/1729817
Объяснение
Мы разрабатываем приложение, которое очень динамично и основано на шаблоне entity_name
, найденном в http://www.sqlalchemy.org/trac/wiki/UsageRecipes/EntityName
С тех пор мы развили эту идею, чтобы она сохраняла сущности в разных базах данных в зависимости от того, каким пользователем вы вошли в систему. Это потому, что каждый пользователь в системе имеет свою собственную базу данных и может создавать свои собственные сущности (таблицы). Для этого мы расширяем базовый объект для каждой базы данных, а затем расширяем этот новый объект для каждого дополнительного объекта, который они создают в своей базе данных.
Когда приложение запускается, мы создаем словарь, содержащий движок, метаданные, классы и таблицы всех этих баз данных и отражающие все метаданные. Когда пользователь входит в систему, он получает доступ к ней.
Когда два пользователя одновременно получают доступ к сайту, что-то идет не так, и SQLAlchemy заканчивает тем, что ищет таблицы в неправильной базе данных. Я предполагаю, что это связано с многопоточностью, но, насколько я вижу, мы следуем всем правилам, когда дело доходит до сессий (CP и SQLA), движков, метаданных, таблиц и картографов.
Если бы кто-нибудь мог привести мой пример (https://gist.github.com/1729817), быстрый взгляд и указать на любые явные проблемы, которые были бы великолепны.
Обновление
Я могу решить проблему, изменив код для использования моего собственного настраиваемого сеансового маршрутизатора, например:
# Thank you zzzeek (http://techspot.zzzeek.org/2012/01/11/django-style-database-routers-in-sqlalchemy/)
class RoutingSession(Session):
def get_bind(self, mapper = None, clause = None):
return databases[cherrypy.session.get('database')]['engine']
А потом:
Session = scoped_session(sessionmaker(autoflush = True, autocommit = False, class_ = RoutingSession))
Так что просто жестко запрограммируйте его, чтобы вернуть механизм, связанный с базой данных, установленной в сеансе. Это хорошая новость, но теперь я хочу знать, почему мой оригинальный код не работал. Либо я делаю это неправильно, либо следующий код не совсем безопасен:
# Before each request (but after the session tool)
def before_request_body():
if cherrypy.session.get('logged_in', None) is True:
# Configure the DB session for this thread to point to the correct DB
Session.configure(bind = databases[cherrypy.session.get('database')]['engine'])
Полагаю, что привязка, которая здесь происходит, была перезаписана пользователем в другом потоке, что странно, потому что я подумал, что scoped_session
это все о безопасности потоков?