Hi Chris:

I'm a bit hesitant to share what I've done, b/c it's still a work in
progress etc, but here goes:

    MySQL MyISAM, wait_timeout=28800
    SQLAlchemy 0.5.6, pool_recycle=3600

I've written a few decorators (mostly stolen from SQLAlchemy docs &
examples):

def with_query_write(fn):
    def go(self, *args, **kw):
        try:
            result = fn(self, *args, **kw)
            self.session.commit()
            return result
        except:
            self.session.rollback()
            raise
    return go

def with_query_read(fn):
    def go(self, *args, **kw):
        try:
            return fn(self, *args, **kw)
        except:
            self.session.rollback()
            raise
    return go

def with_session_write(fn):
    def go(*args, **kw):
        try:
            result = fn(*args, **kw)
            session.commit()
            return result
        except:
            session.rollback()
            raise
    return go

All session writes go through base_dao.py, and are decorated with
@with_session_write. This way the code isn't littered with session.add(),
session.commit(), session.rollback() etc.

    @with_session_write
    def save(self, instance):
        session.add(instance)
        ...

We've also extended Query (for reasons other than framing -- code omitted)
and added @with_query_write and @with_query_read decorators. I don't love
how I extended Query, and as of PyCon I know a better way to do this, but I
haven't had a chance to re-implement it.

Anyhoo, we pass the custom query class to the sessionmaker:

    session = orm.scoped_session(orm.sessionmaker(query_cls=FooQuery))

The custom query class:

class FooQuery(Query):

    def __init__(self, *arg, **kw):
        Query.__init__(self, *arg, **kw)
        ...

    @with_query_read
    def all(self):
        return Query.all(self)

    @with_query_write
    def delete(self):
        return Query.delete(self)

    ...

Finally, we're using pylons and are removing the contextual session in the
finally clause of the base controller's __call__ method.

class BaseController(WSGIController):

    def __call__(self, environ, start_response):
        try:
            ...
        finally:
            session.remove()

We only ever see 'MySQL server has gone away' on our idle failover app
instances, as the only traffic they get are occasional pings from nagios. I
would have thought a combination of wait_timeout & pool_recycle would
prevent this... but I'm probably missing some piece of the big picture.

    OperationalError: (OperationalError) (2006, 'MySQL server has gone
away')

I do wonder how possible it would be wrt the open session in view etc to
implement a reconnect on is_dissconect() in _handle_dbapi_exception()...

I also wonder if there's some app out there using SQLAlchemy with an
exemplary data access layer that we could all learn from. I should try a few
code search engines... I went with DAOs (one per mapped table) which extend
a base DAO that know how to do pagination, saves, deletes, etc. I dunno...

Thanks fro asking Chris. I'm watching the answers to these threads too.

--diana

On Wed, Apr 28, 2010 at 9:37 AM, Chris Withers <ch...@simplistix.co.uk>wrote:

> Hi All,
>
> I'm still trying to get an answer on this...
>
> Am I right in understanding that the basic session lifecycle should be:
>
> try:
>    <use session>
>    session.commit()
> except:
>   log()
>   session.rollback()
> finally:
>   session.remove()
>
> The structure I've traditionally used with transactions has been:
>
> try:
>    <use session>
> except:
>   log()
>   session.rollback()
> else:
>   session.commit()
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalch...@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to