问题描述
如何保护数据库在初始化之前不被访问?
我有一个数据库。需要初始化。这可能会花费一些时间,因此初始化会返回Deferred。我们称其为延迟的d_db_ready
。
我还有其他方法,例如read_a_value_from_the_database()
,该方法仅在初始化数据库之后才想访问数据库。
容易!我向d_db_ready
添加了回调:
d_value = d_db_ready.addCallback(read_a_value_from_the_database)
现在,可以从延迟的d_value
中找到我希望从数据库中获得的值。
让我们再次尝试:
d_value2 = d_db_ready.addCallback(read_a_value_from_the_database)
这一次不起作用。这是因为d_db_ready
的回调链现在以我们从数据库中读取的值结尾。它不再提供对数据库的访问。
此处应使用哪种模式?我应该如何保护数据库在初始化之前不被访问?
read_a_value_from_the_database
的一个选项是返回数据库...但是这样就不会返回值。此外,这种方法一定程度上导致了潜在的潜在错误:当我忘记返回数据库时,我将不得不追查先前调用的函数,而当我只剩下当前正在调用的函数时。 / p>
下面是一些可运行的代码来演示我的问题:
from twisted.internet.defer import Deferred
database = "uninitialized"
d_db_ready = Deferred()
def init_database():
global database
print("Initializing database")
database = { "value": 1 }
d_db_ready.callback(database)
def read_a_value_from_the_database(db):
value = db["value"]
print("value:",value)
return value
d_db_ready.addCallback(read_a_value_from_the_database)
d_db_ready.addCallback(read_a_value_from_the_database) # <--- errors
init_database()
解决方法
在数据库准备就绪之前,您所显示的模式已经很好地防止了数据库的使用。它的问题是它不支持多种用途。第二个回调不会失败,因为它具有未初始化的数据库。它只会在初始化数据库后失败,因为它会获得先前操作的结果而不是数据库的结果。
有两个常见选项。一种是停止具有多种用途。这并不听起来那么荒谬。例如:
def main():
...
d_db_ready.addCallback(run_the_program)
...
def run_the_program(database):
d_a = read_a_value_from_the_database(database)
d_b = read_a_value_from_the_database(database)
...
现在d_db_ready
上唯一的回调是run_the_program
,run_the_program
负责将数据库传递到需要去的地方。
第二个选项是使您的就绪API支持多种用途。您可以在此处采用多种不同的实现方法,但它们都以打破Deferred的结果链接功能为中心。例如,您可以这样做:
def wait_for_db(callback):
def safe_callback(database):
try:
callback(database)
except:
# logging or support an errback or something
return database
d_db_ready.addCallback(safe_callback)
safe_callback
确保d_db_ready
的结果始终是数据库,而不是某些随机回调的结果。
可能有更好的实现此想法的方法(例如,仍然基于Deferred
的实现,而不是放弃传递裸露的回调的能力)。希望这个简单的方法可以使您大致了解。
基于@ Jean-PaulCalderone回答中的第二个选项,这是基于Deferreds的实现:
def get_database():
d = Deferred()
def when_ready(db):
d.callback(db)
return db
d_db_ready.addCallback(when_ready)
return d
get_database().addCallback(read_a_value_from_the_database)
get_database().addCallback(read_a_value_from_the_database) # <--- works correctly
init_database()
请注意,d_db_ready
现在应该被认为是功能get_database()
的专用,并且不再应由用户访问。