On Oct 3, 2012, at 5:21 AM, Gombas, Gabor wrote:

> Hi,
>  
> I’ve run into a rather interesting issue, where calling session.rollback() 
> dies. The error I get with SQLA 0.7 is:
>  
>  
> What I see when turning on query logging:
>  
> -          The Foo object with primary key 342 is loaded (using a non-primary 
> mapper, if that makes any difference), and then deleted. The session is 
> flushed.
> -          A new object is created (this time using the primary mapper), and 
> during INSERT, SQLite assigns the same primary key to it as the previously 
> deleted object had (I can see that from subsequent SELECT statements 
> referencing the new object; it would be nice if SQLA logged the primary key 
> after an INSERT at debug level)
> -          An application error (unrelated to SQLAlchemy) occurs, which 
> causes rollback() to be called
>  
> I have a reliable reproducer, but it is the unit test of a large application, 
> so there are lots of possibly unrelated things going on. I’ve spent two days 
> coming up with a standalone reproducer, but I’ve failed, that’s why I’m 
> sending this mail instead of opening a ticket.

So when the rollback goes to "restore the snapshot", it goes through an 
aggregated ".new" collection for every INSERT that's happened in the 
transaction, and expunges these objects - that is, removes them from the 
identity map.  Then it goes through everything that was DELETEd in the 
transaction by going through an aggregated ".deleted" collection for every 
DELETE that's happened in the transaction, and restores them to their former 
state in the identity map.   The assumption here is that only way a key in the 
identity map would be replaced would be the DELETE via session + subsequent 
INSERT via session.

So let me just note that this has worked without anyone reporting an issue for 
over three years.   But now that you're showing me this, I can confirm there 
are two additional paths where a conflicting identity might be in the map, for 
which we will need to add some special handling.

Starting like this:

s = Session(e)

a1 = A(id=1, data='a1')
a2 = A(id=2, data='a2')

s.add(a1)
s.add(a2)

s.commit()

If we first delete one of the rows:

s.delete(a1)
s.flush()

Then we UPDATE another row to point to that PK, it's unhandled, this will blow 
it up:

a2.id = 1
s.flush()

The other, if an INSERT occurred that the Session was unaware of:

s.execute("insert into a (id, data) values (1, 'a3')")

a3 = s.query(A).get(1)

Catching both of these is easy enough and a patch is added to #2583: 
http://www.sqlalchemy.org/trac/ticket/2583

The SQLite case you describe isn't exactly like either of these; if the object 
were flushed by the session and received the same PK, that case is already 
handled since the object would be in ._new.  So my only guess is that, that's 
not the trigger of the issue.    So at the very least you need to try that 
patch and see if it clears things up.

Assuming that's the fix, I don't know that there's a clean way to patch this 
in, except for patching your install or monkeypatching a new Session class in.  
   I should have it committed in both 0.7/0.8 within 30, unless I hit some 
snags in the tests.

-- 
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.

Reply via email to