Repository navigation
Conversation
- Persist zero-byte trailing-slash objects as S3 markers - Remove synthetic empty-directory objects from listings - Validate multipart upload bucket and object identity - Add marker lifecycle and listing regression tests Co-authored-by: LunelleEverdawn <lunelle+suyunmeng@oplist.org>
- Backfill bucket-aware marker key hashes before AutoMigrate - Retain the latest marker for duplicate bucket and key pairs - Replace the legacy composite index with the unique hash index - Cover legacy marker migration and repeat startup Co-authored-by: LunelleEverdawn <lunelle+suyunmeng@oplist.org>
- Preserve the root slash marker during bucket listing - Add a regression test for the root marker key Co-authored-by: LunelleEverdawn <lunelle+suyunmeng@oplist.org>
jyxjjj
left a comment
There was a problem hiding this comment.
[P1] marker 的精确 key 没有贯通 HTTP 路由,实际 HEAD、GET、DELETE 仍然访问不到 PUT 保存的对象。
server/s3/backend.go:123
当前固定使用的 gofakes3 v0.8.2 会先裁掉请求路径首尾的 /,只对普通 PUT 补回末尾的 /。因此 PUT /mp/dir/ 保存的是 dir/,但 HEAD、GET、DELETE /mp/dir/ 传入后端的是 dir,匹配不到保存的 marker。只有 marker、没有物理目录时,HEAD 和 GET 会返回找不到对象,DELETE 也不会删除 marker。
要让各方法经过路由后仍然保留同一个对象 key,并补经过 HTTP handler 的生命周期测试。现在的测试直接调用后端方法,绕过了这层问题。
[P2] 带 delimiter 的列表把子目录 marker 放进了 Contents,目录分组和分页计数会出错。
server/s3/list.go:239
例如只有 nested/marker/,请求 prefix=nested/&delimiter=/ 时,应返回 CommonPrefixes 中的 nested/marker/。这里先执行 marker 的 addContent,随后在没有物理目录时直接 continue,返回的却是一个普通对象。如果同时存在同名物理目录,还会把同一个 key 同时加入 Contents 和 CommonPrefixes,并占用两次 MaxKeys。
AWS ListObjectsV2 文档要求,被 delimiter 折叠的 key 只出现在 CommonPrefixes,每组只计为一项。要根据请求 prefix 和 delimiter 决定 marker 是否需要折叠成 CommonPrefixes。TestS3NestedMarkerListing 当前把这个错误结果写成了预期,也要一起调整。
Generated by ChatGPT-6.1-Sol Medium
- Recover trailing-slash marker keys lost by gofakes3 routing - Return delimiter-folded markers as common prefixes - Add HTTP lifecycle and delimiter listing regression tests Co-authored-by: LunelleEverdawn <lunelle+suyunmeng@oplist.org>
jyxjjj
left a comment
There was a problem hiding this comment.
[P2] 下载重定向会绕过 marker 判断,GET 和 HEAD 可能返回不同对象。
server/s3/server.go:42
redirectHandler 在 trailingSlashHandler 和 S3 后端之前执行。如果桶里同时存在普通文件 a 和 marker a/,GET /桶/a/ 会先被 directObjectURL 通过 path.Join 解析成普通文件 a。当驱动允许直链时,请求直接重定向到文件内容,后端没有机会返回零字节 marker。HEAD 则进入后端,返回 marker。
要让尾斜杠对象先按精确 key 判断,避免被普通文件的直链截走,并覆盖普通文件和同名 marker 并存的 HTTP 测试。
[P2] 分片上传仍然丢失尾斜杠,完成后保存的不是 marker。
server/s3/server.go:16
当前固定使用的 gofakes3 v0.8.2 路由会先裁掉路径首尾的 /,补回尾斜杠的逻辑只在普通 PUT 路由里执行。POST /桶/dir/?uploads、UploadPart 和 CompleteMultipartUpload 传入后端的 object 都是 dir,最终 putStream 会走普通文件上传分支,不会保存 dir/ marker。
新增的 TestS3MarkerMultipartCompletion 直接调用后端,绕过了这个路由行为。要让分片上传各阶段保留同一个精确 key,并补经过 HTTP handler 的测试。
之前指出的普通 HEAD、GET、DELETE 尾斜杠问题,以及 delimiter 分组问题,最新修改已经处理,不再重复提出。
Reviewed or refined by GPT-6.1-Sol Medium
- Skip direct download redirects for trailing-slash object keys - Flag trailing-slash requests for every method, including multipart routes - Restore exact marker keys in multipart create, part, complete and abort - Add HTTP tests for same-name file redirects and multipart markers Co-authored-by: LunelleEverdawn <lunelle+suyunmeng@oplist.org>
|
最新评审中指出的两点问题已在 2701e29 中得到修复:
上述两种情况现均已配备 HTTP 级别的测试用例。 |
jyxjjj
left a comment
There was a problem hiding this comment.
[P1] marker 删除后再次 DELETE,会落到同名普通文件,造成误删。
server/s3/backend.go:419–434
例如桶里同时存在普通文件 a 和 marker a/。第一次 DELETE /bucket/a/ 会删除 marker,第二次相同请求查不到 marker 后,会继续进入 path.Join(bucketPath, objectName) 和 fs.Remove,最终删除普通文件 a。HEAD、GET 在 marker 不存在时也会回退到 a,返回另一个对象。
这里沿用了旧的路径归一化行为,但新增 marker 生命周期只处理了“marker 存在”的分支,没有处理删除后的精确 key 隔离。要让尾斜杠 key 查找失败后仍按该对象处理,DELETE 不存在的 marker 应成功结束,不能继续删除去掉斜杠后的文件。补一个 a 与 a/ 并存、连续删除两次 a/ 后 a 仍保留的 HTTP 测试。
之前指出的下载重定向、分片上传尾斜杠丢失和 delimiter 分组问题,最新代码已经处理,重定向和分片上传也补了 HTTP 测试,不再重复提出。
Reviewed or refined by GPT-6.1-Sol Medium
- Prevent trailing-slash reads from falling back to same-name files - Keep idempotent marker deletes from removing ordinary objects - Cover marker and ordinary key coexistence through HTTP Co-authored-by: LunelleEverdawn <lunelle+suyunmeng@oplist.org>
|
已确认该问题确实会发生,并已在提交 |
Summary / 摘要
Persist zero-byte S3 objects with trailing slashes as durable markers so HEAD, GET, LIST, DELETE, and COPY behave consistently. Remove synthetic empty-directory objects and preserve stable marker metadata and modification times.
将以
/结尾的零字节 S3 对象作为持久化 marker 保存,使 HEAD、GET、LIST、DELETE 和 COPY 行为一致。移除虚假的空目录对象,并保持 marker 元数据及修改时间稳定。Migrate legacy marker hashes and indexes to a unique bucket-and-key identity; bind multipart operations to their original bucket and object. Listing also preserves the root
/marker.将旧 marker hash 和索引迁移为 bucket 与 key 的唯一标识;multipart 操作绑定原始 bucket 和对象。列表同时支持根路径
/marker。This PR has breaking changes.
/ 此 PR 包含破坏性变更。
This PR changes public API, config, storage format, or migration behavior.
/ 此 PR 修改了公开 API、配置、存储格式或迁移行为。
This PR requires corresponding changes in related repositories.
/ 此 PR 需要关联仓库同步修改。
Related repository PRs / 关联仓库 PR:
Related Issues / 关联 Issue
Fixes #3186
Testing / 测试
go test ./internal/db ./server/s3 -count=1go test ./...(not run; full-repository validation was outside the requested scope)Checklist / 检查清单
/ 我已阅读 CONTRIBUTING。
/ 我确认此贡献符合仓库许可证、贡献规范和行为准则。
gofmt,go fmt, orprettierwhere applicable./ 我已按适用情况使用
gofmt、go fmt或prettier格式化变更代码。/ 我已在适用情况下请求相关维护者或代码所有者审查。
AI Disclosure / AI 使用声明
/ 此 PR 包含 AI 辅助内容。
Tools used / 使用工具:
Usage scope / 使用范围:
Code generation / 代码生成
Refactoring / 重构
Documentation / 文档
Tests / 测试
Translation / 翻译
Review assistance / 审查辅助
I have reviewed and validated all AI-assisted content included in this PR.
/ 我已审核并验证此 PR 中的所有 AI 辅助内容。
I have ensured that all AI-assisted commits include
Co-Authored-Byattribution./ 我已确保所有 AI 辅助提交都包含
Co-Authored-By归属信息。I can reproduce all AI-assisted content included in this PR without any AI tools.
/ 我可以在没有任何 AI 工具的情况下重现此 PR 中包含的所有 AI 辅助内容。