great.  can you try the attached patch please (latest 0.8 tip), which will not 
call "commit" if the "prepare" returned false.   I'm not sure if this is 
complete though, if we should be doing a "rollback" afterwards, or what.   

not sure if I can get my local oracle XE to do two phase for real, that would 
help a lot.  this patch would need quite a few tests.






On Nov 20, 2012, at 6:33 PM, Thierry Florac wrote:

> Hi Michael,
> 
> Following your guidelines, this is the first test I made against cx_Oracle :
> 
> >>> conn = cx_Oracle.connect('scott','tiger','localhost:1521/ORCL', 
> >>> twophase=True)
> >>> id = random.randint(0, 2 ** 128)
> >>> xid = (0x1234, "%032x" % id, "%032x" % 9)
> >>> conn.begin(*xid)
> >>> conn.prepare()
> False
> >>> conn.commit()
> Traceback (most recent call last):
>   File "<console>", line 1, in <module>
> DatabaseError: ORA-24756: la transaction n'existe pas
> 
> As you can see, the "prepare" method returns False, meaning that the 
> transaction was *NOT* prepared.
> If I add any update statement, everything is OK.
> 
> Oracle 11g documentation seems to indicate that the "prepare" call can return 
> three "status" :
> 
> Prepared      Data on the node has been modified by a statement in the 
> distributed transaction, and the node was successfully prepared.
> Read-only     No data on the node has been, or can be, modified (only 
> queried), so no preparation is necessary.
> Abort The node cannot successfully prepare.
> 
> cx_Oracle returns False "when there is nothing available to prepare", so 
> probably at least in case 2, case for which "commit shouldn't be done" to 
> avoid ORA-24756 error.
> So it's behaviour seems conform with Oracle OCI documentation...
> 
> So my last question is : in the context of an SA session, who is responsible 
> of the prepare/commit statements, so that commit is not called if prepared 
> was not OK ? 
> 
> Best regards,
> Thierry
> 
> 
> 2012/11/20 Michael Bayer <mike...@zzzcomputing.com>
> Googling for the ORA code yields the usual tautological answers that say 
> nothing.
> 
> The first step here would be to isolate the issue in a simple test case with 
> SQLAlchemy only, and then cx_oracle where I suspect the bug lies.
> 
> The Session will not create a transaction at all, if no SQL is emitted.   So 
> that would mean SQL is definitely emitted.  Can you confirm this ? (look at 
> your SQL logging).
> 
> Then, an initial test would be like this:
> 
> s = Session(twophase=True, bind=my_oracle_engine)
> s.execute("select 1 from dual")
> s.commit()
> 
> 
> Here's my test, against Postgresql:
> 
> from sqlalchemy.orm import Session
> from sqlalchemy import create_engine
> 
> e = create_engine('postgresql://scott:tiger@localhost/test', echo=True)
> 
> s = Session(e, twophase=True)
> s.execute("select 1")
> s.commit()
> 
> the output:
> 
> 2012-11-20 10:53:06,894 INFO sqlalchemy.engine.base.Engine select version()
> 2012-11-20 10:53:06,894 INFO sqlalchemy.engine.base.Engine {}
> 2012-11-20 10:53:06,897 INFO sqlalchemy.engine.base.Engine select 
> current_schema()
> 2012-11-20 10:53:06,897 INFO sqlalchemy.engine.base.Engine {}
> 2012-11-20 10:53:06,900 INFO sqlalchemy.engine.base.Engine select 1
> 2012-11-20 10:53:06,900 INFO sqlalchemy.engine.base.Engine {}
> 2012-11-20 10:53:06,900 INFO sqlalchemy.engine.base.Engine PREPARE 
> TRANSACTION '_sa_ddca4886b1f5db002e83058341de2609'
> 2012-11-20 10:53:06,900 INFO sqlalchemy.engine.base.Engine {}
> 2012-11-20 10:53:06,908 INFO sqlalchemy.engine.base.Engine COMMIT PREPARED 
> '_sa_ddca4886b1f5db002e83058341de2609'
> 2012-11-20 10:53:06,908 INFO sqlalchemy.engine.base.Engine {}
> 2012-11-20 10:53:06,909 INFO sqlalchemy.engine.base.Engine BEGIN
> 2012-11-20 10:53:06,909 INFO sqlalchemy.engine.base.Engine {}
> 
> which I will explain, first there's a "BEGIN TWOPHASE" that unfortunately is 
> not logged, I have just committed a fix for that in tip.  Then there's the 
> PREPARE and the COMMIT - but this is Postgresql's syntax.  The Oracle 
> implementation only uses an XID when it calls cx_oracle's "begin()" method, 
> and then I'd assume its up to cx_oracle to do the rest.    So if there's a 
> bug anywhere, it's probably in cx_oracle.   My guess is that cx_oracle itself 
> is not emitting the BEGIN unless it sees DML (INSERT, UPDATE, DELETE).    The 
> "BEGIN" you see afterwards is a tweak that's specific to the psycopg2 dialect 
> when twophase is used so that it can run the next transaction.
> 
> A cx_oracle script would be:
> 
> import cx_Oracle
> import random
> 
> conn = cx_Oracle.connect(...)
> 
>  id = random.randint(0, 2 ** 128)
> xid = (0x1234, "%032x" % id, "%032x" % 9)  # no idea where i got this from, 
> probably cx_oracle 
> conn.begin(*xid)
> conn.prepare()
> conn.commit()
> 
> 
> if that fails, but then if you were to insert in the middle:
> 
> cursor = conn.cursor()
> cursor.execute("INSERT INTO sometable (q) VALUES (3)")
> 
> and then it works, there you go.   cx_oracle's bad behavior.
> 
> All of the "two phase" implementations required weird hacks to get them to 
> work, as this feature is exceedingly rare.   But for cx_Oracle, I never saw 
> it work completely hence we haven't been able to come up with workarounds for 
> existing issues.  
> 
> 
> 
> On Nov 20, 2012, at 4:28 AM, Thierry Florac wrote:
> 
>> Hi Michael,
>> 
>> This is a complete traceback produced by removing my checking code against 
>> attribute modification.
>> Original code is then as simple as this:
>> 
>>     @ajax.handler
>>     def updateOrderMenuItemBO(self):
>>         session = getSession(SESSION_NAME)
>>         ids = [ int(id) for id in self.request.form.get('ids') ]
>>         for menu in session.query(MenuItemBO).filter(MenuItemBO.id.in_(ids)):
>>             index = ids.index(menu.id) + 1
>>             menu.rang = index
>>         return getUtility(IJSONWriter).write({ 'status': 'OK' })
>> 
>> 
>> URL: 
>> http://172.23.109.204:8080/boconsult/%40%40menuItemBo.html/%40%40ajax/updateOrderMenuItemBO
>> File 
>> '/var/local/env/rdf/lib/python2.5/site-packages/Paste-1.7.5.1-py2.5.egg/paste/evalexception/middleware.py',
>>  line 306 in respond
>>   app_iter = self.application(environ, detect_start_response)
>> File '/var/local/env/rdf/webapp/src/webapp/startup.py', line 33 in wrapper
>>   return app(environ, start_response)
>> File 
>> '/var/local/env/rdf/webapp/eggs/zope.app.wsgi-3.4.3-py2.5.egg/zope/app/wsgi/__init__.py',
>>  line 55 in __call__
>>   request = publish(request, handle_errors=handle_errors)
>> File 
>> '/var/local/env/rdf/webapp/eggs/zope.publisher-3.4.10-py2.5.egg/zope/publisher/publish.py',
>>  line 138 in publish
>>   publication.afterCall(request, obj)
>> File 
>> '/var/local/env/rdf/webapp/eggs/zope.app.publication-3.4.4-py2.5.egg/zope/app/publication/browser.py',
>>  line 78 in afterCall
>>   super(BrowserPublication, self).afterCall(request, ob)
>> File 
>> '/var/local/env/rdf/webapp/eggs/zope.app.publication-3.4.4-py2.5.egg/zope/app/publication/zopepublication.py',
>>  line 175 in afterCall
>>   txn.commit()
>> File 
>> '/var/local/env/rdf/webapp/eggs/ZODB3-3.8.5-py2.5-linux-x86_64.egg/transaction/_transaction.py',
>>  line 325 in commit
>>   self._commitResources()
>> File 
>> '/var/local/env/rdf/webapp/eggs/ZODB3-3.8.5-py2.5-linux-x86_64.egg/transaction/_transaction.py',
>>  line 432 in _commitResources
>>   rm.tpc_finish(self)
>> File '/var/local/src/ztfy/ztfy.alchemy/ztfy/alchemy/datamanager.py', line 
>> 148 in tpc_finish
>>   self.tx.commit()
>> File 
>> '/var/local/env/rdf/webapp/eggs/SQLAlchemy-0.7.3-py2.5-linux-x86_64.egg/sqlalchemy/orm/session.py',
>>  line 317 in commit
>>   t[1].commit()
>> File 
>> '/var/local/eggs-2.5/SQLAlchemy-0.7.3-py2.5-linux-x86_64.egg/sqlalchemy/engine/base.py',
>>  line 1971 in commit
>>   self._do_commit()
>> File 
>> '/var/local/eggs-2.5/SQLAlchemy-0.7.3-py2.5-linux-x86_64.egg/sqlalchemy/engine/base.py',
>>  line 2059 in _do_commit
>>   self.connection._commit_twophase_impl(self.xid, self._is_prepared)
>> File 
>> '/var/local/eggs-2.5/SQLAlchemy-0.7.3-py2.5-linux-x86_64.egg/sqlalchemy/engine/base.py',
>>  line 1290 in _commit_twophase_impl
>>   self.engine.dialect.do_commit_twophase(self, xid, is_prepared)
>> File 
>> '/var/local/eggs-2.5/SQLAlchemy-0.7.3-py2.5-linux-x86_64.egg/sqlalchemy/dialects/oracle/cx_oracle.py',
>>  line 720 in do_commit_twophase
>>   self.do_commit(connection.connection)
>> File 
>> '/var/local/env/rdf/webapp/eggs/SQLAlchemy-0.7.3-py2.5-linux-x86_64.egg/sqlalchemy/engine/default.py',
>>  line 306 in do_commit
>>   connection.commit()
>> DatabaseError: ORA-24756: la transaction n'existe pas
>> 
>> 
>> SA integration in Zope in done throught my own "ztfy.alchemy" package, which 
>> is mainly a copy of "zope.alchemy" package to which I added several utility 
>> tools to facilitate engines definition via persistent utilities. But the 
>> transaction machinery is unchanged.
>> Session definition in getSession function is globally done as follows 
>> (simplified):
>> 
>> def getSession(dsn, status=STATUS_ACTIVE):
>>     engine = 
>> sqlalchemy.create_engine(dsn,echo,pool_size,pool_recycle,encoding,convert_unicode,strategy='threadlocal')
>>     factory = scoped_session(sessionmaker(bind=engine, twophase=True, 
>> extension=ZopeTransactionExtension()))
>>     session = factory()
>>     if join:
>>         join_transaction(session, initial_state=status)
>>     return session
>> 
>> 
>> Adding a few traces in ZopeTransactionException class also add these 
>> messages on the console:
>> 
>>  >>> after begin
>> 2012-11-20T10:01:12 INFO sqlalchemy.engine.threadlocal.TLEngine SELECT 
>> consultbo."MENU_ITEM_BO".id AS "consultbo_MENU_ITEM_BO_id", 
>> consultbo."MENU_ITEM_BO".id_app AS "consultbo_MENU_ITEM_BO_id_app", 
>> consultbo."MENU_ITEM_BO".id_appapp AS "consultbo_MENU_ITEM_BO_i_1", 
>> consultbo."MENU_ITEM_BO".niveau AS "consultbo_MENU_ITEM_BO_niveau", 
>> consultbo."MENU_ITEM_BO".libelle AS "consultbo_MENU_ITEM_BO_libelle", 
>> consultbo."MENU_ITEM_BO".rang AS "consultbo_MENU_ITEM_BO_rang", 
>> consultbo."MENU_ITEM_BO".nature AS "consultbo_MENU_ITEM_BO_nature" 
>> FROM consultbo."MENU_ITEM_BO" 
>> WHERE consultbo."MENU_ITEM_BO".id IN (:id_1, :id_2, :id_3, :id_4, :id_5, 
>> :id_6, :id_7, :id_8, :id_9)
>> 2012-11-20 10:01:12,266 INFO sqlalchemy.engine.threadlocal.TLEngine {'id_2': 
>> 8, 'id_3': 1, 'id_1': 11, 'id_6': 12, 'id_7': 13, 'id_4': 10, 'id_5': 5, 
>> 'id_8': 14, 'id_9': 15}
>> ------
>>  >>> after flush
>>  >>> before commit
>> ------
>> 2012-11-20T10:01:12 CRITICAL txn.139760315672320 A storage error occurred 
>> during the second phase of the two-phase commit.  Resources may be in an 
>> inconsistent state.
>>  >>> after rollback
>> 
>> 
>> Hope this can help to understand the problem; any advise is greatly 
>> welcome...
>> 
>> Best regards,
>> Thierry
>> 
>> 
>> 
>> 2012/11/20 Michael Bayer <mike...@zzzcomputing.com>
>> 
>> On Nov 19, 2012, at 6:23 PM, Thierry Florac wrote:
>> 
>> > As far as I can understand it, I'm globally OK with you but... probably
>> > not completely :-\
>> > I agree with the fact that SQLAlchemy is not the only package which
>> > takes part into the global transaction, as SA's session is handled by a
>> > Zope transaction manager. And the whole mechanism works globally
>> > perfectly.
>> > What I don't understand is that when:
>> > - I open a session,
>> > - I load a set of objects from the database,
>> > - I update these objects, setting an attribute with it's current value
>> >   (I know, said like that it can seems a little silly!),
>> 
>> no problem with that, it produces an event which will cause the flush to do 
>> something, but no UPDATE will be emitted in most cases if no net change
>> 
>> > - then when the transaction is committed, SA probably knows that the
>> >   objects were not really modified, because no UPDATE instruction is
>> >   executed;
>> 
>> yup
>> 
>> > but the SA two-phases transaction is begun, prepared and
>> >   aborts on commit because of the given Oracle error.
>> 
>> the first news to me here is that two-phase commit works with cx_oracle and 
>> SQLAlchemy.   I've never seen it work or been able to test it.
>> 
>> I'm also not familiar with this behavior.   Oracle won't allow PREPARE ?   
>> Can I see stack trace + full ORA message ?  the error code is especially 
>> significant, had no idea this was an Oracle error.
>> 
>> Also, is this two-phase as implemented by SQLAlchemy itself ?   You have 
>> "twophase=True" with sessionmaker() ?  I hope zope.sqlalchemy isn't 
>> hardcoding that..
>> 
>> 
>> > - but if I add a check to update objects attribute only if the new
>> >   value is different from the actual one, the SA transaction is begun
>> >   on my first request, but not prepared nor committed, and no error is
>> >   raised,
>> 
>> doesn't sound right.   If SA starts a transaction, it will also be 
>> PREPARE/COMMITTED because zope.sqlalchemy is calling commit() on the 
>> Session.    You can't be just leaving that new transaction hanging open.   
>> Or it might be rolled back, but its one or the other.
>> 
>> 
>> 
>> 
>> 
>> -- 
>> http://www.imagesdusport.com -- http://www.ztfy.org
>> 
>> -- 
>> 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.
> 
> 
> 
> -- 
> http://www.imagesdusport.com -- http://www.ztfy.org
> 
> -- 
> 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.



