What's the recommended scoped_session usage pattern in a multithreaded sqlalchemy webapp?

Question:

I’m writing an application with python and sqlalchemy-0.7. It starts by initializing the sqlalchemy orm (using declarative) and then it starts a multithreaded web server – I’m currently using web.py for rapid prototyping but that could change in the future. I will also add other “threads” for scheduled jobs and so on, probably using other python threads.

From SA documentation I understand I have to use scoped_session() to get a thread-local session, so my web.py app should end up looking something like:

import web
from myapp.model import Session  # scoped_session(sessionmaker(bind=engine))
from myapp.model import This, That, AndSoOn
urls = blah...
app  = web.application(urls, globals())

class index:
    def GET(self):
        s = Session()
        # get stuff done
        Session().remove()
        return(stuff)

class foo:
    def GET(self):
        s = Session()
        # get stuff done
        Session().remove()
        return(stuff)

Is that the Right Way to handle the session?

As far as I understand, I should get a scoped_session at every method since it’ll give me a thread local session that I could not obtain beforehand (like at the module level).

Also, I should call .remove() or .commit() or something like them at every method end, otherwise the session will still contain Persistent objects and I would not be able to query/access the same objects in other threads?

If that pattern is the correct one, it could probably be made better by writing it only once, maybe using a decorator? Such a decorator could get the session, invoke the method and then make sure to dispose the session properly. How would that pass the session to the decorated function?

Asked By: Luke404

||

Answers:

You don’t need to create a scoped session if you create new session for each request and each request is handled by single thread.

You have to call s.commit() to make pending objects persistent, i.e. to save changes into database.

You may also want to close session by calling s.close().

Answered By: Denis Otkidach

Yes, this is the right way.

Example:

The Flask microframework with Flask-sqlalchemy extension does what you described. It also does .remove() automatically at the end of each HTTP request (“view” functions), so the session is released by the current thread. Calling just .commit() is not sufficient, you should use .remove().

When not using Flask views, I usually use a “with” statement:

@contextmanager
def get_db_session():
    try:
        yield session
    finally:
        session.remove()

with get_db_session() as session:
    # do something with session

You can create a similar decorator.

Scoped session creates a DBMS connection pool, so this approach will be faster than opening/closing session at each HTTP request. It also works nice with greenlets (gevent or eventlet).

Answered By: Alex Lokk