我有一个使用Tornado构建的小型网络应用,我想使用ZODB进行一些数据存储。根据ZODB文档,multi-threaded programs are supported, but they should start up a new connection per thread。我认为这意味着我必须做一些像
这样的事情### 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()
首先,对于所有与数据库交互的处理程序还是仅进行写入的处理程序,新连接是否仍然是必需的?在启动时启动一个连接并将其用于我的所有读取是否合理,然后只在我需要编写内容时才进行上述连接歌曲和舞蹈?
其次,在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表现。
也欢迎对这种方法进行一般性的核查。
答案 0 :(得分: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
对象作为上下文管理器可确保事务在enter时启动,并在退出时无异常地提交,在退出时中止,但异常。
您也可以创建一个上下文管理器来处理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
此上下文管理器获取数据库对象,并返回根对象,在再次退出上下文时自动关闭连接。