这周一直在琢磨 go-fs 的实现:我们计划基于 go-storage 来实现一个 FUSE,它会有点像 goofys,尽可能的支持 POSIX 语义,但是并不完全保证 POSIX 兼容,面向连续读写优化。在实现 FUSE 的过程中,我发现 go-storage 还缺少了很多功能,比如说:

  • Link
  • Rename
  • Copy
  • ...

Rename 与 Copy 的支持我们之后再讨论,这次先聊聊 Link,更具体的来说是 SymLink。在众多存储相关的 API 中,SymLink 的使用率并不是很高,但是作为一个有志向有理想的存储抽象层,完全提供不了这样的能力是非常失败的,缺乏这样的支持会导致用户无法开发类似 FUSE 这样的应用,所以我们一定要能够支持它。

在我们目前支持的存储服务中,有些是原生支持 Link 的,比如 fs 与 oss:

  • fs 自然不必说,有着 Symlink 的原生 API
  • oss 作为对象存储,也提供了 PutSymlink 与 GetSymlink API

更多的服务并不支持 Link 的,比如使用最为广泛的 s3。但是基于现有的 API 我们可以很轻松的实现类似逻辑,比如将 Link Target 存储在 Object Metadata 中,在 Stat 时获取。但是从一个存储抽象层角度来看,这样做是有问题的:

  • 无法确定这是否是用户预期的行为:我们无法确切的知晓用户是否知道这并不是原生的 API 调用,也无法知道用户对该实现的敏感程度。用户可能要做一些偏底层的实现,他期望 go-storage 在缺乏原生支持的情况下返回不支持,而不是性能较差的模拟实现;用户可能在做一些上层的应用,他不在乎 go-storage 底层是如何实现该功能。
  • 锁定在 go-storage 框架内:一旦用户使用我们模拟出来的 API,那用户就会依赖 go-storage 库来创建和读取数据,对很多用户来说,这是不可接受的。

按照我们原先的设计,我们可能会选择只为原生支持该功能的服务实现。但这一限制过于严格了,毕竟有用户愿意承担虚拟实现的开销与后果,所以我们需要在允许服务供应商实现这些功能的同时,给用户一个选择的权利。这些虚拟实现的 API 只有在用户显式启用时才会使用,否则就返回该功能尚未实现。同时通过 service 配置文件中引入新的配置项,将该逻辑通过代码生成的方式固化下来,保证所有的 service 都有一致的实现。这些想法组合起来就是我的新 Proposal: GSP-87: Feature Gates

在该 Proposal 被实现后,我们可以放心大胆地为各个服务实现他们本来不支持的功能而不需要担心影响到用户:

  • 为对象存储服务支持 CreateDir / CreateLink 等功能
  • 为 fs 实现 content-md5 / content-type 等功能
  • 为 fs 实现分段上传功能

下周请了五天年假,计划了一场跟女朋友一起的七天六夜云云南之旅,途径昆明,西双版纳,大理和丽江。

预祝自己玩的开心~


下周再见!