SQLite supports a \"shared cache\" for :memory:
databases when they are opened with a special URI (according to sqlite.org):
[T]he same in-me
You should avoid passing uri=True
on older Python versions and the problem will be fixed:
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()
SQLAlchemy docs about the SQLite dialect describe the problem and a solution in detail:
Threading/Pooling Behavior
Pysqlite’s default behavior is to prohibit the usage of a single connection in more than one thread. This is originally intended to work with older versions of SQLite that did not support multithreaded operation under various circumstances. In particular, older SQLite versions did not allow a :memory: database to be used in multiple threads under any circumstances.
Pysqlite does include a now-undocumented flag known as
check_same_thread
which will disable this check, however note that pysqlite connections are still not safe to use in concurrently in multiple threads. In particular, any statement execution calls would need to be externally mutexed, as Pysqlite does not provide for thread-safe propagation of error messages among other things. So while even:memory:
databases can be shared among threads in modern SQLite, Pysqlite doesn’t provide enough thread-safety to make this usage worth it.SQLAlchemy sets up pooling to work with Pysqlite’s default behavior:
When a
:memory:
SQLite database is specified, the dialect by default will useSingletonThreadPool
. This pool maintains a single connection per thread, so that all access to the engine within the current thread use the same:memory:
database - other threads would access a different:memory:
database.When a file-based database is specified, the dialect will use
NullPool
as the source of connections. This pool closes and discards connections which are returned to the pool immediately. SQLite file-based connections have extremely low overhead, so pooling is not necessary. The scheme also prevents a connection from being used again in a different thread and works best with SQLite’s coarse-grained file locking.Using a Memory Database in Multiple Threads
To use a
:memory:
database in a multithreaded scenario, the same connection object must be shared among threads, since the database exists only within the scope of that connection. TheStaticPool
implementation will maintain a single connection globally, and thecheck_same_thread
flag can be passed to Pysqlite asFalse
:from sqlalchemy.pool import StaticPool engine = create_engine('sqlite://', connect_args={'check_same_thread':False}, poolclass=StaticPool)
Note that using a
:memory:
database in multiple threads requires a recent version of SQLite.
Source: https://docs.sqlalchemy.org/en/13/dialects/sqlite.html#threading-pooling-behavior