On Nov 20, 2012, at 6:33 PM, Thierry Florac wrote:

Hi Michael,

Following your guidelines, this is the first test I made against cx_Oracle :

>>> conn = cx_Oracle.connect('scott','tiger','localhost:1521/ORCL', twophase=True)
>>> id = random.randint(0, 2 ** 128)
>>> xid = (0x1234, "%032x" % id, "%032x" % 9)
>>> conn.begin(*xid)
>>> conn.prepare()
False
>>> conn.commit()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
DatabaseError: ORA-24756: la transaction n'existe pas

As you can see, the "prepare" method returns False, meaning that the transaction was *NOT* prepared.
If I add any update statement, everything is OK.

Oracle 11g documentation seems to indicate that the "prepare" call can return three "status" :

PreparedData on the node has been modified by a statement in the distributed transaction, and the node was successfully prepared.
Read-onlyNo data on the node has been, or can be, modified (only queried), so no preparation is necessary.
AbortThe node cannot successfully prepare.

cx_Oracle returns False "when there is nothing available to prepare", so probably at least in case 2, case for which "commit shouldn't be done" to avoid ORA-24756 error.
So it's behaviour seems conform with Oracle OCI documentation...

So my last question is : in the context of an SA session, who is responsible of the prepare/commit statements, so that commit is not called if prepared was not OK ? 

