- 分布式文件系统
- 对象存储
- 存储
【免费下载链接】seaweedfs
SeaweedFS is a distributed storage system for object storage (S3), file systems, and Iceberg tables, designed to handle billions of files with O(1) disk access and effortless horizontal scaling.
导读
在 SeaweedFS 中启用 filer group 后,S3 bucket 对应的底层 collection 会被自动冠以{filerGroup}_{bucketName}前缀,这一命名规则贯穿写入与删除全链路;若删除路径只使用裸 bucket 名,collection 删除就会失败并遗留孤儿数据。本文以仓库中的集成测试套件 test/s3/filer_group 为主体,完整讲解其背景、三个核心测试用例、运行方式与配置项,并结合weed/s3api与weed/filer源码揭示 collection 命名和删除的底层实现。读完后,你将掌握如何在 filer group 部署下正确验证 S3 桶的创建与删除行为,并能复现该场景的完整集成测试。
背景:filer group 下的 Collection 命名规则
当 SeaweedFS 配置了 filer group(通过-filer.filerGroup选项,weed mini模式下对应-filer.filerGroup;独立weed filer进程对应-filerGroup,见 filer.go 与 mini.go),S3 bucket 对应的 collection 会按如下规则命名:
Collection name = {filerGroup}_{bucketName}例如 filer group 为mygroup、bucket 为mybucket时,collection 将被命名为mygroup_mybucket。这一命名规则由 S3 网关在写入路径上主动指定,而不是 master 端推导出来的。
从源码看,filer_util.go 中的getCollectionName就是该规则的核心实现:
func (s3a *S3ApiServer) getCollectionName(bucket string) string { if s3a.option.FilerGroup != "" { return fmt.Sprintf("%s_%s", s3a.option.FilerGroup, bucket) } return bucket }该函数被写入路径的多处代码调用:
- 普通对象上传:s3api_object_handlers_put.go 在进入分块上传前通过
s3a.getCollectionName(bucket)解析collection; - 大对象 manifest 写入:s3api_chunk_manifest.go 中的
saveManifestChunk对 manifest 块同样使用该函数确定 collection,确保分块数据与对象本体落在同一命名空间。
同时,S3 网关在注册 master client 时也会携带自己的 filer group 身份(s3api_server.go),而weed s3命令在连接 filer 后从注册响应中获取FilerGroup(s3.go),因此网关、filer、broker 之间可以基于同一 group 名实现元数据共享(flag 注释明确写着 "share metadata with other filers in the same filerGroup")。
本测试套件针对的问题:Collection 删除失败与孤儿数据
本套件创建的初衷,是为了验证对一个缺陷修复的正确性。该缺陷的表现是:
- 管理界面(admin UI)在删除 collection 时只使用了裸 bucket 名;
- 当配置了 filer group 时,真实 collection 名带
{filerGroup}_前缀,导致删除请求无法匹配; - 结果是在 admin UI 删除 bucket 后,其对应的 collection 数据变成孤儿数据,残留在 volume 中无法回收。
因此,测试套件需要覆盖的核心断言是:在 filer group 配置下,S3 API 的DeleteBucket必须触发对「带前缀的完整 collection 名」的删除。
这一修复逻辑在删除路径的源码中可以得到印证。filer_delete_entry.go 中的bucketCollection函数与写入路径使用同一套解析链——先看是否配置了 filer group,若是则返回group + "_" + name,否则才回退到存储规则MatchStorageRule与 bucket 名本身:
func (f *Filer) bucketCollection(ctx context.Context, bucket string) (collection string) { bucketDir := f.DirBucketsPath + "/" + bucket + "/" resolve := func(dir, name string) string { if f.MasterClient != nil { if group := f.MasterClient.FilerGroup; group != "" { return group + "_" + name } } return util.Nvl(f.FilerConf.MatchStorageRule(dir).Collection, name) } collection = resolve(bucketDir, bucket) // 若解析结果等于默认 metaLog collection,则共享 collection 必须保留 if collection == f.metaLogCollection { return "" } // 若存在前缀超出该 bucket 的存储规则仍指向同一 collection,也必须保留 for _, rule := range f.FilerConf.ToProto().Locations { ... } ... }随后,DeleteEntryMetaAndData 在判定删除目标是 bucket(f.IsBucket(entry))时,通过f.bucketCollection(ctx, entry.Name())取得完整 collection 名,并以collectionDeleteTimeout(15 秒)上限的独立 context 调用DoDeleteCollection;而 DoDeleteCollection 最终通过 gRPC 向 master 发起CollectionDeleteRequest,其中Name字段携带的就是带前缀的完整 collection 名。
另一个独立于集成套件的单元测试 filer_delete_collection_test.go 也验证了同一行为:当 filer group 为tenant1时,删除/buckets/photos后 master 收到的CollectionDelete名称必须是tenant1_photos而非photos。
测试套件总览
套件位于 test/s3/filer_group,共四个文件:
| 文件 | 作用 |
|---|---|
| README.md | 测试目的、背景、运行方式与预期行为说明 |
| s3_filer_group_test.go | 测试主体:配置加载、S3/master 客户端构造与三个测试用例 |
| Makefile | 自动构建、启动带 filer group 的服务器、跑测试与清理 |
| test_config.json | 测试连接参数(端点、凭据、filer group 名) |
它属于端到端集成测试:真实启动带 filer group 的 SeaweedFS 服务,通过 AWS SDK Go v2 调用 S3 API 完成建桶、上传、删除,再通过 master 的 gRPCCollectionList接口核对 collection 的真实存在状态。
三个测试用例详解
套件中的三个测试均以真实 S3 API 操作驱动,并通过 master 侧轮询确认 collection 状态。注意:三者都要求FILER_GROUP非空,否则直接t.Skip跳过。
TestFilerGroupCollectionNaming
验证「filer group 前缀正确应用于 collection 命名」,流程如下:
- 生成唯一 bucket 名(
filergroup-test-{UnixNano},见 getNewBucketName); CreateBucket建桶,随后PutObject上传一个对象以触发 collection 创建;- 通过 master 的
CollectionList轮询等待{filerGroup}_{bucketName}出现(10 秒超时、200ms 间隔,见 waitForCollectionExists); - 断言该带前缀 collection 确实存在;
DeleteObject+DeleteBucket清理,并轮询确认 collection 被删除。
expectedCollection := getExpectedCollectionName(bucketName) // "{filerGroup}_{bucketName}" ... waitForCollectionExists(t, masterClient, expectedCollection) require.True(t, collectionExists(t, masterClient, expectedCollection), ...)TestBucketDeletionWithFilerGroup
专门验证「配置 filer group 时,S3 的桶删除会正确删除带前缀的 collection」:
- 建桶并上传对象,等待带前缀 collection 建立;
- 删除对象与桶(均通过 S3 API);
- 轮询等待 collection 消失(waitForCollectionDeleted);
- 断言 collection 已不存在——这正是对「修复后不再产生孤儿数据」的直接验证。
TestMultipleBucketsWithFilerGroup
扩展到多桶场景:同时创建 3 个独立命名的 bucket,逐个上传对象,等待全部带前缀 collection 建立;再逐个删除对象与桶,等待并断言所有 collection 全部消失,从而排除前缀逻辑只在单桶下偶然正确的情况。
运行测试
前置条件
- 运行中的 SeaweedFS 服务,且已配置 filer group;
- S3 网关可访问;
- master 可访问,用于 collection 核验。
方式一:手动启动服务 + 环境变量
# 设置环境变量 export FILER_GROUP=testgroup export S3_ENDPOINT=http://localhost:8333 export MASTER_ADDRESS=localhost:19333 # 运行测试 go test -v ./...注意:MASTER_ADDRESS默认是 master 的gRPC 端口,测试代码注释明确说明其取值规则为gRPC 端口 = 10000 + master HTTP 端口(s3_filer_group_test.go)。master HTTP 端口为 9333 时,gRPC 端口即 19333。
方式二:Makefile 一键流程
Makefile 封装了构建、起服务、测试、清理的全流程:
# 构建 weed 二进制(输出到 weed/weed_binary) make build-weed # 启动带 filer group 的服务器(默认 testgroup) make start-servers FILER_GROUP=testgroup # 运行测试(服务需已在运行) make test # 停止服务器 make stop-servers # 或一条命令完成「起服务→跑测试→停服务」全周期 make full-test实际 Makefile 中提供的 target 名为start-server/stop-server/test-with-server(README 中写作start-servers等复数形式,请以 Makefile 为准)。常用 target 一览:
| Target | 行为 |
|---|---|
help | 打印可用目标与默认配置 |
build-weed | 在weed目录构建weed_binary |
check-deps | 检查 Go 与依赖(aws-sdk-go-v2、testify) |
start-server | 启动weed mini,带 filer group |
test | 运行测试,注入 FILER_GROUP/S3_ENDPOINT/MASTER_ADDRESS |
test-with-server | 自动完成起服务→测试→停服务,失败时打印日志 |
stop-server | 按 PID 停止服务(先 TERM 后 KILL) |
logs | 跟踪服务器日志 |
health-check | 检查 S3 与 metrics 端口可用性 |
clean | 停服务并清理日志、测试数据目录与测试缓存 |
其中start-server的核心启动命令值得关注(Makefile):
export AWS_ACCESS_KEY_ID=some_access_key1 && \ export AWS_SECRET_ACCESS_KEY=some_secret_key1 && \ ./weed_binary mini \ -debug \ -dir=./test-volume-data \ -s3.port=8333 \ -s3.config=../../../docker/compose/s3.json \ -filer.filerGroup=testgroup \ > weed-server.log 2>&1 &启动前会检查 8333 端口占用,随后以 30 秒窗口nc探测 S3 端口就绪。
配置方式与优先级
测试参数有两条配置途径:
环境变量:
FILER_GROUP:filer group 名(必填,否则测试跳过)S3_ENDPOINT:S3 API 端点(默认http://localhost:8333)MASTER_ADDRESS:master gRPC 地址(默认localhost:19333)
test_config.json文件(test_config.json):
{ "s3_endpoint": "http://localhost:8333", "access_key": "some_access_key1", "secret_key": "some_secret_key1", "region": "us-east-1", "filer_group": "testgroup" }配置文件加载的完整优先级逻辑见 init 函数:先尝试读取test_config.json(解析失败仅告警不中断),随后环境变量对同名配置进行覆盖。即:环境变量 > test_config.json > 代码内默认值。测试代码默认值还包括 S3 凭据some_access_key1/some_secret_key1与区域us-east-1,与 Makefile 启动服务器时注入的AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY、docker/compose/s3.json 中的静态用户配置保持一致。
测试基础设施的底层实现
从 s3_filer_group_test.go 可以看到测试是如何「自证」collection 状态的:
- S3 客户端(getS3Client):基于 AWS SDK Go v2,使用静态凭据构造,并通过
o.BaseEndpoint指向本地的 S3 端点、o.UsePathStyle = true使用 path-style 寻址(本地存储常见的兼容配置)。 - master 客户端(getMasterClient):以
grpc.NewClient直连 master gRPC 端口,构造master_pb.SeaweedClient。 - Collection 查询(listAllCollections):调用
masterClient.CollectionList,同时带上IncludeNormalVolumes与IncludeEcVolumes,确保普通卷与纠删码(EC)卷上的 collection 都能被看到。 - 轮询机制:
waitForCollectionExists与waitForCollectionDeleted均使用 10 秒总超时、200ms 间隔的轮询。前者在超时后t.Fatalf并打印当前全部 collection 列表,便于排查命名错误;后者用require.Eventually断言删除最终生效。
这套「S3 API 触发 + master 侧核验」的双端验证模式,保证了断言的不是客户端缓存或假象,而是 master 实际维护的 collection 元数据。
预期行为对照表
配置 filer group(如testgroup)
| Bucket 名称 | 期望 Collection 名称 |
|---|---|
mybucket | testgroup_mybucket |
test-123 | testgroup_test-123 |
未配置 filer group
| Bucket 名称 | 期望 Collection 名称 |
|---|---|
mybucket | mybucket |
test-123 | test-123 |
这两种行为在测试代码中由getExpectedCollectionName统一处理(s3_filer_group_test.go),也再次印证了getCollectionName在 filer_util.go 中「有 group 加前缀、无 group 用原名」的对称逻辑。
边界行为与设计细节
从删除路径源码还能读出几个值得注意的边界规则:
- 共享 collection 必须保留:
bucketCollection中若解析结果恰好等于 filer 的默认metaLogCollection,或存在前缀超出该 bucket 的存储规则仍指向同一 collection,则删除 bucket 时返回空 collection 名、不触发CollectionDelete,避免误删其他路径仍在写入的共享卷(filer_delete_entry.go)。 - 删除超时与容错:bucket 删除触发的 collection 清理使用
context.WithoutCancel派生、上限 15 秒的独立 context,即使调用方挂断,清理也会在后台继续完成;master 短暂不可用时也能为 S3 客户端的重试留出预算(filer_delete_entry.go)。 - 自定义存储规则与 filer group 共存:单元测试
TestDeleteBucketUnderFilerGroup展示了 filer group 前缀优先于自定义存储规则的解析结果——即使/buckets/photos配置了Collection: "archive",删除时仍以tenant1_photos为准(filer_delete_collection_test.go)。
小结
test/s3/filer_group集成测试套件完整覆盖了 SeaweedFS filer group 场景下 S3 bucket 生命周期中最关键的两个事实:写入时 collection 带{filerGroup}_前缀,删除时也必须使用带前缀的完整 collection 名。通过 Makefile 一键启动真实服务、AWS SDK 驱动 S3 API、master gRPC 轮询核验,它既是一份可重复运行的回归测试,也是一份理解 collection 命名规则的活文档。对于在多租户或 filer group 部署模式下排查「删除桶后数据残留」问题的开发者,这套测试及其背后的 filer_delete_entry.go 实现,是定位和验证问题最直接的参考路径。
- 分布式文件系统
- 对象存储
- 存储
【免费下载链接】seaweedfs
SeaweedFS is a distributed storage system for object storage (S3), file systems, and Iceberg tables, designed to handle billions of files with O(1) disk access and effortless horizontal scaling.
相关推荐
SeaweedFS S3 反向代理签名验证:nginx + `-s3.externalUrl` 完整实战指南
SeaweedFS S3 反向代理签名验证:nginx + s3.externalUrl 完整实战指南 本篇指南以 SeaweedFS 仓库中的 proxy_s
分布式文件系统对象存储存储SeaweedFS S3 兼容性实测指南:基于 AWS Java SDK v1/v2 的 ETag 格式验证与集成测试
SeaweedFS S3 兼容性实测指南:基于 AWS Java SDK v1/v2 的 ETag 格式验证与集成测试 SeaweedFS 在启动 s3 开关后
分布式文件系统对象存储存储用Open Agents构建多租户AI智能体平台:5个关键架构设计指南
用Open Agents构建多租户AI智能体平台:5个关键架构设计指南 Open Agents 是一个开源的云端 AI 智能体(AI Agent)参考应用模板,
分布式文件系统对象存储存储
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考