Подтвердить что ты не робот

Sqlalchemy и общий кэш SQLite

SQLite поддерживает "общий кэш" для баз данных :memory:, когда они открываются специальным URI (согласно sqlite.org):

[T] его же база данных в памяти может быть открыта двумя или более базами данных соединения следующим образом:

rc = sqlite3_open("file::memory:?cache=shared",&db);

Я могу воспользоваться этим в Python 3.4, используя параметр

4b9b3361

Ответ 1

Вам следует избегать передачи uri=True в старых версиях Python, и проблема будет исправлена:

import sqlite3
import sys

import sqlalchemy


DB_URI = 'file::memory:?cache=shared'
PY2 = sys.version_info.major == 2
if PY2:
    params = {}
else:
    params = {'uri': True}

creator = lambda: sqlite3.connect(DB_URI, **params)

engine = sqlalchemy.create_engine('sqlite:///:memory:', creator=creator)
engine.connect()

Ответ 2

Документы SQLAlchemy о диалекте SQLite подробно описывают проблему и решение:

Поведение потоков/объединения

Поведение Pysqlites по умолчанию запрещает использование одного соединения в более чем одном потоке. Первоначально он предназначен для работы с более старыми версиями SQLite, которые не поддерживают многопоточные операции при различных обстоятельствах. В частности, старые версии SQLite ни при каких обстоятельствах не позволяли использовать базу данных: memory: database в нескольких потоках.

Pysqlite включает в себя недокументированный флаг, известный как check_same_thread, который отключит эту проверку, однако обратите внимание, что соединения pysqlite по-прежнему небезопасны для одновременного использования в нескольких потоках. В частности, любые вызовы выполнения операторов должны быть внешне мьютексированы, так как Pysqlite не обеспечивает потокобезопасное распространение сообщений об ошибках среди прочего. Таким образом, хотя даже :memory: базы данных могут совместно использоваться потоками в современном SQLite, Pysqlite не обеспечивает достаточной безопасности потоков, чтобы оправдать это использование.

SQLAlchemy устанавливает пул для работы с поведением Pysqlites по умолчанию:

  • Когда указана база данных :memory: SQLite, диалект по умолчанию будет использовать SingletonThreadPool. Этот пул поддерживает одно соединение для каждого потока, так что при любом доступе к модулю в текущем потоке используется одно и то же :memory: database - другие потоки будут иметь доступ к другому :memory: database.

  • Когда указана файловая база данных, диалект будет использовать NullPool в качестве источника соединений. Этот пул закрывает и сбрасывает соединения, которые немедленно возвращаются в пул. Соединения на основе файлов SQLite имеют очень низкую нагрузку, поэтому пул не требуется. Схема также предотвращает повторное использование соединения в другом потоке и лучше всего работает с грубой блокировкой файлов SQLites.

Использование базы данных памяти в нескольких потоках

Чтобы использовать базу данных :memory: в многопоточном сценарии, один и тот же объект соединения должен быть общим для потоков, поскольку база данных существует только в рамках этого соединения. Реализация StaticPool будет поддерживать единственное соединение глобально, и флаг check_same_thread может быть передан Pysqlite как False:

from sqlalchemy.pool import StaticPool
engine = create_engine('sqlite://',
              connect_args={'check_same_thread':False},
              poolclass=StaticPool)

Обратите внимание, что для использования базы данных :memory: в нескольких потоках требуется последняя версия SQLite.

Источник: https://docs.sqlalchemy.org/en/13/dialects/sqlite.html#threading-pooling-behavior