在约束验证期间,将只读 API 请求缓存到 OS 组件

https://blueprints.launchpad.net/heat/+spec/constraint-validation-cache

Heat 会向 OpenStack 中的其他客户端发送大量请求。当同时尝试部署大量实例时,这些请求会导致一些开销。Heat 在验证属性约束期间执行的其中一项请求是检查外部资源是否存在(镜像、flavor 或其他)。为了减少这种开销,当前规范中提出了一种验证缓存机制。

问题描述

问题的详细描述在以下用例中说明

  1. 用户准备一个包含 N 个堆栈的模板,这些堆栈使用相同的资源(镜像、flavor、密钥对等)

  2. 用户请求 Heat 创建堆栈

  3. 在资源的自定义约束验证期间,Heat 执行以下操作

    • 找到验证自定义约束的适当类

    • 向其他客户端请求约束(检查卷、服务器、flavor 等是否存在)

    • 如果请求成功,则通过验证

这种方法会导致一些开销请求,因为我们需要多次请求相同的信息(镜像、flavor 等的存在)。此外,当前的实现会将这种开销翻倍,因为我们会在资源创建和堆栈验证期间两次检查属性约束。

提议的变更

期望的用例如下

  1. Heat 使用 dogpile.cache 初始化一个缓存后端和每个客户端插件的缓存区域(缓存配置在 heat.conf 中定义)。Heat 还为它们注册生成函数(有关更多信息,请参阅 http://dogpilecache.readthedocs.org/en/latest/usage.html

  2. 用户准备一个包含 N 个堆栈的模板,这些堆栈使用相同的资源(镜像、flavor、密钥对等)

  3. 用户请求 Heat 创建堆栈

  4. 在资源的自定义约束验证期间,Heat 执行以下操作

    • 找到验证自定义约束的适当类

    • 向另一个 OS 组件请求客户端插件的数据

    • 如果启用了缓存,则检查客户端插件的缓存区域,并返回具有相同资源名称(卷、服务器、flavor 等)和相同上下文的 API 请求结果。如果在缓存中未找到结果,则缓存区域会自动使用生成函数请求新值(请参阅下面的注释),否则使用 client_plugin 请求新值

    • 如果在请求过程中未引发任何异常,则通过验证

    • 如果引发了异常,则从缓存中删除该值,因为我们需要每次都请求它。

注意:如果缓存大小超过 heat 中的 size 选项,则需要刷新最旧的值。此逻辑应由缓存后端管理。

为了支持上述情况,应执行以下步骤

  • heat.conf 中应支持缓存配置选项

  • 应使用 heat.conf 中的选项配置缓存后端。请注意,使用 dogpile,我们可以使用多种类型的缓存后端(内存、memcached、文件系统、DB、自编写等)。每个后端都需要特定的输入参数。

  • 应为每个客户端配置缓存区域。此外,应使用 heat.conf 选项为客户端定义生存时间 (time-to-live) 值和缓存大小选项。

  • heat.engine.clients.ClientPlugin 的子类应请求有关新值的缓存区域,如果已启用缓存

  • 对客户端插件的请求应具有一个属性(use_cache=False),该属性允许定义是否应使用缓存。这允许仅对约束验证使用缓存,并避免意外使用缓存。

备选方案

  1. 在 BaseCustomConstraint 中实现缓存并将其集成。在这种情况下,缓存将仅用于自定义约束验证,但这并不是最佳解决方案,原因如下

    • 缓存未来不能用于其他目的

    • 从概念上讲,ClientPlugin 是缓存的更合适位置。在约束和缓存之间没有严格的 OOP 关系。

  2. 在客户端插件中实现轻量级缓存。此解决方案在审查期间被拒绝。请参阅下面的详细信息:“客户端插件在堆栈对象创建后,从资源首次访问时实例化。由于我们现在有 decouple-nested,这将变得不那么重要,因为每个嵌套堆栈都会重新创建这些客户端。并且在收敛时,所有资源都会重新创建客户端对象,因为资源操作将通过 rpc 发送以进行处理。所以,考虑到这一点,memcached 难道不是更好的选择吗?”

实现

负责人

主要负责人

kkushaev

里程碑

完成目标里程碑

Liberty-1

工作项

  • 实现缓存后端 - 利用 dogpile 后端来跟踪超时和先前请求的结果

  • 实现缓存区域的初始化

  • 将缓存集成到向其他客户端发出请求的 ClientPlugin 的子类型中

  • 为每个步骤准备测试

依赖项