在域模型中添加区分层

https://blueprints.launchpad.net/glance/+spec/add-distinction-layer

为了提高稳定性和性能,建议通过添加区分层来修改域,该层将存储对象的全部更改,并仅将修改后的属性传递给数据库。

问题描述

当前的 Glance Images v2 架构存在一个缺陷,该缺陷与系统高负载下可能出现的竞争条件风险相关。以下是一个竞争条件的示例:假设一个镜像具有属性 {"foo": "bar", "goo": "far"}。User1 想要更改 {"foo": "baz"}。User2 想要更改 {"goo": "yuck"}。如果 User1 和 User2 同时发出请求,他们都会获得相同的数据进行处理。假设 User1 的更改首先写入,结果为:{"foo": "baz", "goo": "far"}。然后 User2 的更改被写入,结果为:{"foo": "bar", "goo": "yuck"}。User1 的更改已被撤销,这不是期望的行为。

除此之外,在更新时数据库负载不合理增加也是一个问题。之所以出现此问题,是因为每次更新都会更新所有镜像字段,无论它们是否被修改,因此数据库上存在大量不必要的流量。此外,镜像状态更改也存在问题。

这些问题在低负载下不会出现,但未来随着更广泛的使用,可能会导致严重问题,例如信息丢失和不稳定行为。

有关问题的更详细描述,请参阅参考文献 [7][8]

提议的变更

在更新镜像时,会从数据库中检索原始镜像。区分层创建修改后的镜像的字典表示,并将其与原始镜像进行比较。然后,它将修改后的属性字典传递给数据库层。

继续上一节的示例,考虑 User1 的更新处理完毕后,数据库中为 {"foo": "baz"} 的情况。User2 的区分层镜像记录包含 {"foo": "bar"},因为这是获取数据时的镜像状态。但是,由于 User2 的更改请求不涉及 foo,因此只会将 {"goo": "yuck"} 写入数据库。因此,最终的镜像记录将具有 {"foo": "baz", "goo": "yuck"},正如两位用户所期望的那样。当然,如果 User2 实际上更改了 foo 的值,那么 User2 的更改将被持久化到数据库中,但这是预期的行为。

备选方案

曾尝试使用继承来解决该问题。有关提交的参考资料 [1][2]。但提交被放弃,因为继承与 Glance 的设计策略相悖,即倾向于扁平的层次结构,并更喜欢聚合而不是继承。

或者,有人建议在镜像处于“保存”状态时禁止更新。但这个想法被拒绝了,因为镜像往往会处于“保存”状态较长时间,并且用户期望在此期间能够修改镜像记录。此外,它仅修复了状态为“保存”的特殊情况,但竞争条件和数据库负载不合理增加的整体问题仍然存在。

数据模型影响

REST API 影响

安全影响

通知影响

其他最终用户影响

性能影响

进行了性能测试。我们使用的数据库是:MySQL Ver 14.14 Distrib 5.5.47 我们确定了更新镜像的一个参数所花费的时间。测试表明执行时间与请求数量有关。测试代码在 [3] 中提供。

我们测试了两个版本的代码:1) “master” 分支;2) 提交 “Add distinction layer” [6]

结果在表格 [4] 和图表 [5] 中提供。

图表显示了执行时间与请求数量的依赖关系:* 蓝色线 - “master” 分支;* 红色线 - 提交 “Add distinction layer” [6]

测试得出结论,使用区分层可使平均更新速度提高 34.54%。

其他部署者影响

开发人员影响

实现

负责人

dshakhray

评审人员

mfedosin nikhil rosmaita

工作项

  1. 实现区分层
    1. 创建一个区分层并将其添加到 ‘gateway.py’

    2. 添加类 ImageProxy、ImageRepoProxy、ImageFactoryProxy。

    3. 将 get、add、save 方法添加到 ImageRepoProxy 类。

    4. 将其他方法添加到 ImageRepoProxy 类以实现逻辑。

    5. 对 ‘db/simple/api.py’、‘db/simple/api.py’、‘db/__init__.py’ 进行适当的更改。

  2. 添加相关的功能和单元测试。

  3. 编写关于该层的文档。

依赖项

测试

将根据需要添加单元和功能测试。

文档影响

所有更改都必须在相关的文档部分中详细描述。

参考资料