我教会了存储桶说 Git 语言
本文作者将一个兼容 Git 的对象存储桶(Tigris)改造成了纯 Go 的 Git 服务器。通过 billy 文件系统抽象层和 go-git 库,实现了完整的 Git 协议支持(HTTP、git://、SSH),且所有数据存储在对象存储中,无需本地文件系统。文章详细介绍了实现过程中遇到的挑战:POSIX rename 原子性、stat() 调用风暴、打包文件写入死锁、克隆时的随机读取延迟,以及 ListObjectsV2 调用泛滥等性能问题,并逐一给出了基于 Tigris 特性的解决方案。这是一个实验性项目,尚未经过严格测试。
背景速读
- **Tigris** 是一个兼容 S3 协议的对象存储服务,作者在这里工作。对象存储(如 AWS S3)与传统文件系统不同:没有目录结构,没有原子 rename,每次操作都是 HTTP 请求。
- **go-git** 是 Git 协议和内部数据格式的纯 Go 实现,不依赖系统安装的 git 二进制文件。它的存储层基于 **billy** 接口 —— 一个 Go 的文件系统抽象,最初就是为 go-git 设计的。
- **objgit** 是作者基于以上组件拼出来的实验项目:让一个对象存储桶(bucket)直接充当 Git 服务器,不依赖任何本地磁盘作为永久存储。传统 Git 服务器(包括 GitHub)本质上还是依赖单机文件系统存放仓库数据,而 objgit 试图把这个状态全部推到对象存储里,从而实现无状态、可水平扩展的 Git 服务。
- 文章的核心矛盾:Git 的持久化策略严重依赖文件系统的原子 rename、微秒级 stat 调用和随机读局部性,这些在对象存储的毫秒级延迟和操作语义下全部失效。作者逐一 hack 解决了这些问题,但文章本质上是在追问:把 Git 强行塞进对象存储到底是不是一个好主意?