I\'m trying to set up a system that elegantly defers database operations to a seperate thread in order to avoid blocking during Twisted callbacks.
So far, here is my app
Short answer
Call .remove()
on Session
, not session
.
Long answer:
scoped_session
doesn't really return a Session
class. Instead, it creates an object that pays attention to which thread it's called in. Calling it will either return the existing Session
instance associated with that thread or associate a new one and return that. A thread local is what associates a thread with a session.
The remove
method on a scoped_session
object removes the session object currently associated with the thread in which it's called. That means it's the opposite of scoped_session.__call__
, which is kind of a confusing API.
Here's a short Python script to illustrate the behavior.
import threading
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
_engine = create_engine('sqlite:///:memory:')
Session = scoped_session(sessionmaker(_engine))
def scoped_session_demo(remove=False):
ids = []
def push_ids():
thread_name = threading.currentThread().getName()
data = [thread_name]
data.append(Session())
if remove:
Session.remove()
data.append(Session())
ids.append(data)
t = threading.Thread(target=push_ids)
t.start()
t.join()
push_ids()
sub_thread, main_thread = ids
sub_name, sub_session_a, sub_session_b = sub_thread
main_name, main_session_a, main_session_b = main_thread
print sub_name, sub_session_a == sub_session_b
print main_name, main_session_a == main_session_b
print sub_name, '==', main_name, sub_session_a == main_session_b
print 'Without remove:'
scoped_session_demo()
print 'With remove:'
scoped_session_demo(True)
Its output:
Without remove:
Thread-1 True
MainThread True
Thread-1 == MainThread False
With remove:
Thread-2 False
MainThread False
Thread-2 == MainThread False