This work is licensed under a Creative Commons Attribution 3.0
Unported License.
http://creativecommons.org/licenses/by/3.0/legalcode
我们的提议是赋予 Swift 用户更改容器及其包含对象的存储策略的能力。
Swift 目前禁止用户更改容器的存储策略,这种限制至少会带来两个问题。
一个问题是灵活性不足。例如,有一个组织使用 Swift 作为办公数据的备份存储,并且每月以日期命名的容器(如 ‘backup-201502’)进行归档。较旧的归档变得不那么重要,因此用户希望减少存储容量。然后 Swift 用户会尝试将容器的存储策略更改为更便宜的策略,如 ‘2-副本策略’ 或 ‘EC 策略’,但他们会感到非常失望,因为一旦创建容器就无法更改其策略。解决此问题的变通方法是创建具有其他存储策略的新容器,然后将所有对象从现有容器复制到新容器,但这种变通方法又会带来另一个问题。
另一个问题是可访问性。将所有文件复制到其他容器会导致所有文件的 URL 发生变化。这会让用户感到困惑和沮丧。解决此问题的变通方法是在将所有文件复制到新容器后,用户删除旧容器并创建具有其他存储策略的同名容器,然后将所有对象复制回原始名称的容器。然而,这显然需要比单次复制更繁重的工作量和更长的时间。
环通常因策略而异,因此,策略 1 的 ‘a/c/o’ 对象很可能被放置在与策略 0 的 ‘a/c/o’ 对象不同的节点设备上。因此,与策略更改相关的对象替换需要很长时间和大量的内部流量。出于这个原因,用户更改策略的请求必须转换为在存储节点之间传输对象的异步行为,该行为由后台守护进程驱动。显然,Swift 在更改策略期间不得暂停任何用户的存储或获取信息的请求。
我们需要添加或修改 Swift 服务器和守护进程的行为如下
服务器的更改
守护进程的更改
目前,Swift 对于带有 X-Storage-Policy 头的用户 POST 容器请求返回 “204 No Content”。这表示“未执行任何操作”。为了保持向后兼容性并避免意外执行,我们希望保持这种行为不变。因此,我们建议引入新的头来“强制”执行策略更改,如下所示。
| 参数 | 风格 | 类型 | 描述 |
|---|---|---|---|
| X-Forced-Change-Storage-Policy: <policy_name> (可选) | header | xsd:string | 将容器的存储策略更改为 ‘policy_name’ 指定的策略。此更改伴随着异步后台进程来传输对象。 |
此 API 的可能响应如下:
| 代码 | 注意事项 |
|---|---|
| 202 Accepted | 正确接受请求并开始准备对象替换。 |
| 400 Bad Request | 拒绝带有已弃用或未在配置文件中定义的策略的请求。 |
| 409 Conflict | 拒绝请求,因为另一个更改策略过程尚未完成(与 3-c 更改相关) |
当策略更改请求被接受(响应代码为 202)时,目标容器会存储以下两个 sysmeta:
| Sysmeta | 注意事项 |
|---|---|
| X-Container-Sysmeta-Prev-Index: <int> | “预更改”策略索引。它将用于尚未传输到新策略的 GET 或 DELETE 对象。 |
| X-Container-Sysmeta-Objects-Queued: <bool> | 这将用于守护进程确定策略更改的状态。如果为 False,则接受了策略更改请求但尚未准备好对象传输。如果为 True,则对象已排队到用于策略更改的特殊容器,因此已准备好进行传输。如果未定义,则未请求对该容器进行策略更改。 |
由于以下两个原因,此功能应作为 ‘change-policy’ 中间件实现
对象将由后台进程逐步传输。从 Swift 操作员的角度来看,了解策略更改的进度非常重要,即有多少对象已经传输或仍未传输。这可以通过简单地为每个存储策略公开容器 DB 文件的 policy_stat 表来实现。每个策略的状态将通过以下方式公开X-Container-Storage-Policy-<Policy_name>-Bytes-Used和X-Container-Storage-Policy-<Policy_name>-Object-Countheader 如下
$ curl -v -X HEAD -H "X-Auth-Token: tkn" http://<host>/v1/AUTH_test/container
< HTTP/1.1 200 OK
< X-Container-Storage-Policy-Gold-Object-Count: 3
< X-Container-Storage-Policy-Gold-Bytes-Used: 12
< X-Container-Storage-Policy-Ec42-Object-Count: 7
< X-Container-Storage-Policy-Ec42-Bytes-Used: 28
< X-Container-Object-Count: 10
< X-Container-Bytes-Used: 40
< Accept-Ranges: bytes
< X-Storage-Policy: ec42
< ...
以上响应表明完成了 70% 的对象传输。
在我目前的考虑中,对象 PUT 应该只完成到新策略。这不会影响旧策略中的任何对象,从而简化了更改策略的过程。因此,获取对象的最佳方法是首先根据新策略的环向对象服务器发送 GET 请求,如果响应代码为 404 NOT FOUND,则代理服务器会将 GET 请求重新发送到旧策略的对象服务器。
但是,此行为仍在讨论中,因为两次向对象服务器发送 GET/HEAD 请求可能会增加用户 GET 对象请求的延迟,尤其是在策略更改的早期阶段。
为了将对象排队到策略更改列表中,某些进程必须监视容器是否请求更改其策略。将此任务添加到 container-replicator 似乎是最佳方法,因为 container-replicator 最初的作用是搜索所有容器 DB 以检查 Swift 集群的完整性。因此,这可以最大限度地减少添加此新功能的锁定容器 DB 的额外时间。
Container-replicator 将检查容器是否具有X-Container-Sysmeta-Objects-Queuedsysmeta 及其值为 False。该容器中的对象应排队到用于策略更改的特殊容器的对象列表中。该特殊容器是在特殊帐户下创建的`.change_policy`。特殊容器的名称应唯一且与请求更改策略的容器具有一对一关系。特殊容器的名称定义如下:<account_name>:<container_name>。这个特殊的帐户和容器由新的守护进程访问object-transferrer,它真正地将对象从旧策略传输到新策略。
Object-transferrer 是新引入的守护进程,用于更改策略。Object-transferrer 从帐户读取特殊容器列表`.change_policy`并从每个特殊容器读取对象列表。Object-transferrer 使用内部客户端将这些对象从旧策略传输到新策略。在对象成功传输到新策略后,旧策略中的对象将通过 DELETE 方法删除。
如果 transferrer 完成传输特殊容器中的所有对象,它将删除特殊容器并删除 sysmetaX-Container-Sysmeta-Prev-Index和X-Container-Sysmeta-Objects-Queued从容器中,将该容器的状态从 IN-CHANGING 更改为正常 (POLICY CHANGE COMPLETED)。
| 步骤 | 描述 | 容器 /a/c 对象 | 容器 /a/c/ 元数据 | 容器 /.change_policy/a:c 对象 |
|---|---|---|---|---|
0
|
初始化。
|
(‘o1’, 1)
(‘o2’, 1)
(‘o3’, 1)
|
X-Backend-Storage-Policy-Index: 1
|
N/A
|
1
|
POST /a/c X-Forced-Change-Storage-Policy: Pol-2
|
(‘o1’, 1)
(‘o2’, 1)
(‘o3’, 1)
|
X-Backend-Storage-Policy-Index: 2
X-Container-Sysmeta-Prev-Policy-Index: 1
X-Container-Sysmeta-Objects-Queued: False
|
N/A
|
2
|
container-replicator 搜索策略更改容器
|
(‘o1’, 1)
(‘o2’, 1)
(‘o3’, 1)
|
X-Backend-Storage-Policy-Index: 2
X-Container-Sysmeta-Prev-Policy-Index: 1
X-Container-Sysmeta-Objects-Queued: True
|
(‘o1’, 0, ‘application/x-transfer-1-to-2’)
(‘o2’, 0, ‘application/x-transfer-1-to-2’)
(‘o3’, 0, ‘application/x-transfer-1-to-2’)
|
3
|
object-transferrer 传输 ‘o1’ 和 ‘o3’
|
(‘o1’, 2)
(‘o2’, 1)
(‘o3’, 2)
|
X-Backend-Storage-Policy-Index: 2
X-Container-Sysmeta-Prev-Policy-Index: 1
X-Container-Sysmeta-Objects-Queued: True
|
(‘o2’, 0, ‘application/x-transfer-1-to-2’)
|
4
|
object-transferrer 传输 ‘o2’
|
(‘o1’, 2)
(‘o2’, 2)
(‘o3’, 2)
|
X-Backend-Storage-Policy-Index: 2
X-Container-Sysmeta-Prev-Policy-Index: 1
X-Container-Sysmeta-Objects-Queued: True
|
空
|
5
|
object-transferrer 删除特殊容器和来自容器 /a/c 的元数据
|
(‘o1’, 2)
(‘o2’, 2)
(‘o3’, 2)
|
X-Backend-Storage-Policy-Index: 2
|
N/A
|
上表重点介绍了容器在更改存储策略以及相应的特殊容器中的数据转换。元组表示对象信息,第一个元素是对象名称,第二个元素是策略索引,第三个元素(如果有)是 content-type 的值,该值是为策略更改定义的。
鉴于三个对象存储在容器/a/c作为 policy-1(步骤 0)。当接受将此容器的策略更改为 policy-2 的请求时(步骤 1),后端策略索引将更改为 2,并且两个 sysmeta 存储在此容器中。在定期 container-replicator 过程中,replicator 找到具有策略更改 sysmeta 的容器,然后创建特殊容器/.change_policy/a:c并包含对象列表(步骤 2)。这些对象具有旧策略和新策略的信息以及 content-type 字段。当 object-transferrer 从`.change_policy`帐户找到此特殊容器时,它会从旧策略(通常是从本地设备)获取一些对象,并将它们放入新策略的存储节点(步骤 3 和 4)。如果特殊容器变为空(步骤 5),则表示该容器的策略更改完成,因此将删除特殊容器,并且原始容器的策略更改元数据也将被删除。
Container-reconciler 是一个守护进程,它将注册在不正确的策略中的对象恢复到正确的策略中。因此,调解过程满足了策略更改的几乎所有功能要求。使用 container-reconciler 进行策略更改的优点是,我们只需要修改 Swift 源代码中的几个点。但是,使用 container-reconciler 存在一个大问题。这个问题是 container-reconciler 没有功能来确定特定容器中对象的策略更改是否完成。因此,这使得从旧策略获取 GET/HEAD 对象以及允许下一个存储策略更改请求变得复杂。根据 Swift hack-a-thon(于 2015 年 2 月举行)和东京峰会(于 2015 年 10 月举行)的讨论,我们决定添加 object-transferrer 来更改容器的策略。