ЗОДБ с Торнадо
У меня есть небольшое веб-приложение, созданное с использованием 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()
Во-первых, новое соединение все еще необходимо для всех обработчиков, взаимодействующих с БД, или только для тех, которые делают запись? Было бы разумно установить одно соединение при запуске и использовать его для всех моих операций чтения, а затем выполнять вышеуказанное соединение "Петь и танцевать" только тогда, когда мне нужно что-то написать?
Во-вторых, каков идиоматический способ абстрагирования этого паттерна в 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)
в виду, но это, вероятно, мой показ на Лиспе.
Общая проверка на подходе также будет приветствоваться.
1 ответ
Вам нужно создать новое соединение для каждого потока. 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
Этот менеджер контекста берет объект базы данных и возвращает корневой объект, автоматически закрывая соединение при повторном выходе из контекста.