2011-04-26 102 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