2013-01-11 65 views
2

我們有一個使用collective.tin,collective.lead和SqlAlchemy將MySql數據庫與Plone集成的混合Web應用程序(最後一次升級到Plone 4.0)。Plone + SqlAlchemy + MySql中的未提交事務

好吧,我知道collective.tin從未發佈過,並且collective.lead已被取代;然而,所有的事情幾年以來都幾乎完美地發揮作用。

最近我們經歷了一個非常奇怪的行爲,並尋求幫助,以瞭解它。

其中,我們有2個Plone內容類型,比如A和B,通過子類化collective.tin和相應的innodb MySql表定義; B的行具有朝向外部的鍵。

在15-20分鐘的時間跨度內,2個不同的用戶創建了3個A對象和一些10-20B對象,這些對象未提交給MySql但由Plone建立索引;我從linux shell中用MySql客戶端執行的查詢無法找到那些A行(沒有查找B行);然而,這兩個用戶以及其他用戶通過Web應用程序(上述組件堆棧)執行的查詢有時仍在查找並正確顯示這些3A對象中的一些對象。

只有在我重新啓動Zope實例後,纔有可能從Plone Web界面恢復正常活動; 3 MySql數據庫中仍缺少A行和許多B行,但自動增量計數器顯示了預期的增量;我不得不從Plone索引中爲A對象移除3個無效的大腦(不用擔心B對象)。

對可能的原因和如何調查問題的任何建議?

+0

的什麼版本'sqlalchemy'被使用? –

+0

sqlalchemy 0.4.8;因爲我使用了collective.tin 0.3(svn /未發佈),我記得我無法使用更新版本的sqlalchemy。 – gtoffoli

回答

4

我們對sqlalchemy 0.4有同樣的問題;會話將與實際的數據庫內容不同步。在我們的例子中,這個問題有點被掩蓋了,因爲用戶通過會話關聯被髮送到羣集中的特定後端。如果親和力突然消失,消息就消失了。確切的細節有點朦朧,因爲我找不到正確的(古代)修正歷史記錄。

從我可以從上下文中收集到的是,會話標識映射可以防止會話從數據庫中獲取它之前檢索到的對象。因此它不會在不同的會話中看到對這些對象所做的更改。

修復是在每次提交或回滾後在會話上調用.expire_all(); SQLAlchemy 0.5和更高版本會自動執行此操作(會話中的autoexpire=True,我相信現在稱爲expire_on_commit),但對於0.4,您需要註冊SessionExtension才能爲您完成此操作。

幸運的是,我們還可以使用collective.lead這個項目,所以我的解決方法是你的修復:

# The identity map should be flushed on commit. 
# SQLAlchemy 0.5 does this properly, but in 0.4 we need to do this via 
# a SesssionExtension. 

from sqlalchemy import __version__ 
if __version__[:3] == '0.4': 
    from sqlalchemy.orm.session import SessionExtension 

    class ExpireAllSessionExtension(SessionExtension): 
     def after_commit(self, session): 
      """Expire the identity-map on commit""" 
      session.expire_all() 

     def after_rollback(self, session): 
      """Expire the identity-map on rollback""" 
      session.expire_all() 

    def installExtension(): 
     # Patch collective.lead.database to let us install the extension 
     # on the session created there. 
     from collective.lead.database import Database 
     old_session = Database.session.fget 
     def session(self): 
      session = old_session(self) 
      if session.extension is None: 
       session.extension = ExpireAllSessionExtension() 
      return session 
     Database.session = property(session) 
else: 
    def installExtension(): 
     pass 

當定義映射器,在安裝這個擴展有:

from .sessionexpiration import installExtension 

# Ensure that sessions get properly expired on commit and rollback. 
installExtension() 
+0

非常感謝Martijn! 我還沒有嘗試過你的修補程序,但根據你寫的內容,我也認爲我們非常幸運找到一個願意在這個特定主題上分享他的經驗的人。 – gtoffoli