Best regards,
Thierry


2012/11/20 Michael Bayer <mike...@zzzcomputing.com>
Googling for the ORA code yields the usual tautological answers that say nothing.

The first step here would be to isolate the issue in a simple test case with SQLAlchemy only, and then cx_oracle where I suspect the bug lies.

The Session will not create a transaction at all, if no SQL is emitted.   So that would mean SQL is definitely emitted.  Can you confirm this ? (look at your SQL logging).

Then, an initial test would be like this:

s = Session(twophase=True, bind=my_oracle_engine)
s.execute("select 1 from dual")
s.commit()


Here's my test, against Postgresql:

from sqlalchemy.orm import Session
from sqlalchemy import create_engine

e = create_engine('postgresql://scott:tiger@localhost/test', echo=True)

s = Session(e, twophase=True)
s.execute("select 1")
s.commit()

the output:

2012-11-20 10:53:06,894 INFO sqlalchemy.engine.base.Engine select version()
2012-11-20 10:53:06,894 INFO sqlalchemy.engine.base.Engine {}
2012-11-20 10:53:06,897 INFO sqlalchemy.engine.base.Engine select current_schema()
2012-11-20 10:53:06,897 INFO sqlalchemy.engine.base.Engine {}
2012-11-20 10:53:06,900 INFO sqlalchemy.engine.base.Engine select 1
2012-11-20 10:53:06,900 INFO sqlalchemy.engine.base.Engine {}
2012-11-20 10:53:06,900 INFO sqlalchemy.engine.base.Engine PREPARE TRANSACTION '_sa_ddca4886b1f5db002e83058341de2609'
2012-11-20 10:53:06,900 INFO sqlalchemy.engine.base.Engine {}
2012-11-20 10:53:06,908 INFO sqlalchemy.engine.base.Engine COMMIT PREPARED '_sa_ddca4886b1f5db002e83058341de2609'
2012-11-20 10:53:06,908 INFO sqlalchemy.engine.base.Engine {}
2012-11-20 10:53:06,909 INFO sqlalchemy.engine.base.Engine BEGIN
2012-11-20 10:53:06,909 INFO sqlalchemy.engine.base.Engine {}

