SQLAlchemy пытается получить доступ к таблице в неправильной базе данных - PullRequest
2 голосов
/ 03 февраля 2012

Мы используем 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 это все о безопасности потоков?

...