On Mon, Jan 7, 2013 at 9:27 AM, Michael Bayer <mike...@zzzcomputing.com>wrote:

>
> On Jan 7, 2013, at 1:11 AM, Ken Lareau wrote:
>
>
> Okay, this is what I suspected and feared. :)  Creating new sessions isn't
> much of an
> issue, and I came up with a class to manage this for me before realizing
> my problem
> is going to end up being much deeper...
>
> My current library that uses SQLAlchemy was based off a single session...
> so in a
> file called meta.py I had the following line:
>
>     Session = scoped_session(sessionmaker())
>
> (swiped from an old Pylons example).  In my __init__.py file, I did:
>
>     from tagopsdb.database.meta import Session
>
> and then in when initializing my connection to the database, simply did:
>
>     Session.configure(bind=<engine>)
>
> From all other areas of the library, I simple re-used the aforementioned
> import, then
> actually directly used Session, such as:
>
>     Session.add(<obj>)
>
> or
>
>     Session.commit()
>
> Now... this may be very poor usage of it, though I'm trying to improve my
> under-
> standing and utilize SQLAlchemy better.
>
>
> that's pretty much the usage we've encouraged for a long time, the
> "Session" is basically "the main Session", I think its OK.
>

Well, it's good to know I at least had that relatively right. :)

Of course, with the sudden need for more
> than one session, I'm finding that I'm running into an issue.
>  Specifically:
>
> 1) To make it easy to find the correct session, I'm using a dictionary
> which I
>     pass around instead of Session... but referencing a given session is a
> bit
>     clunky - 'sessions.current[<name>]' is quite a bit less succinct than
> the
>     use of just 'Session', and while I could just assign a given entry to
> a shorter
>     name, that just seems to add to the mistake. :)
>
>
> Depending on the usage pattern here, if the need for the "extra"
> transaction is localized, then I'd be using an explicit passing pattern for
> this second Session - that is, not using a global registry.   If the case
> is more like large amounts of code are split 50/50 between these two
> Sessions, then I'd possibly use a second Session registry.  Since this
> Session is more intended as an ad-hoc "commit" Session though I might stick
> to keeping it as a non-global object.
>
>
>
> 2) All the methods in my library currently expect the session to be
> 'Session';
>     that changes with the need for multiple sessions, and it means either I
>     will now need to explicitly pass the session into every method, or
> find a
>     way to have the session automatically determined... which may not be
>     possible or reasonable.
>
>
>
> OK, well there's another path here, which is that you can affix a second
> Session object to your registry temporarily.
>
> existing = Session.registry()   # current Session
> Session.registry.set(my_temporary_session)  # set a different Session
>
> ... call functions ...
>
> # restore the original
>
> Session.registry.set(existing)
>

Given this and your previous comments, and after some conversations with
a coworker, I decided to expose the 'engine' variable from the database
library and create a context manager as so:

@contextlib.contextmanager
def isolated_transaction(NewSession=None):
    """Manage a new transaction (session) within an existing session"""

    needed_session = True
    existing = Session.registry()

    if NewSession is None:
        NewSession = scoped_session(sessionmaker(bind=engine))
    else:
        needed_session = False

    Session.registry.set(NewSession)

    try:
        yield
    finally:
        Session.registry.set(existing)

        if needed_session:
            NewSession.close()


(This code hasn't quite been tested yet and may need some tweaking)
With this, whenever I need an 'isolated' transaction I can simply do
from my program:

    with isolated_transaction():
        <do stuff with Session>

And things should 'just work'... at least that's my hope!  I can also pass
an existing new session if I want to use one in multiple places (not
sure I'll need that just yet, but it was easy enough to write into the
method, so...)

Hopefully this seems like a sane solution to my problem. :)


>> Depending on the database in use, using low isolation levels can have the
>> effect that other transactions can view "dirty reads" as the transaction
>> proceeds, but this is obviously an all-or-nothing thing.    When I need
>> certain resources exposed during a long running transaction, I transfer
>> that data to a different Session and commit() those changes distinctly.
>>
>> My current needs would tend to use the short transactions for things that
> are mostly
> isolated from anything going on in the longer running (main) transaction,
> though I do
> suspect I might need what you mention in your last sentence, but might you
> be able
> to refer me to an example of how it would work, perchance?
>
>
> I'd advise against going this route, you'd pretty much need to use MySQL
> MyISAM tables to get guaranteed "dirty reads", that is, there's no
> transaction at all, and it's not really how transactions were meant to be
> used.    Lowering the isolation level is usually just a means to get more
> transaction throughput.
>

Okay, I suspect I misunderstood what was being mentioned here, which is
no problem; I'll avoid it. :)  Thanks once again for all the help.

-Ken


>  --
> You received this message because you are subscribed to the Google Groups
> "sqlalchemy" group.
> To post to this group, send email to sqlalchemy@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.
>



-- 
- Ken Lareau

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@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