Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using sqlalchemy scoped_session in theading.Thread

I'm having problems using sqlalchemy together with threading.

import queue
import threading

import sqlalchemy
from sqlalchemy import create_engine, Column, Integer, String, Sequence
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.scoping import scoped_session

engine = create_engine('sqlite:///:memory:', echo=False)
Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
    name = Column(String)
    fullname = Column(String)
    password = Column(String)

    def __repr__(self):
        return "<User(name='%s', fullname='%s', password='%s')>" % (
        self.name, self.fullname, self.password)
Base.metadata.create_all(engine)

sessionfactory = sessionmaker(bind=engine)

# called by each thread
def write_name(q, name, sessionfactory):
    session = scoped_session(sessionfactory)
    ed_user = User(name=name, fullname='Power', password='edspassword')
    session.add(ed_user)
    session.commit()
    q.put(name)

names = ["Max", "Austin"]

q = queue.Queue()

for u in names:
    t = threading.Thread(target=write_name, args = (q, u, sessionfactory))
    t.daemon = True
    t.start()

s = q.get()

This results in:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users [SQL: 'INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)'] [parameters: ('Max', 'Power', 'edspassword')]

but it works fine to add and read data in the main thread. Furthermore I assume I need to use threading over multiprocess because scoped_session uses thread local storage.

like image 314
sonium Avatar asked Oct 10 '15 14:10

sonium


People also ask

What is scoped_session in SQLAlchemy?

The scoped_session object is a very popular and useful object used by many SQLAlchemy applications. However, it is important to note that it presents only one approach to the issue of Session management.

Is flask SQLAlchemy session scoped or scoped?

FYI, when using flask-sqlalchemy, the session object provided is by default a scoped session object. Show activity on this post. I am looking into this myself, but I am not an expert.

What is the use of sessionmaker in SQLAlchemy?

* SQLAlchemy provides the sessionmaker function which generates a new Session object whenever it is called. The recommended way to use it is to have a global variable that has a configured sessionmaker object which can then be used by other parts of the application to create sessions.

How does threading work with scopedsession?

The ScopedSession object by default uses threading.local () as storage, so that a single Session is maintained for all who call upon the ScopedSession registry, but only within the scope of a single thread. Callers who call upon the registry in a different thread get a Session instance that is local to that other thread.


1 Answers

The main problem is that you can't have multiple connections to a SQLite database that only exists in memory, because each connection will create a new empty database. See the SQLAlchemy docs on this. In short, you need to create the engine like this to make sure that is only one instance that can be shared across threads.

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

Once you do that, you don't need scoped_session, because the point of scoped_session is to create one connection per each thread and you specifically can't do that here.

Also, note that you should have only one scoped_session instance if you want it to work correctly (with a non-SQLite engine). You should treat it as global variable and then it will be able to handle the thread-local stuff.

like image 102
Lukáš Lalinský Avatar answered Sep 17 '22 03:09

Lukáš Lalinský