Here is the basic outline of the web request hander. It creates an operation, gets the return value, and either spawns a thread to execute the operation (with non-sqlalchemy related instrumentation) or completes the execution on the operation model. In the case I see there is a provisioning phase and the operation return value is None.
There are two potential threading problems I see: 1) Since op._operation is an ORM, could evaluating it in a boolean context cause the ORM to be reattached to a previously closed session? 2) if the operation returns orms they will may accessed concurrently by the handler thread and the execute thread. This return value is sent as the xmlrpc response and contains only python primitives. The operations we see this on return None. try: op = Operation(..) # the first session is created, used, and closed here...the close happens in a try/finally and the "transaction XXX committed" logging happens after close() completes successfully ret = op.get_return_value() # the operation invariably returns None in the vast majority of cases we've seen if has_attr(op, 'provision'): # the operations we see this with do have a provision() method AsyncOperation(LOGGER, MonitoringAgent.getInstance().monitor("%s-execute" % op.api_name())(_async_execution_logger(op.api_name())(op.execute))) # execute exists on Operation base class and calls provision finally: if op._operation and not op.has_provisioning_phase: # changing the order of this could avoid boolean evaluation of op._operation in the case we spawned a thread op._operation.execution_complete() # does not get called in the operations we see this error in On Fri, Sep 5, 2014 at 9:21 AM, Michael Bayer <mike...@zzzcomputing.com> wrote: > > On Sep 5, 2014, at 12:03 PM, Lonnie Hutchinson <lonn...@skytap.com> wrote: > > > > > On Thu, Sep 4, 2014 at 5:53 PM, Michael Bayer<mike...@zzzcomputing.com> > wrote: > >> >> > The code I say appears to expect the session_id to remain on detached >> states in certain situations is that the check in Session._attach checks >> not only the session_id but that the referenced session still exists in >> _sessions: >> > if state.session_id and \ >> > state.session_id is not self.hash_key and \ >> > state.session_id in _sessions: >> > raise sa_exc.InvalidRequestError( >> > "Object '%s' is already attached to session '%s' " >> > "(this is '%s')" % (orm_util.state_str(state), >> > state.session_id, self.hash_key)) >> > >> > I am interested in this in the hopes that sheds light on the source of >> the intermittent failures. >> >> yeah that code is intended to detect when an object is added to a session >> that hasn't been removed from another. >> >> > So, is the intent of the 'state.session_id in _sessions" clause to handle > the situation where the session is collected while it contains instances? > > > it’s to handle the case where Session A was garbage collected, not cleanly > closed, and now an object from that now-gone session wants to be added to a > different session. this might not even be a case we should be handling, > perhaps if we changed the scheme for sessions to be an absolutely unique > counter and just made it more strict. Not a change planned for now > though... > > > >> >> I agree, it appears to be pretty clear. However, on the application side > I have log messages indicating I successfully commit() and close() the > session before receiving an error saying an instance is still attached to > it: > > 2014-08-13 14:14:11 tuk1m1cm1 [debug] [DEBUG] db.py:57 - transaction 8710 > started > > 2014-08-13 14:14:12 tuk1m1cm1 [debug] [DEBUG] db.py:143 - transaction 8710 > committed > > 2014-08-13 14:14:12 tuk1m1cm1 [debug] [DEBUG] db.py:57 - transaction 8719 > started > > 2014-08-13 14:14:13 tuk1m1cm1 [debug] [DEBUG] db.py:140 - transaction 8719 > rolled back2014-08-13 14:14:13 tuk1m1cm1 [err] [ERROR] threadutils.py:98 - > Error executing asynchronous operation asynch-op <function execute at > 0x5e54c08>(, {}) : Object '<Account at 0x5d49250>' is already attached to > session '8710' (this is '8719') at Traceback (most recent call last):.. > > > Again I see lots terms here “threadutils”, “async operations”, things that > suggest the application has a complex system of concurrency which may be > misbehaving. I think you should be adding more assertions to your code to > ensure that close() has completed fully before an object is attempted to be > moved to another session. > > > > "transaction" refers to a session, the integer is the session.hash_key. > > >> > In debugging this I have noticed that after the first session has been >> closed() and the initializer has returned the session remains in _sessions. >> However, if I call gc.collect() the session is removed, suggesting it just >> hasn't been fully cleaned up yet. >> >> that's not really accurate. There's a collection of all the Sessions in >> the weak referencing map _sessions, and Python's GC may not in fact remove >> those sessions in a deterministic way. But that has no bearing upon >> testing objects that have been removed from that session, and therefore >> have no session_id, if they belong to that session - their session_id has >> been cleared out. >> >> > I believe this is relevant because if the session were to be GC'ed the > "state.session_id in _sessions" would not evaluate to true and the attach > to the session I want would succeed. > > > This is essentially saying that a certain assertion being removed allows > the code to “work”. But the assertion here is testing for a specific > thing; that an object was not fully detached from a session before it is > attempted to be used in another. I don’t see any indication that this > assertion is malfunctioning. > > > -- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to sqlalchemy+unsubscr...@googlegroups.com. > To post to this group, send email to sqlalchemy@googlegroups.com. > Visit this group at http://groups.google.com/group/sqlalchemy. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at http://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.