which I will explain, first there's a "BEGIN TWOPHASE" that unfortunately is not logged, I have just committed a fix for that in tip.  Then there's the PREPARE and the COMMIT - but this is Postgresql's syntax.  The Oracle implementation only uses an XID when it calls cx_oracle's "begin()" method, and then I'd assume its up to cx_oracle to do the rest.    So if there's a bug anywhere, it's probably in cx_oracle.   My guess is that cx_oracle itself is not emitting the BEGIN unless it sees DML (INSERT, UPDATE, DELETE).    The "BEGIN" you see afterwards is a tweak that's specific to the psycopg2 dialect when twophase is used so that it can run the next transaction.

A cx_oracle script would be:

import cx_Oracle
import random

conn = cx_Oracle.connect(...)

 id = random.randint(0, 2 ** 128)
xid = (0x1234, "%032x" % id, "%032x" % 9)  # no idea where i got this from, probably cx_oracle 
conn.begin(*xid)
conn.prepare()
conn.commit()


if that fails, but then if you were to insert in the middle:

cursor = conn.cursor()
cursor.execute("INSERT INTO sometable (q) VALUES (3)")

and then it works, there you go.   cx_oracle's bad behavior.

All of the "two phase" implementations required weird hacks to get them to work, as this feature is exceedingly rare.   But for cx_Oracle, I never saw it work completely hence we haven't been able to come up with workarounds for existing issues.  



