2

我通过官方的 sqlanydb 驱动程序将 Twisted 11 与 SQLAnywhere 12 一起使用。

一般来说,它工作正常。

但有时应用程序在第一次查询时会因中止而崩溃。

如果一个查询有效,那么以下所有查询也有效。但是我的测试很少通过。

这太糟糕了,而且 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
4

2 回答 2

2

看起来您的数据库库不是线程安全的。为了使其稳定连接,请执行以下操作:

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

这会将最大并发设置为 1,并且 ThreadPool 将被限制为 1 个线程,这意味着不会同时运行任何查询。这应该可以阻止您的非线程安全库给您带来任何麻烦,同时仍然在线程中运行查询并且不会阻塞主循环。

于 2011-05-04T07:54:21.313 回答
1

是的,您的延期清单看起来不会按照您的意愿行事。每个 runQuery 都将在 adbapi 线程池中运行,因此无法保证这些查询的顺序。“LAST_UPDATE_SQL”作为 DeferredList 中的最后一件事不一定会最后发生。延迟列表中的查询是否应该是单个事务的一部分?

不知道这里的 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
于 2011-04-27T16:08:00.093 回答