2011-04-26 62 views
2

我通過官方的sqlanydb驅動程序與SQLAnywhere 12一起使用Twisted 11。Python + Twisted + sqlanydb = abort()

通常,它工作正常。

但是偶爾應用程序會在第一個查詢中異常終止。

如果一個查詢工作,所有以下的工作。但是我的測試很少通過。

這是可怕的發展和strace並沒有告訴我任何信息太多。有時它崩潰在select()內,有時在mmap()...

我正在運行64位Linux並在本地運行Sybase作爲dbeng12進行測試。

是否有人正在成功使用這些組件?任何建議如何解決?我以前用過Django的sqlanydb,它從不崩潰。

使用打印,我發現它崩潰DeferredList內,重要的代碼基本上是以下幾點:

class WhoisDb(object): 
    # ... shortened ... 
    def _get_contacts(self, dom): 
     if not dom: 
      self.d.errback(UnknownDomain(self._get_limit())) 
      return 
     self.dom = Domain._make(dom[0]) 

     dl = defer.DeferredList([ 
      self.dbpool.runQuery(CON_SQL, (self.dom.dom_owner,)), 
      self.dbpool.runQuery(CON_SQL, (self.dom.dom_admin,)), 
      self.dbpool.runQuery(CON_SQL, (self.dom.dom_tech,)), 
      self.dbpool.runQuery(
       LAST_UPDATE_SQL, 
       (self.dom.domName,)), ]).addCallback(self._fmt_string) 

    def get_whois(self, domain): 
     self.d = defer.Deferred() 
     if not self._check_limit(): 
      self.d.errback(LimitExceeded(MAX_PER_HOUR)) 
     elif not RE_ALLOWED_TLDS.match(domain): 
      self.d.errback(UnknownDomain(self._get_limit())) 
     else: 
      self.dbpool.runQuery(
        'select ' + DOM_FIELDS + ' from domains where ' 
        'domain = ? or domain_idn = ?', 
        (domain, domain,)) \ 
          .addCallback(self._get_contacts) 

     return self.d 

_fmt_string(),如果它崩潰不叫。

GDB裏,這是一個簡單的SIGSEV:

(gdb) run ~/.virtualenvs/whois/bin/trial test.test_protocol.ProtocolTestCase.test_correct_domain 
Starting program: /home/hynek/.virtualenvs/whois/bin/python ~/.virtualenvs/whois/bin/trial test.test_protocol.ProtocolTestCase.test_correct_domain 
[Thread debugging using libthread_db enabled] 
test.test_protocol 
    ProtocolTestCase 
    test_correct_domain ... [New Thread 0x7ffff311a700 (LWP 6685)] 
[New Thread 0x7ffff3099700 (LWP 6686)] 
[New Thread 0x7ffff27dc700 (LWP 6723)] 
[New Thread 0x7ffff1fdb700 (LWP 6724)] 
[New Thread 0x7ffff17da700 (LWP 6725)] 
[New Thread 0x7ffff0fd9700 (LWP 6729)] 

Program received signal SIGSEGV, Segmentation fault. 
[Switching to Thread 0x7ffff1fdb700 (LWP 6724)] 
0x00007ffff4d4167c in ??() from /opt/sqlanywhere12/lib64/libdbcapi_r.so 
+1

也許這是由一個陳舊的連接到數據庫造成的?這隻會在您的應用程序運行一段時間(比如說5分鐘以上)沒有任何活動之後崩潰嗎?在運行任何查詢之前,您可以嘗試檢查連接的狀態嗎? 我也很好奇你是如何將它整合到Twisted中的。 – stderr 2011-04-26 12:46:20

+0

我可以取消資格。我在每個setUp()中重新啓動數據庫服務器以防止這種影響。它無論如何都會立即發生。我在init之後添加了一個sleep()來規避可能的競賽消息,但它沒有幫助。 – hynek 2011-04-26 12:49:51

+0

哦,這在你的測試套件中失敗了嗎?這實際上是我認爲的好消息。選擇/ mmap類型指向Twisted中的崩潰。 SqlAnyDb不包含CExtension,是嗎?您是否在使用其他CExtension模塊? 您正在使用Twisted Trial運行測試套件? – stderr 2011-04-26 15:49:49

回答

2

它看起來像你的數據庫庫不是線程安全的。爲了使之成爲穩定的連接,這樣做:

self.dbpool = ConnectionPool(..., cp_min=1, cp_max=1) 

這將最大併發性設置爲1,和線程池將被限制爲1個線程,這意味着沒有查詢將同時運行。這應該會阻止非線程安全的庫導致您遇到任何戲劇,同時仍然在線程中運行查詢並且不阻止主循環。

+0

另一個允許同時允許多個查詢的解決方案是使用ODBC。我寫了一篇關於可能遇到的問題的[博客文章](http://hynek.me/blog/2011/04/twisted-sybase/)。 – hynek 2011-05-04 08:33:29

1

呀,你的遞延列表看起來像它不會做你想要什麼。每個runQuery都將在adbapi線程池中運行,因此不能保證這些查詢的排序。作爲DeferredList中最後一件事的「LAST_UPDATE_SQL」不一定會最後發生。延期列表中的查詢應該是單個事務的一部分嗎?

不確切知道SQL查詢在這裏我假設有時一個事務已經爲您的LAST_UPDATE_SQL設置,有時它還沒有設置,取決於那些runQuery最終實際運行的順序。

以下是如何使用adbapi.runInteraction用單個adbapi線程替換延遲列表。我不是100%相信這會解決您的問題,但我認爲這是編寫您嘗試執行的那種數據庫交互的正確方法。

class WhoisDb(object): 
    # ... shortened ... 
    def _get_contacts(self, dom): 
     if not dom: 
      self.d.errback(UnknownDomain(self._get_limit())) 
      return 
     self.dom = Domain._make(dom[0]) 

     d = self.dbpool.runInteraction(
       self._get_stuff_from_db 
      ) 
     d.addCallback(self._fmt_string) 
     d.addErrback(self._fmt_string) # don't forget to add an errback! 
     return d 

    def _get_stuff_from_db(self, cursor): 
     cursor.execute(CON_SQL, (self.dom.dom_owner,)), 
     cursor.execute(CON_SQL, (self.dom.dom_admin,)), 
     cursor.execute(CON_SQL, (self.dom.dom_tech,)), 
     cursor.execute(
      LAST_UPDATE_SQL, 
      (self.dom.domName,)), ]) 
     return cursor.fetchall() # or whatever you need to return obviously 
+0

謝謝你的努力,我試了一下就像你所建議的那樣,不幸的是沒有成功。注意:它們不需要在事務內部,查詢是完全獨立的,它們只需要相同的參數。打印顯示,中止發生__離開'_get_contacts()',但在進入'_get_stuff_from_db()'之前。 – hynek 2011-04-27 17:02:57