On Nov 20, 2012, at 4:28 AM, Thierry Florac wrote:

Hi Michael,

This is a complete traceback produced by removing my checking code against attribute modification.
Original code is then as simple as this:

    @ajax.handler
    def updateOrderMenuItemBO(self):
        session = getSession(SESSION_NAME)
        ids = [ int(id) for id in self.request.form.get('ids') ]
        for menu in session.query(MenuItemBO).filter(MenuItemBO.id.in_(ids)):
            index = ids.index(menu.id) + 1
            menu.rang = index
        return getUtility(IJSONWriter).write({ 'status': 'OK' })


File '/var/local/env/rdf/lib/python2.5/site-packages/Paste-1.7.5.1-py2.5.egg/paste/evalexception/middleware.py', line 306 in respond
  app_iter = self.application(environ, detect_start_response)
File '/var/local/env/rdf/webapp/src/webapp/startup.py', line 33 in wrapper
  return app(environ, start_response)
File '/var/local/env/rdf/webapp/eggs/zope.app.wsgi-3.4.3-py2.5.egg/zope/app/wsgi/__init__.py', line 55 in __call__
  request = publish(request, handle_errors=handle_errors)
File '/var/local/env/rdf/webapp/eggs/zope.publisher-3.4.10-py2.5.egg/zope/publisher/publish.py', line 138 in publish
  publication.afterCall(request, obj)
