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.