跳转到: 导航, 搜索

Swift/ObjectSystemMetadata


更新

此wiki页面的内容已过时

概述

最初的系统元数据补丁 (https://review.openstack.org/#/c/51228/) 仅支持账户和容器系统元数据。

现在有补丁正在审核中,这些补丁将中间件生成的元数据存储在对象上,例如:

对象系统元数据不应存储在 x-object-meta- 用户元数据命名空间中,因为 (a) 存在与任意用户元数据名称冲突的可能性,以及 (b) 如果用户向对象发送 POST 请求,则 x-object-meta- 命名空间中的系统元数据将会丢失。

这项工作的目标是能够以类似于用户元数据的方式将系统元数据与对象一起持久化,但能够在进行 POST 请求时独立更新单个系统元数据项,而用户元数据则会被整个 POST 请求替换。

提议的方法

最初仅在 PUT 请求上启用对象系统元数据,以支持现有的用例

  • 系统元数据存储在 .data 文件 xattrs 中,并原样复制到 .meta 文件。
  • 使用代理和网关中间件已经支持并保留的 x-object-sysmeta- 命名空间。
  • 将 x-object-sysmeta- 标头存储在对象文件 xattrs 中,以及其他对象元数据。
  • 像账户和容器元数据一样,将对象系统元数据属性存储为 key: (value, timestamp) 对,从而能够将“胜者为王”规则应用于将来的单个属性。


后续启用 POST 上的系统元数据更新

  • 允许并发 POST 请求,从而导致具有不同系统元数据集的多个 .meta 文件。
  • 在处理 POST 时,对象服务器 (DiskFile) 读取潜在的多个现有的 .meta 文件,并将系统元数据合并到一个写入到新的 .meta 文件中的统一集合中 – 系统元数据 key: (value, timestamp) 格式允许从 .meta 文件集合中选择每个系统元数据 key 的最新版本。
  • 在处理 GET/HEAD 时,同样从潜在的多个 .meta 文件读取并合并系统元数据。
  • 仅当其系统元数据已被合并到较新的 .meta 文件中时,才删除 .meta 文件。

详情

可以通过将 x-object-sysmeta- 命名空间添加到持久化标头的集合中,在对象服务器上与用户元数据一起存储系统元数据。但是,对象用户元数据语义(POST 替换整个集合)不适用于系统元数据。相反,带有系统元数据标头的 POST 请求应导致这些系统元数据项被更新,而其他现有的系统元数据将被保留。

'fast-POST'

在没有相同对象上的并发请求的情况下,可以通过对象服务器中元数据读取-修改-写入周期的简单更改来实现对系统元数据的逐项更新。系统元数据(通过命名空间 x-object-sysmeta- 区分)从现有的 .data 或 .meta 文件读取并逐项更新,而用户元数据继续作为整个集合进行更新。

并发 POST 会产生写入多个 .meta 文件的可能性。对于用户元数据,这没有问题,因为最新的 .meta 文件被认为是包含最新的整个元数据集。对于系统元数据,每个并发生成的 .meta 文件可能包含应保留和合并在后续请求处理期间的唯一元数据项。

因此,提议的新行为是在已知其系统元数据已被读取并合并到较新的 .meta 文件中之前,保留 obj_dir 中的多个 .meta 文件。然后,在构造 diskfile 对象时,应读取所有现有的 .meta 文件(通常只有一个)以获取潜在的系统元数据贡献。这需要对 diskfile 清理代码 (diskfile.hash_cleanup_listdir()) 进行细微的更改:在创建新的 .meta 文件后,不要删除所有较旧的 .meta 文件,而仅删除在构建新 .meta 文件期间读取的那些文件。在大多数情况下,结果将相同,但如果第二个并发请求写入了第一个请求处理程序未读取的 .meta 文件,则该 .meta 文件将保留在原位。

同样,需要在异步清理过程(由复制器守护程序调用)中进行更改。清理过程必须检查旧文件的系统元数据,以确定哪些文件对统一元数据集没有独特的贡献,并仅删除那些文件。

为了能够在合并来自多个 .meta 文件的贡献时实现逐项“胜者为王”语义,系统元数据应存储为 key: (value, timestamp) 对(如账户和容器元数据)。

删除系统元数据项

当 POST 请求中包含标头 'x-object-sysmeta-x':"" 时,应删除键为 'x-object-sysmeta-x' 的系统元数据项。可以通过两种方式实现

  1. 不要将键 'x-object-sysmeta-x' 包含在最新的 .meta 文件中。这种方法的风险是,如果较旧的 .meta 文件未能被删除,并且该文件包含 'x-object-sysmeta-x' 的过时值,则该过时值将在未来的合并期间被重新引入。可以通过将过时 .meta 文件的记录作为每个新的 .meta 文件的一部分来避免这种情况,但此列表可能会增长到包含对象历史记录期间创建的所有 .meta 文件。
  2. 使用空值持久化系统元数据项,即 key : ('"", timestamp),以指示任何未来的元数据合并该项已被删除。这可以防止从较旧的 .meta 文件中包含过时值,但代价是存储空值。空值的系统元数据可以在后续合并期间最终删除,当观察到没有现有的 .meta 文件包含该键的值时,即没有过时值。

对于以下示例,我们假设采用第二种解决方案。

fast-POST 示例

考虑以下场景。最初对象目录仅包含原始数据文件

obj_dir:
	t1.data :
		x-object-sysmeta-p: ('p1', t0)

两个并发 POST 更新对象,时间戳分别为 t2 和 t3

POST
X-Timestamp: t2
x-object-sysmeta-p: 'p2'
x-object-sysmeta-x: 'x1'
x-object-sysmeta-y: 'y1'

POST
X-Timestamp: t3
x-object-sysmeta-x: 'x2'
x-object-sysmeta-z: 'z1'

这些导致两个 .meta 文件添加到对象目录

obj_dir:
	t1.data :
		x-object-sysmeta-p: ('p1', t0)
	t2.meta:
		x-object-sysmeta-p: ('p2', t2)
		x-object-sysmeta-x: ('x1', t2)
		x-object-sysmeta-y: ('y1', t2)
	t3.meta:
		x-object-sysmeta-p: ('p1', t0)
		x-object-sysmeta-x: ('x2', t3)
		x-object-sysmeta-z: ('z1', t3)

当前 t2.meta 将在某个时间点被删除。提议的新行为是读取 t2.meta 和 t3.meta,并在下次构造 diskfile 对象时合并结果,以便在遇到重复的系统元数据键时,保留具有最新时间戳的项并丢弃该键的其他项,即对后续 HEAD 请求的响应将包含

HEAD response (new):
x-object-sysmeta-p: 'p2'
x-object-sysmeta-x: 'x2'
x-object-sysmeta-y: 'y1'
x-object-sysmeta-z: 'z1'

现在考虑进一步的 POST 请求在 t4 收到

POST
X-Timestamp: t4
x-object-sysmeta-p: ''
x-object-sysmeta-x: 'x3'

此 POST 的处理方式如下。现有的系统元数据如上所述通过合并 t2.meta 和 t3.meta 文件的内容获得。然后,现有的元数据使用来自 POST 请求的任何新项进行更新,并将结果集写入 t4.meta。此时,t2.meta 和 t3.meta 中的不同系统元数据集在 t4.meta 中统一,从而使 t2.meta 和 t3.meta 过时。写入 t4.meta 后,diskfile 清理代码将删除 t2.meta 和 t3.meta,在 obj_dir 中留下 t1.data 和 t4.meta

obj_dir:
	t1.data :
		x-object-sysmeta-p: ('p1', t0)
	t4.meta:
		x-object-sysmeta-p: ('', t4)
		x-object-sysmeta-x: ('x3', t3)
		x-object-sysmeta-z: ('z1', t3)
		x-object-sysmeta-y: ('y1', t2)

请注意,如上所述,x-object-sysmeta-p: ("", t4) 存储在 t2.meta 和/或 t3.meta 的删除失败的情况下。

'POST-as-copy'

当启用 post-as-copy 时,两个并发 POST 会导致对象服务器上的两个 PUT,这可能会导致 obj_dir 中生成两个 .data 文件。当前的清理代码将在退出 PUT 处理程序之前删除最旧的文件。同样,这对于系统元数据来说是不正确的行为,因为 .data 文件可能包含应随后合并的唯一元数据项。

为了支持系统元数据 POST 语义,当发生并发 PUT 时,可能需要在 obj_dir 中保留多个 .data 文件。但是,这仅在 PUT 是由于 POST-as-copy 事件引起的时才需要。幸运的是,由于 POST-as-copy,可以通过代理控制器在处理 POST-as-copy 时添加的 X-Fresh-Metadata 标头来区分 PUT(此标头用作代理对象控制器内部的标志,以指示所有现有的用户元数据都将被替换,但该标头最终作为副作用发送到后端对象服务器)。通过将此标头与其他元数据一起持久化到 .data 文件中,可以区分由 POST-as-copy 事件创建的 .data 文件与由常规 PUT 创建的 .data 文件,并以类似于系统元数据处理的 .meta 文件的方式处理。

为了避免存储过时的对象数据,可以截断较旧的 .data 文件,使其长度为零,仅保留其 xattrs。

'POST-as-copy' 示例

再次考虑我们的示例。最初 obj_dir 包含 t1.data,这是原始对象 PUT 的结果

obj_dir:
	t1.data :
		x-object-sysmeta-p: ('p1', t0)

两个并发 PUT(由于 POST-as-copy)更新对象,时间戳分别为 t2 和 t3

POST
X-Timestamp: t2
x-fresh-metadata: 'True'
x-object-sysmeta-p: 'p2'
x-object-sysmeta-x: 'x1'
x-object-sysmeta-y: 'y1'

POST
X-Timestamp: t3
x-fresh-metadata: 'True'
x-object-sysmeta-x: 'x2'
x-object-sysmeta-z: 'z1'

这些导致两个 .data 文件添加到对象目录,并且 t1.data 被删除

obj_dir:
	t2.data:
		x-fresh-metadata: 'True'
		x-object-sysmeta-p: ('p2', t2)
		x-object-sysmeta-x: ('x1', t2)
		x-object-sysmeta-y: ('y1', t2)
	t3.data:
		x-fresh-metadata: 'True'
		x-object-sysmeta-p: ('p1', t0)
		x-object-sysmeta-x: ('x2', t3)
		x-object-sysmeta-z: ('z1', t3)

当下次构造 diskfile 对象时,将读取 t2.data 和 t3.data 的 xattrs,并根据 x-fresh-metadata 属性为 True,合并它们的系统元数据项。由于其数据已过时,因此可以截断数据文件 t2.data 为零长度。

现在考虑进一步的 PUT(由于 POST-as-copy)在 t4 收到

POST
X-Timestamp: t4
x-fresh-metadata: 'True'
x-object-sysmeta-p: ''
x-object-sysmeta-x: 'x3'

此 PUT 的处理方式如下。diskfile 对象通过合并 t2.data 和 t3.data 文件的内容来构造现有的系统元数据。然后,现有的元数据使用来自 PUT 请求的任何新项进行更新,并将结果集写入 t4.data。此时,t2.data 和 t3.data 中的不同系统元数据集在 t4.data 中统一,因此在写入 t4.data 后,diskfile 清理代码可以删除 t2.data 和 t3.data,在 obj_dir 中留下 t4.data

obj_dir:
	t4.meta:
		x-object-sysmeta-p: ('', t4)
		x-object-sysmeta-x: ('x3', t3)
		x-object-sysmeta-z: ('z1', t3)
		x-object-sysmeta-y: ('y1', t2)

请注意,与 fast-POST 示例一样,x-object-sysmeta-p: ("", t4) 存储在 t2.data 和/或 t3.data 的删除失败的情况下。

仍需考虑

  • 对复制器的影响
  • 对其他后端的影响
  • 对性能的任何影响