On Thu, Jan 17, 2013 at 4:13 PM, Michael Bayer <mike...@zzzcomputing.com>wrote:
> > On Jan 17, 2013, at 7:01 PM, Ken Lareau wrote: > > On Thu, Jan 17, 2013 at 3:54 PM, Michael Bayer > <mike...@zzzcomputing.com>wrote: > >> >> On Jan 17, 2013, at 6:45 PM, Ken Lareau wrote: >> >> Sadly, it looks like when I try the code in my full application, it is >> failing miserably. >> I'm seeing errors like this: >> >> sqlalchemy.orm.exc.DetachedInstanceError: Instance <Deployments at >> 0x3367910> is not bound to a Session; attribute refresh operation cannot >> proceed >> >> and >> >> sqlalchemy.exc.InvalidRequestError: Object '<HostDeployments at >> 0x21cd050>' is already attached to session '1' (this is '3') >> >> Sadly it's not immediately obvious as to what's going on... not even >> certain how >> to start debugging this problem. >> >> >> well an object can only be "attached" to one Session at a time. so the >> tricky thing is that it's kind of easy to get objects in the Session >> sometimes when you don't want them to. like if you have A and B, and >> there's a relationship between both like A.bs an B.a, putting an "A" into a >> Session will pull in the "B" and vice versa. >> >> So if you're putting some kind of object into this other Session to be >> committed immediately, you'd have to make sure it isn't being pulled into >> your primary session. if you call object_session(someobj) on any object >> it will show you what Session it belongs to. >> >> The other option is to merge() the state of the object into another >> Session. merge() makes a copy of an object from one Session to another. >> Though here you're looking to commit those objects in the second session >> and not at all in the first so you probably should jsut make sure those >> objects are only in that one Session. >> > > That would explain things, since I have things like: > > with isolated_transaction(): > host_dep.status = 'ok' > Session.commit() > > Obviously the 'host_dep' is from the original session, so this isn't going > to work. I know about merge(), > though in this case I'm not fully certain how to use it, or if it can even > solve issues like this. :( > > > OK you could merge in a situation like that: > > with isolated_transaction(): > merge_host_dep = Session.merge(host_dep) > merge_host_dep.status = "ok" > Session.commit() > > but now your "host_dep" is in a different state as "merge_host_dep", and > that row has a different value in the "isolated" transaction than the one > locally. if you updated it locally, then you'd see that same UPDATE > statement happen again and it might even hit upon a conflict when you try > to commit the main transaction. > > the "using two transactions" pattern is sort of best if you can isolate > rows that are intended for one side or the other. > Sadly in this case... I can't isolate, at least not very easily. The code is involved enough that extracting the necessary parts to isolate a given object for change would be very, very difficult, if not impossible. Unfortunately this may require me to completely rewrite large parts of my application. :( I do thank you for all the help you've given so far, though. It's been very much appre- ciated. - Ken > - Ken > > >> >> >> >> - Ken >> >> >> On Tue, Jan 8, 2013 at 2:59 PM, Ken Lareau <klar...@tagged.com> wrote: >> >>> On Tue, Jan 8, 2013 at 2:16 PM, Michael Bayer >>> <mike...@zzzcomputing.com>wrote: >>> >>>> >>>> On Jan 8, 2013, at 4:00 PM, Ken Lareau wrote: >>>> >>>> >>>> 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. :) >>>> >>>> >>>> its good, though you can save yourself all the trouble with >>>> scoped_session/sessionmaker by just saying this: >>>> >>>> from sqlalchemy.orm import Session >>>> new_session = Session(bind=engine) >>>> >>>> or which might be even more portable, use the sessionmaker from your >>>> existing registry: >>>> >>>> new_session = Session.session_factory() >>>> >>>> session_factory is the sessionmaker() you stuck onto scoped_session in >>>> the first place. In that case you don't need to refer to the Engine >>>> explicitly. >>>> >>> >>> Nice, the second one definitely works and I no longer need to export >>> 'engine' >>> and only need to import Session from my library where I have the context >>> manager place. :) >>> >>> Thanks again! >>> >>> - Ken >>> >>> >>>>>> 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. >>>> >>>> >>>> >>>> -- >>>> 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 >>> >>> >> >> >> -- >> - 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. >> >> >> >> -- >> 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. > > > -- > 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.