File '/var/local/env/rdf/webapp/eggs/zope.app.publication-3.4.4-py2.5.egg/zope/app/publication/browser.py', line 78 in afterCall
  super(BrowserPublication, self).afterCall(request, ob)
File '/var/local/env/rdf/webapp/eggs/zope.app.publication-3.4.4-py2.5.egg/zope/app/publication/zopepublication.py', line 175 in afterCall
  txn.commit()
File '/var/local/env/rdf/webapp/eggs/ZODB3-3.8.5-py2.5-linux-x86_64.egg/transaction/_transaction.py', line 325 in commit
  self._commitResources()
File '/var/local/env/rdf/webapp/eggs/ZODB3-3.8.5-py2.5-linux-x86_64.egg/transaction/_transaction.py', line 432 in _commitResources
  rm.tpc_finish(self)
File '/var/local/src/ztfy/ztfy.alchemy/ztfy/alchemy/datamanager.py', line 148 in tpc_finish
  self.tx.commit()
File '/var/local/env/rdf/webapp/eggs/SQLAlchemy-0.7.3-py2.5-linux-x86_64.egg/sqlalchemy/orm/session.py', line 317 in commit
  t[1].commit()
File '/var/local/eggs-2.5/SQLAlchemy-0.7.3-py2.5-linux-x86_64.egg/sqlalchemy/engine/base.py', line 1971 in commit
  self._do_commit()
File '/var/local/eggs-2.5/SQLAlchemy-0.7.3-py2.5-linux-x86_64.egg/sqlalchemy/engine/base.py', line 2059 in _do_commit
  self.connection._commit_twophase_impl(self.xid, self._is_prepared)
File '/var/local/eggs-2.5/SQLAlchemy-0.7.3-py2.5-linux-x86_64.egg/sqlalchemy/engine/base.py', line 1290 in _commit_twophase_impl
  self.engine.dialect.do_commit_twophase(self, xid, is_prepared)
File '/var/local/eggs-2.5/SQLAlchemy-0.7.3-py2.5-linux-x86_64.egg/sqlalchemy/dialects/oracle/cx_oracle.py', line 720 in do_commit_twophase
  self.do_commit(connection.connection)
File '/var/local/env/rdf/webapp/eggs/SQLAlchemy-0.7.3-py2.5-linux-x86_64.egg/sqlalchemy/engine/default.py', line 306 in do_commit
  connection.commit()
DatabaseError: ORA-24756: la transaction n'existe pas


SA integration in Zope in done throught my own "ztfy.alchemy" package, which is mainly a copy of "zope.alchemy" package to which I added several utility tools to facilitate engines definition via persistent utilities. But the transaction machinery is unchanged.
Session definition in getSession function is globally done as follows (simplified):

