Является ли пул соединений в sqlalchemy потокобезопасным?

В документации сказано, что пул соединений также не предназначен для многопоточности:

It’s critical that when using a connection pool, and by extension when using an Engine created via create_engine(), that the pooled connections are not shared to a forked process. TCP connections are represented as file descriptors, which usually work across process boundaries, meaning this will cause concurrent access to the file descriptor on behalf of two or more entirely independent Python interpreter states.

Насколько я понимаю, если я создам пул соединений:

self.engine = create_engine('postgresql://{user}:{password}@{host}:{port}/{db}'.format(
    user=Configuration().get(section='repository', option='user'),
    password=Configuration().get(section='repository', option='password'),
    host=Configuration().get(section='repository', option='host'),
    port=Configuration().get(section='repository', option='port'),
    db=Configuration().get(section='repository', option='database')
), echo=False, pool_size=3)

self.session = sessionmaker(self.engine, expire_on_commit=False)

а затем вызовите self.session() в разных потоках, у меня будет 3 разных соединения, которые используются в N разных потоках. Означает ли это, что только 3 параллельных потока будут выполнять некоторую работу, в то время как другие будут ждать, пока один или несколько потоков не вызовут session.close()? Или есть шанс, что> 2 потока будут использовать одно и то же соединение одновременно?

NullPool безопаснее (потому что каждый новый сеанс - это новое соединение) или нет?

self.engine = create_engine('postgresql://{user}:{password}@{host}:{port}/{db}'.format(
            user=Configuration().get(section='repository', option='user'),
            password=Configuration().get(section='repository', option='password'),
            host=Configuration().get(section='repository', option='host'),
            port=Configuration().get(section='repository', option='port'),
            db=Configuration().get(section='repository', option='database')
        ), echo=False, poolclass=NullPool)

Общий вопрос: можно ли в таком случае использовать один и тот же пул соединений:

engine = create_engine('connection_string', echo=False, pool_size=3)
Session = sessionmaker(engine)

def some_function():
    session = Session()
    ...

pool = Pool(processes=10)
pool.map(some_function)
pool.close()
pool.join()

@ IljaEverilä, но я до сих пор не понимаю этой части: могу ли я использовать один и тот же пул соединений в нескольких потоках или нет?

Nikita Ryanov 09.08.2018 16:43

Теперь кажется, я понял главную идею

Nikita Ryanov 09.08.2018 17:00
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
8
3
6 639
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В целом, кажется, есть смесь между потоками и процессами. Вопрос начинается с вопроса, является ли пул соединений SQLAlchemy поточно-ориентированным, но заканчивается примером кода, в котором используется multiprocessing. Краткий ответ на «общий вопрос»: нет, вы не должны совместно использовать движок и связанный с ним пул соединений через границы процесса, если используется разветвление. Но есть исключения.

Реализации пула сами по себе потокобезопасны и через прокси-сервер Engine также является потокобезопасным, потому что механизм не сохраняет состояние в дополнение к сохранению ссылки на пул. С другой стороны, соединения, извлеченные из пула, - это нет потокобезопасный и ни Session.

Documentation says that connection pool also is not designed for multithreading:

Есть небольшая ошибка в чтении, поскольку исходная цитата из документации касается совместного использования пулов соединений через границы процесс, если используется разветвление. Это, вероятно, приведет к проблемам, потому что под уровнями SQLAlchemy и DB-API обычно находится сокет TCP / IP или дескриптор файла, и с ними не следует работать одновременно.

В этом конкретном случае использование NullPool было бы безопасным, в то время как другие - нет, поскольку он вообще не объединяется, и поэтому соединения не будут разделяться между процессами, если только один из них не сделает этого.

Does it mean that only 3 concurrent thread will do some work while others will wait until one or more thread will call session.close()?

Предполагая, что QueuePool используется, установленный размер не является жестким пределом, и есть место для переполнения. Размер определяет количество подключений, которые будут постоянно храниться в пуле. Если предел переполнения достигнут, вызов будет ждать timeout секунд, прежде чем отказаться и повысить TimeoutError, если соединение не стало доступным.

Or there is a chance that >2 threads will use the same connection simultaneously?

Два или более потоки не смогут случайно проверить одно и то же соединение из пула, за исключением StaticPool, но можно явно разделить его между потоками после (не делайте этого).


В конце концов, «Работа с двигателями и соединениями - базовое использование» покрывает основные части вопроса:

A single Engine manages many individual DBAPI connections on behalf of the process and is intended to be called upon in a concurrent fashion [emphasis added].

...

For a multiple-process application that uses the os.fork system call, or for example the Python multiprocessing module, it’s usually required that a separate Engine be used for each child process. This is because the Engine maintains a reference to a connection pool that ultimately references DBAPI connections - these tend to not be portable across process boundaries. An Engine that is configured not to use pooling (which is achieved via the usage of NullPool) does not have this requirement.

Замечательный ответ! Спасибо! The question begins by asking if an SQLAlchemy connection pool is thread-safe, but ends with a code example that uses multiprocessing наверное до сих пор путаю эти термины

Nikita Ryanov 09.08.2018 22:25

процесс может состоять из одного или нескольких потоки, работающих одновременно и, возможно, параллельно. Линия между двумя время от времени может казаться немного размытым.

Ilja Everilä 09.08.2018 23:00

В случае, если это поможет кому-то еще - и это действительно ответ на другой вопрос, который будет:

Does SQLAlchemy use the same connection pool for all engines in the same thread?

Ответ - нет. Как указывает @ ilja-everila, SQLA надеется вы должны использовать один engine для каждого процесса. Итак, если вы это сделаете

engine1 = create_engine(...)
engine2 = create_engine(...)
engine1.pool is engine2.pool   # <- False

# so although pool_size=5, you can open more than 5 total connections
# because each engine has separate pools
connections1 = [engine1.connect() for _ in range(5)]
connections2 = [engine1.connect() for _ in range(5)]

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

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

Таким образом, вы должны стремиться иметь один глобальный / одноэлементный экземпляр движка для своего приложения.

Учитесь на моем провале!

Другие вопросы по теме