扭曲延迟模式以保护未初始化的数据库

2024-10-03 13:21:23 发布

您现在位置:Python中文网/ 问答频道 /正文

我应该如何保护数据库在初始化之前不被访问

我有一个数据库。它需要初始化。这可能需要时间,因此初始化将返回延迟的。我们称之为延迟d_db_ready

我还有其他方法,比如说read_a_value_from_the_database(),它们希望访问数据库,但只有在数据库初始化之后

轻松点!我将回调添加到d_db_ready

d_value = d_db_ready.addCallback(read_a_value_from_the_database)

现在,我希望从数据库中得到的值可以在Deferredd_value中找到

让我们再试一次:

d_value2 = d_db_ready.addCallback(read_a_value_from_the_database)

这次不行了。这是因为d_db_ready的回调链现在以我们从数据库读取的值结束。它不再允许访问数据库

这里应该使用什么模式?在初始化数据库之前,我应该如何保护它不被访问

一个选项是read_a_value_from_the_database返回数据库。。。但这样就不会返回值。此外,这种方法是一个可怕的潜在错误的来源:当我忘记返回数据库时,我必须找出以前调用过的函数,而我所拥有的只是当前调用的函数

下面是一些可运行的代码来演示我的问题:

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()


Tags: the方法函数from数据库readdbinit
2条回答

根据@Jean-PaulCalderone回答中的第二个选项,下面是一个基于延迟的实现:

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()的私有函数,用户不应该再访问它

您展示的模式已经很好地防止了在数据库准备就绪之前使用数据库。它的问题是不支持多种用途。第二次回调不会失败,因为它得到了一个未初始化的数据库。它只有在数据库初始化后才会失败,因为它获取的是先前操作的结果,而不是数据库

有两种常见的选择。一是停止多种用途。这并不像听起来那么荒谬。例如:

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支持多种用途。这里有许多不同的实现方法,但它们都围绕着打破延迟的结果链接特性。例如,您可以这样做:

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的实现,而不是放弃传递裸回调的功能)。希望这个简单的例子能给你一个大致的想法

相关问题 更多 >