def getSession(dsn, status=STATUS_ACTIVE):
    engine = sqlalchemy.create_engine(dsn,echo,pool_size,pool_recycle,encoding,convert_unicode,strategy='threadlocal')
    factory = scoped_session(sessionmaker(bind=engine, twophase=True, extension=ZopeTransactionExtension()))
    session = factory()
    if join:
        join_transaction(session, initial_state=status)
    return session


Adding a few traces in ZopeTransactionException class also add these messages on the console:

 >>> after begin
2012-11-20T10:01:12 INFO sqlalchemy.engine.threadlocal.TLEngine SELECT consultbo."MENU_ITEM_BO".id AS "consultbo_MENU_ITEM_BO_id", consultbo."MENU_ITEM_BO".id_app AS "consultbo_MENU_ITEM_BO_id_app", consultbo."MENU_ITEM_BO".id_appapp AS "consultbo_MENU_ITEM_BO_i_1", consultbo."MENU_ITEM_BO".niveau AS "consultbo_MENU_ITEM_BO_niveau", consultbo."MENU_ITEM_BO".libelle AS "consultbo_MENU_ITEM_BO_libelle", consultbo."MENU_ITEM_BO".rang AS "consultbo_MENU_ITEM_BO_rang", consultbo."MENU_ITEM_BO".nature AS "consultbo_MENU_ITEM_BO_nature" 
FROM consultbo."MENU_ITEM_BO" 
WHERE consultbo."MENU_ITEM_BO".id IN (:id_1, :id_2, :id_3, :id_4, :id_5, :id_6, :id_7, :id_8, :id_9)
2012-11-20 10:01:12,266 INFO sqlalchemy.engine.threadlocal.TLEngine {'id_2': 8, 'id_3': 1, 'id_1': 11, 'id_6': 12, 'id_7': 13, 'id_4': 10, 'id_5': 5, 'id_8': 14, 'id_9': 15}
------
 >>> after flush
 >>> before commit
------
2012-11-20T10:01:12 CRITICAL txn.139760315672320 A storage error occurred during the second phase of the two-phase commit.  Resources may be in an inconsistent state.
 >>> after rollback


Hope this can help to understand the problem; any advise is greatly welcome...

Best regards,
Thierry



2012/11/20 Michael Bayer <mike...@zzzcomputing.com>

On Nov 19, 2012, at 6:23 PM, Thierry Florac wrote:

> As far as I can understand it, I'm globally OK with you but... probably
> not completely :-\
> I agree with the fact that SQLAlchemy is not the only package which
> takes part into the global transaction, as SA's session is handled by a
> Zope transaction manager. And the whole mechanism works globally
> perfectly.
> What I don't understand is that when:
> - I open a session,
> - I load a set of objects from the database,
> - I update these objects, setting an attribute with it's current value
>   (I know, said like that it can seems a little silly!),

no problem with that, it produces an event which will cause the flush to do something, but no UPDATE will be emitted in most cases if no net change

> - then when the transaction is committed, SA probably knows that the
>   objects were not really modified, because no UPDATE instruction is
>   executed;

yup

> but the SA two-phases transaction is begun, prepared and
>   aborts on commit because of the given Oracle error.

the first news to me here is that two-phase commit works with cx_oracle and SQLAlchemy.   I've never seen it work or been able to test it.

I'm also not familiar with this behavior.   Oracle won't allow PREPARE ?   Can I see stack trace + full ORA message ?  the error code is especially significant, had no idea this was an Oracle error.

Also, is this two-phase as implemented by SQLAlchemy itself ?   You have "twophase=True" with sessionmaker() ?  I hope zope.sqlalchemy isn't hardcoding that..


> - but if I add a check to update objects attribute only if the new
>   value is different from the actual one, the SA transaction is begun
>   on my first request, but not prepared nor committed, and no error is
>   raised,

doesn't sound right.   If SA starts a transaction, it will also be PREPARE/COMMITTED because zope.sqlalchemy is calling commit() on the Session.    You can't be just leaving that new transaction hanging open.   Or it might be rolled back, but its one or the other.





--
http://www.imagesdusport.com -- http://www.ztfy.org

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



--
http://www.imagesdusport.com -- http://www.ztfy.org

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

Attachment: cx_oracle_prepare.patch
Description: Binary data

Reply via email to