4

我有一个用Tornado构建的小型网络应用程序,我想在其中使用ZODB进行一些数据存储。根据 ZODB 文档,支持多线程程序,但它们应该为每个线程启动一个新连接。我认为这意味着我必须做类似的事情

### On startup
dbFilename = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Data.fs")
db = DB(FileStorage(dbFilename))

### Example handler
class Example(tornado.web.RequestHandler):
    def get(self):
        try:
            conn = db.open()
            root = conn.root()
            ### do stuff with root here
            root._p_changed = 1  ## Include these lines for writes
            transaction.commit() ## on sub-elements
        finally:
            conn.close()

首先,对于所有 db-interacting 处理程序还是只需要进行写入的处理程序,新连接是否仍然需要?在启动时启动一个连接并将其用于我所有的阅读是否合理,然后仅在我需要写东西时才进行上述连接歌舞?

其次,在 Python 中抽象该模式的惯用方式是什么?我有类似的东西

def withDB(fn):
    try:
        conn = db.open()
        root = conn.root()
        res = fn(root)
        root._p_changed = 1
        transaction.commit()
        return res
    finally:
        conn.close()

def delete(formName):
    def local(root):
        ### do stuff with root here
    return withDB(local)

记住,但这可能是我的 Lisp 展示。

也欢迎对该方法进行一般性的头部检查。

4

1 回答 1

4

您需要为每个线程创建一个新连接。ZODB 为每个连接提供一致的每个事务视图(MVCC,多视图并发控制),因此即使是读取,您也需要一个单独的连接。该连接可以重新用于一个线程中的顺序请求。

因此,对于连接,我将使用由 提供的每个线程池ZODB.DB,可能会缓存每个请求的连接(如pyramid_zodbconn所做的那样)。

在您的请求处理程序中,您可以将事务管理器用作上下文管理器:

class Example(tornado.web.RequestHandler):
    def get(self):
        connection = some_connection_pool.get_connection()
        with transaction.manager:
            root = conn.root()
            res = fn(root)
            root._p_changed = 1

使用transaction.manager对象作为上下文管理器可确保事务在进入时启动,并在退出时无异常提交,在退出时异常中止。

您也可以创建一个上下文管理器来处理 ZODB 连接:

from contextlib import contextmanager

@contextmanager
def zodbconn(db):
    conn = db.open()
    yield conn.root()
    conn.close()

然后将其与事务管理器一起用作上下文管理器:

class Example(tornado.web.RequestHandler):
    def get(self):
        with zodbconn(db) as root, transaction.manager:
            res = fn(root)
            root._p_changed = 1

这个上下文管理器获取数据库对象,并返回根对象,当上下文再次退出时自动关闭连接。

于 2013-04-09T19:56:56.750 回答