在域模型中添加区分层¶
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 的更改已被撤销,这不是期望的行为。
除此之外,在更新时数据库负载不合理增加也是一个问题。之所以出现此问题,是因为每次更新都会更新所有镜像字段,无论它们是否被修改,因此数据库上存在大量不必要的流量。此外,镜像状态更改也存在问题。
这些问题在低负载下不会出现,但未来随着更广泛的使用,可能会导致严重问题,例如信息丢失和不稳定行为。
提议的变更¶
在更新镜像时,会从数据库中检索原始镜像。区分层创建修改后的镜像的字典表示,并将其与原始镜像进行比较。然后,它将修改后的属性字典传递给数据库层。
继续上一节的示例,考虑 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]。
图表显示了执行时间与请求数量的依赖关系:* 蓝色线 - “master” 分支;* 红色线 - 提交 “Add distinction layer” [6]。
测试得出结论,使用区分层可使平均更新速度提高 34.54%。
其他部署者影响¶
无
开发人员影响¶
无
实现¶
负责人¶
dshakhray
评审人员¶
mfedosin nikhil rosmaita
工作项¶
- 实现区分层
创建一个区分层并将其添加到 ‘gateway.py’
添加类 ImageProxy、ImageRepoProxy、ImageFactoryProxy。
将 get、add、save 方法添加到 ImageRepoProxy 类。
将其他方法添加到 ImageRepoProxy 类以实现逻辑。
对 ‘db/simple/api.py’、‘db/simple/api.py’、‘db/__init__.py’ 进行适当的更改。
添加相关的功能和单元测试。
编写关于该层的文档。
依赖项¶
无
测试¶
将根据需要添加单元和功能测试。
文档影响¶
所有更改都必须在相关的文档部分中详细描述。