This work is licensed under a Creative Commons Attribution 3.0
Unported License.
http://creativecommons.org/licenses/by/3.0/legalcode
随着存储策略和纠删码的出现,在容器之间移动对象变得越来越有用。但是,我们不希望在这样做时破坏对该对象的现有引用。
例如,一个常见的对象生命周期是对象最初“热”(即频繁请求),并随着时间的推移逐渐“冷却”(变得不那么频繁请求)。用户希望对象一开始就进行复制以实现每秒高请求量,但最终过渡到 EC 以降低存储成本,一旦冷却。
另一种完全不同的用例是,当应用程序在多个容器中分片对象时,但发现需要使用更多的容器;例如,随着写入速率的提高,从 256 个容器增加到 4096 个容器。该应用程序可以通过为所有 256 分片的对象创建 4096 分片的引用来迁移到新模式,从而避免大量数据移动。
还有第三种用例是,用户拥有大量不常访问的数据,这些数据以复制形式存储(因为它是在 Swift 的纠删码支持之前上传的),并且希望以纠删码形式存储。用户可能会要求 Swift 允许在容器级别更改存储策略,但由于这存在危险,我们可以提供这种替代方案。
Swift 将获得符号链接(“symlink”)对象的概念。该对象将引用另一个对象。对符号链接对象的 GET、HEAD 和 OPTIONS 请求将对引用的对象进行操作。对符号链接对象的 DELETE 和 PUT 请求将对符号链接对象进行操作,而不是引用的对象,并分别删除或覆盖它。
可以通过在请求中添加查询参数,对符号链接对象而不是引用的对象执行 GET、HEAD 和 OPTIONS 请求?symlink=true到请求。
POST 的理想行为是将其应用于引用的对象,但由于 Swift 的最终一致性特性,这是不可能的。最初,有人建议 POST 应该直接应用于符号链接,并且在 GET 或 HEAD 期间,符号链接和引用的对象的标头都将被比较,并返回最新的标头。虽然这可行,但如果应用程序曾经直接 GET 或 HEAD 引用的对象,行为可能会很奇怪,因为它不会包含发布到符号链接的任何标头。
考虑到所有这些,剩下的最佳选择是使对符号链接的 POST 失败,并让应用程序来处理它,即直接发布引用的对象。实现此行为需要进行一些更改
1) 为了避免每次 POST 时的 HEAD 操作,对象服务器将意识到符号链接并能够适当地检测它们并失败。 2) 仅仅在对象服务器中使 POST 在对象是符号链接时失败是不行的;考虑以下场景
场景 A
- Add a symlink
T0 - PUT /accnt/cont/obj?symlink=true
- Overwrite symlink with an regular object
T1 - PUT /accnt/cont/obj
- Assume at this point some of the primary nodes were down so handoff nodes
were used.
T2 - POST /accnt/cont/obj
- Depending on the object server hit it may see obj as either a symlink or a
regular object, though we know in time it will indeed be a real object.
场景 B
- Add a regular object
T0 - PUT /accnt/cont/obj
- Overwrite regular object with a symlink
T1 - PUT /accnt/cont/obj?symlink=true
- Assume at this point some of the primary nodes were down so handoff nodes
were used.
T2 - POST /accnt/cont/obj
- Depending on the object server hit it may see obj as either a symlink or a
regular object, though we know in time it will indeed be a symlink.
鉴于上述场景,在 T1(即在发布期间)某些对象服务器可能会看到符号链接,而另一些则看到常规对象,因此不可能使符号链接的 POST 失败。相反,将使用以下行为,对象服务器将始终应用 POST,无论对象是符号链接还是常规对象。接下来,如果对象服务器认为它看到了符号链接,我们仍然会向客户端返回一个错误。在场景 A) 中,这意味着 T1 处的 POST 可能会失败,但更新确实会应用于常规对象,这是正确的行为。在场景 B) 中,这意味着 T1 处的 POST 可能会失败,但更新确实会应用于符号链接,虽然这不理想,但本身并不是不正确的行为,并且返回给应用程序的错误应该导致它将 POST 应用于引用对象,并且考虑到前面提出的观点,这确实是期望的。
Swift 符号链接的目标是类似于 Unix 符号链接(除非这样做没有意义)。
可以使用单个分段 SLO 清单来实现类似的效果。但是,SLO 清单的 ETag 是其分段的 ETag 的 MD5,因此使用单个分段 SLO 清单会更改对象的 ETag。此外,对象元数据(X-Object-Meta-*)必须复制到 SLO 清单中,因为来自 SLO 分段的元数据不会出现在响应中。此外,SLO 清单包含引用分段的 ETag,如果一个分段发生更改,清单将失效。这对于符号链接来说不是一个理想的属性。
DLO 清单不会验证 ETag,但它仍然无法保留引用对象的 ETag 和元数据,因此也不适用。此外,由于 DLO 基于对象名称前缀,因此上传新对象(例如thesis.doc,然后是thesis.doc.old) 可能会导致下载损坏。
此外,DLO 和 SLO 无法相互用作分段,而 Swift 符号链接可以引用 DLO 和 SLO 并且充当 DLO 和 SLO 中的分段。
客户端通过执行零长度 PUT 请求并使用查询参数创建 Swift 符号链接?symlink=true和标头X-Object-Symlink-Target-Object: <object>.
对于跨容器符号链接,还包括标头X-Object-Symlink-Target-Container: <container>。如果省略,则默认为符号链接对象的容器。
对于跨帐户符号链接,还包括标头X-Object-Symlink-Target-Account: <account>。如果省略,则默认为符号链接对象的帐户。
符号链接必须是零字节对象。尝试使用非空请求主体 PUT 符号链接将导致 400 系列错误。
引用的对象不必在符号链接创建时存在。这模仿了 Unix 符号链接的行为。此外,如果我们最终让批量上传在 tarball 中使用符号链接,那么我们将不得不避免验证。tar只是在找到它们时将文件附加到存档中;它不会将符号链接推送到存档的末尾。因此,tarball 中任何给定符号链接在其引用之前出现的概率为 50%。
假设对象是 /v1/MY_acct/con/obj
发出 COPY 请求将对象复制到 EC 策略容器,例如
COPY /v1/MY_acct/con/obj
Destination: ec-con/obj
用符号链接对象覆盖复制的对象
PUT /v1/MY_acct/con/obj?symlink=true
X-Object-Symlink-Target-Container: ec-con
X-Object-Symlink-Target-Object: obj
如果您在不使用?symlink=true的情况下复制符号链接,您将获得引用对象的副本。如果您使用?symlink=true复制符号链接,您将获得符号链接的副本;它将引用相同的对象、容器和帐户。
但是,如果您在不使用X-Object-Symlink-Target-Container的情况下在容器之间复制符号链接,或者在不使用X-Object-Symlink-Target-Account的情况下在帐户之间复制符号链接,新的符号链接将引用不同的对象。
这些肯定会相互作用。我们应该弄清楚如何。
这里没有什么特别的。如果您使用X-Delete-At创建符号链接,则符号链接将在适当的时间被删除。
如果您使用纯 POST 设置X-Delete-At在符号链接上,它将像其他对象元数据一样设置在引用对象上。如果您使用带有?symlink=true的 POST 设置X-Delete-At在符号链接上,它将设置在符号链接本身上。
由于我们几乎肯定最终会将符号链接实现为中间件,因此我们将按以下顺序排列管道
[pipeline:main]
pipeline = catch_errors ... slo dlo symlink ... proxy-server
这样,您可以创建一个目标是大对象符号链接,并且大对象可以将符号链接作为分段引用。
如果我们在代理服务器中实现符号链接,这也有效,但这只有在找到令人信服的理由时才会发生。
将检查符号链接和引用对象的授权。如果用户有权查看符号链接但没有权查看引用的对象,他们将收到 403 错误,就像他们直接尝试访问引用的对象一样。
这里不需要任何特殊操作。符号链接算作一个对象,计入对象计数配额。由于符号链接为零字节,因此不计入存储配额,我们不需要编写任何代码来实现这一点。
如果应用程序直接与对象服务器通信并获取符号链接,则应用程序必须处理它。绕过代理的应用程序应该避免使用符号链接,或者应该知道如何处理它们。
Swift 代理服务器提供的其他服务(例如 SLO、DLO、版本控制、纠删码等)也是如此,因此我们并非没有先例。
符号链接像其他对象一样同步。如果集群 A 中的引用对象与集群 B 中的容器名称不同,则符号链接将指向其中一个集群中的错误位置。
仅包含X-Object-Symlink-Target-Object的容器内符号链接将在两个集群中都能正常工作。此外,如果两个集群中的容器名称相同,则跨容器符号链接(包含X-Object-Symlink-Target-Object和X-Object-Symlink-Target-Container) 也将正常工作。
当前,批量上传会忽略上传的 tarball 中的所有非文件成员。可以将其扩展为处理符号链接成员(即tarinfo.issym() == True) 并从中创建符号链接对象。这对于 Swift 符号链接的初始实现不是必需的,但会很好。
如果给出标志,python-swiftclient 可以将 Swift 符号链接下载为 Unix 符号链接,或者在某些情况下将 Unix 符号链接上传为 Swift 符号链接。这对于 Swift 符号链接的初始实现不是必需的,只是为了表明 python-swiftclient 没有被遗忘。