On Jan 17, 2013, at 7:01 PM, Ken Lareau wrote:
> On Thu, Jan 17, 2013 at 3:54 PM, Michael Bayer <[email protected]>
> 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.
>
> - Ken
>
>
>
>
>>
>> - Ken
>>
>>
>> On Tue, Jan 8, 2013 at 2:59 PM, Ken Lareau <[email protected]> wrote:
>> On Tue, Jan 8, 2013 at 2:16 PM, Michael Bayer <[email protected]>
>> 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 [email protected].
>>> To unsubscribe from this group, send email to
>>> [email protected].
>>> 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 [email protected].
>>> To unsubscribe from this group, send email to
>>> [email protected].
>>> 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 [email protected].
>> To unsubscribe from this group, send email to
>> [email protected].
>> 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 [email protected].
>> To unsubscribe from this group, send email to
>> [email protected].
>> 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 [email protected].
> To unsubscribe from this group, send email to
> [email protected].
> 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 [email protected].
> To unsubscribe from this group, send email to
> [email protected].
> 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 [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en.