From 246fe62c2f5767a3a295673cf70a7ad10b1e3b7c Mon Sep 17 00:00:00 2001
From: GatewayJ <835269233@qq.com>
Date: Sat, 28 Feb 2026 00:39:14 +0800
Subject: [PATCH 1/7] =?UTF-8?q?doc:=20=E7=BB=86=E5=8C=96=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/DESIGN.md | 720 +++++++++++
docs/README.md | 28 +
docs/modules/01-access-layer.md | 122 ++
docs/modules/02-protocol-adapter.md | 157 +++
docs/modules/03-metadata-cluster.md | 1163 ++++++++++++++++++
docs/modules/04-cache-layer.md | 668 +++++++++++
docs/modules/05-scheduler-layer.md | 789 ++++++++++++
docs/modules/06-tape-layer.md | 1012 ++++++++++++++++
docs/modules/07-consistency-performance.md | 765 ++++++++++++
docs/modules/08-observability.md | 1250 ++++++++++++++++++++
docs/modules/09-admin-console.md | 1054 +++++++++++++++++
docs/modules/README.md | 34 +
12 files changed, 7762 insertions(+)
create mode 100644 docs/DESIGN.md
create mode 100644 docs/README.md
create mode 100644 docs/modules/01-access-layer.md
create mode 100644 docs/modules/02-protocol-adapter.md
create mode 100644 docs/modules/03-metadata-cluster.md
create mode 100644 docs/modules/04-cache-layer.md
create mode 100644 docs/modules/05-scheduler-layer.md
create mode 100644 docs/modules/06-tape-layer.md
create mode 100644 docs/modules/07-consistency-performance.md
create mode 100644 docs/modules/08-observability.md
create mode 100644 docs/modules/09-admin-console.md
create mode 100644 docs/modules/README.md
diff --git a/docs/DESIGN.md b/docs/DESIGN.md
new file mode 100644
index 0000000..ad9d533
--- /dev/null
+++ b/docs/DESIGN.md
@@ -0,0 +1,720 @@
+# ColdStore 详细设计方案
+
+> 版本:1.1
+> 更新日期:2025-02-27
+
+## 1. 文档概述
+
+本文档描述 ColdStore 冷存储系统的详细技术设计方案,重点涵盖:
+
+> **模块设计文档**:各层独立设计详见 [modules/](modules/) 目录。
+
+1. **协议层**:兼容 S3 冷归档协议(Glacier 语义)
+2. **集群元数据**:基于 [OpenRaft](https://github.com/databendlabs/openraft) + RocksDB 持久化
+3. **数据缓存层**:基于 [async-spdk](https://github.com/madsys-dev/async-spdk) 的高性能用户态缓存
+
+### 1.1 核心技术选型(已确定)
+
+| 组件 | 库/方案 | 说明 |
+|------|---------|------|
+| Raft 共识 | **openraft** | [databendlabs/openraft](https://github.com/databendlabs/openraft),异步 Raft,Tokio 驱动 |
+| Raft 存储 | **openraft-rocksstore** | RocksDB 后端,RaftLogStorage + RaftStateMachine |
+| SPDK 绑定 | **async-spdk** | [madsys-dev/async-spdk](https://github.com/madsys-dev/async-spdk),原生 async/await |
+| 磁带 SDK | **自研抽象层** | 前期对接 Linux SCSI(st 驱动 + MTIO ioctl),后期可扩展厂商 SDK |
+
+---
+
+## 2. 系统架构总览
+
+### 2.1 物理部署模型
+
+ColdStore 由五类节点组成。**Scheduler Worker 是唯一的业务中枢**,
+Gateway 仅连接 Scheduler Worker,Console 仅连接 Metadata。
+
+```
+┌────────────────┐ ┌────────────────┐
+│ Gateway (N台) │ │ Console (1台) │
+│ S3 HTTP 前端 │ │ 管控面 Web UI │
+│ 无状态 │ │ 无状态 │
+└──────┬─────────┘ └──────┬─────────┘
+ │ gRPC (配置: Scheduler 地址) │ gRPC (配置: Metadata 地址)
+ ▼ ▼
+ ┌─────────────────────────────┐ ┌──────────────────────────┐
+ │ 同一物理节点 │ │ Metadata 节点 (3/5 台) │
+ │ ┌─────────────────────────┐ │ │ Raft 共识 + RocksDB │
+ │ │ Scheduler Worker │─┼───►│ 元数据 + Worker 注册中心 │
+ │ │ 业务中枢:调度编排 │ │ └──────────────────────────┘
+ │ ├─────────────────────────┤ │ ▲
+ │ │ Cache Worker │ │ │ 心跳
+ │ │ SPDK Blobstore 缓存 │ │ ┌──────────┴───────────────┐
+ │ └─────────────────────────┘ │ │ Tape Worker (独立物理节点) │
+ └─────────────────────────────┘ │ 磁带驱动 /dev/nst* │
+ │ 带库 /dev/sg* │
+ └────────────────────────────┘
+```
+
+| 节点类型 | 职责 | 数量 | 连接对象 |
+|----------|------|------|----------|
+| **Metadata** | Raft 共识、元数据存储、Worker 注册中心 | 3/5 | 三类 Worker 心跳;Console 管控 |
+| **Scheduler Worker** | **业务中枢**:调度编排、对接全部层(与 Cache 同机) | 1~N | Metadata、Cache Worker、Tape Worker、Gateway |
+| **Cache Worker** | SPDK NVMe 数据缓存(与 Scheduler 同机) | 1~N | Scheduler Worker (gRPC)、Metadata (心跳) |
+| **Tape Worker** | 磁带驱动管理、数据读写(独立物理节点) | 1~N | Scheduler Worker (gRPC)、Metadata (心跳) |
+| **Gateway** | S3 HTTP 接入 + 协议适配(无状态) | N | **仅 Scheduler Worker** |
+| **Console** | 管控面 Web UI + Admin API(无状态) | 1 | **仅 Metadata** |
+
+**关键设计决策**:
+
+- **Gateway 不直连 Metadata**:Gateway 是纯粹的 S3 协议前端,所有业务请求(包括元数据查询)
+ 都通过 Scheduler Worker 代理。Gateway 配置文件中只需写 Scheduler Worker 地址。
+- **Scheduler Worker 是唯一业务中枢**:持有 MetadataClient,编排 Cache Worker 和 Tape Worker,
+ 接受 Gateway 的全部请求。
+- **Console 仅连 Metadata**:管控面直接查询/操作元数据集群,管理 Worker 的增删。
+- **Scheduler ↔ Cache 使用 gRPC**:虽然同机部署,仍通过 gRPC 跨网络连接,
+ 保持架构一致性,未来可独立扩展。
+
+### 2.2 逻辑分层架构
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ S3 接入层 (Axum) — Gateway │
+│ PUT / GET / HEAD / DELETE / RestoreObject / ListBuckets ... │
+│ S3 冷归档协议适配层 │
+│ StorageClass 映射 | RestoreRequest 解析 | x-amz-restore 响应 | 错误码映射 │
+└─────────────────────────────────────────────────────────────────────────────┘
+ │ gRPC (全部请求)
+ ▼
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ 归档/取回调度器 — Scheduler Worker(业务中枢) │
+│ 接受 Gateway 请求 │ 编排 Cache 和 Tape │ 读写 Metadata │
+└─────────────────────────────────────────────────────────────────────────────┘
+ │ │ │
+ ▼ gRPC ▼ gRPC (同机) ▼ gRPC (远程)
+┌───────────────────────┐ ┌───────────────────────┐ ┌───────────────────────┐
+│ 元数据集群 (Raft) │ │ 数据缓存层 (SPDK) │ │ 磁带管理层 (Tape) │
+│ Metadata 节点 │ │ Cache Worker │ │ Tape Worker │
+│ RocksDB 持久化 │ │ 用户态 NVMe 缓存 │ │ 自研 SDK + SCSI │
+└───────────────────────┘ └───────────────────────┘ └───────────────────────┘
+```
+
+### 2.3 交互模式
+
+**Scheduler Worker 是唯一的元数据读写入口**(Console 管控除外)。
+Gateway、Cache Worker、Tape Worker 均不直接访问 Metadata。
+
+```
+ Console ──管控读写──▶ Metadata
+ ▲
+Gateway ──全部请求──▶ Scheduler Worker ──读+写──────────┘
+ │
+ ├──gRPC──▶ Cache Worker (纯数据存储)
+ └──gRPC──▶ Tape Worker (纯硬件抽象)
+```
+
+| 层 | 与 Metadata 的关系 | 与 Scheduler 的关系 | 说明 |
+|------|------|------|------|
+| Gateway | **无直接连接** | gRPC 客户端 | 所有请求代理给 Scheduler |
+| Scheduler Worker | 读 + 写 | — | 唯一业务中枢 |
+| Cache Worker | 仅心跳注册 | gRPC 服务端 | 纯数据存储 |
+| Tape Worker | 仅心跳注册 | gRPC 服务端 | 纯硬件抽象 |
+| Console | 管控读写 | 无 | Worker 增删、元数据查询 |
+
+---
+
+## 3. S3 冷归档协议兼容设计
+
+### 3.1 协议兼容目标
+
+ColdStore 是**纯冷归档系统**,设计模型与 AWS S3 Glacier Deep Archive 一致:
+所有对象写入即排队归档到磁带,不提供在线热存储层。外部热存储系统在需要冷归档时调用 ColdStore 的 PutObject API,迁移决策由外部系统负责。
+
+在协议层面兼容 AWS S3 Glacier 冷归档语义,使现有 S3 客户端、SDK 及业务系统无需改造即可接入。
+
+### 3.2 存储类别映射
+
+ColdStore 仅有两种内部存储状态:
+
+| S3 Storage Class | ColdStore 内部状态 | 说明 |
+|------------------|-------------------|------|
+| 任意(`GLACIER` / `DEEP_ARCHIVE` / `STANDARD` 等) | `ColdPending` | 写入即排队归档(统一冷存储入口) |
+| 归档完成 | `Cold` | 已归档到磁带 |
+
+### 3.3 RestoreObject API 规范
+
+#### 3.3.1 请求格式
+
+```
+POST /{Bucket}/{Key}?restore&versionId={VersionId} HTTP/1.1
+Host: {endpoint}
+Content-Type: application/xml
+
+
+ integer
+
+ Expedited|Standard|Bulk
+
+
+```
+
+**关键参数:**
+
+| 参数 | 必填 | 说明 |
+|------|------|------|
+| `Days` | 是 | 解冻数据保留天数,最小 1 天 |
+| `GlacierJobParameters.Tier` | 否 | 取回优先级:Expedited / Standard / Bulk |
+| `versionId` | 否 | 对象版本,缺省为当前版本 |
+
+#### 3.3.2 取回层级 (Tier) 语义
+
+| Tier | 预期完成时间 | ColdStore 实现策略 |
+|------|-------------|-------------------|
+| **Expedited** | 1–5 分钟 | 高优先级队列,可配置预留容量 |
+| **Standard** | 3–5 小时 | 默认队列,常规调度 |
+| **Bulk** | 5–12 小时 | 低优先级,批量合并取回 |
+
+#### 3.3.3 响应规范
+
+| 场景 | HTTP 状态码 | 说明 |
+|------|------------|------|
+| 首次解冻请求 | `202 Accepted` | 任务已接受,等待处理 |
+| 已解冻且未过期 | `200 OK` | 可延长 Days,仅更新过期时间 |
+| 解冻进行中 | `409 Conflict` | 错误码 `RestoreAlreadyInProgress` |
+| Expedited 容量不足 | `503 Service Unavailable` | 错误码 `GlacierExpeditedRetrievalNotAvailable` |
+| 对象尚未归档 | `409 Conflict` | ColdPending 状态对象不可 Restore |
+
+### 3.4 HEAD Object 与 x-amz-restore 响应头
+
+对冷对象执行 HEAD 时,需返回 `x-amz-restore` 头以表示解冻状态:
+
+```
+x-amz-restore: ongoing-request="true"
+```
+或
+```
+x-amz-restore: ongoing-request="false", expiry-date="Fri, 28 Feb 2025 12:00:00 GMT"
+```
+
+### 3.5 GET Object 冷对象访问控制
+
+| 对象状态 | GET 行为 |
+|----------|----------|
+| ColdPending(排队中) | 返回 `403 InvalidObjectState`,对象尚未归档 |
+| Cold + 未解冻 | 返回 `403 InvalidObjectState`,提示需先 Restore |
+| Cold + 解冻中 | 返回 `403 InvalidObjectState` |
+| Cold + 已解冻 | 从 SPDK 缓存读取并返回 |
+| Cold + 解冻已过期 | 返回 `403 InvalidObjectState`,需重新 Restore |
+
+### 3.6 错误码映射
+
+| AWS 错误码 | HTTP 状态 | ColdStore 实现 |
+|------------|-----------|----------------|
+| `InvalidObjectState` | 403 | 冷对象未解冻时 GET |
+| `RestoreAlreadyInProgress` | 409 | 重复 Restore 请求 |
+| `GlacierExpeditedRetrievalNotAvailable` | 503 | Expedited 容量不足 |
+### 3.7 归档触发模型
+
+ColdStore 是纯冷归档系统(类似 AWS Glacier Deep Archive),所有对象 PutObject 写入即标记为 `ColdPending`,
+自动排队等待调度器聚合写入磁带。不支持 Hot/Warm 存储类别和生命周期规则。
+
+外部热存储系统在需要冷归档时,直接调用 ColdStore 的 PutObject API 写入对象。
+迁移决策(何时归档)由外部系统负责,ColdStore 不做生命周期管理。
+
+---
+
+## 4. 集群元数据:Raft + RocksDB 设计
+
+### 4.1 设计目标
+
+- **强一致性**:元数据读写通过 Raft 达成共识
+- **高可用**:多数节点存活即可服务
+- **持久化**:RocksDB 作为 Raft 日志与状态机的存储引擎
+- **可扩展**:支持 3/5/7 节点集群
+
+### 4.2 架构图
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ ColdStore 元数据节点 │
+├─────────────────────────────────────────────────────────────────┤
+│ ┌─────────────────────────────────────────────────────────────┐ │
+│ │ Raft Core (openraft) │ │
+│ │ - databendlabs/openraft │ │
+│ │ - 异步事件驱动,Tokio 运行时 │ │
+│ └─────────────────────────────────────────────────────────────┘ │
+│ │ │
+│ │ RaftLogStorage / RaftStateMachine │
+│ ▼ │
+│ ┌─────────────────────────────────────────────────────────────┐ │
+│ │ openraft-rocksstore (RocksStore) │ │
+│ │ - Raft 日志 + 状态机持久化 │ │
+│ │ - RocksDB 单实例,可扩展 CF 存储业务元数据 │ │
+│ └─────────────────────────────────────────────────────────────┘ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+### 4.3 技术选型(已确定)
+
+| 组件 | 选型 | 说明 |
+|------|------|------|
+| Raft 实现 | **openraft** | [databendlabs/openraft](https://github.com/databendlabs/openraft),异步 Raft,Tokio 驱动 |
+| Raft 存储 | **openraft-rocksstore** | [crates.io/openraft-rocksstore](https://crates.io/crates/openraft-rocksstore),RocksDB 后端 |
+| 存储引擎 | RocksDB | 通过 openraft-rocksstore 封装 |
+
+### 4.3.1 OpenRaft 集成要点
+
+- **依赖**:`openraft = "0.9"`,`openraft-rocksstore = "0.9"`
+- **TypeConfig**:定义 `NodeId`、`Node`、`AppData`、`AppDataResponse` 等类型
+- **RocksRequest**:扩展 `AppData`,定义 `PutObject`、`DeleteObject`、`PutRecallTask` 等命令
+- **RocksStateMachine**:实现 `apply` 逻辑,将命令写入 RocksDB 业务 CF
+- **线性读**:`raft.ensure_linearizable().await` 保证读到已提交状态
+
+### 4.4 RocksDB 数据模型
+
+#### 4.4.1 Column Family 设计
+
+| CF 名称 | Key 格式 | Value 格式 | 说明 |
+|---------|----------|-----------|------|
+| `objects` | `obj:{bucket}:{key}` | ObjectMetadata (JSON/Protobuf) | 对象元数据 |
+| `object_versions` | `objv:{bucket}:{key}:{version}` | ObjectMetadata | 多版本对象 |
+| `bundles` | `bundle:{uuid}` | ArchiveBundle | 归档包 |
+| `tapes` | `tape:{tape_id}` | TapeInfo | 磁带信息 |
+| `recall_tasks` | `recall:{uuid}` | RecallTask | 取回任务 |
+| `archive_tasks` | `archive:{uuid}` | ArchiveTask | 归档任务 |
+| `index_bundle_objects` | `idx_b:{archive_id}:{bucket}:{key}` | 空 | 归档包→对象反向索引 |
+| `index_tape_bundles` | `idx_t:{tape_id}:{bundle_id}` | 空 | 磁带→归档包索引 |
+
+#### 4.4.2 Raft 日志存储 (raftdb)
+
+- **Key**: `{log_index}` (8 bytes)
+- **Value**: Raft 日志条目序列化
+- **用途**: 仅存储 Raft 共识日志,与业务 KV 分离
+
+### 4.5 Raft 状态机命令(AppData / RocksRequest 扩展)
+
+参考 openraft-rocksstore 的 `RocksRequest` 模式,扩展 ColdStore 专用命令:
+
+```rust
+// 实现 openraft::AppData
+#[derive(Clone, Debug, Serialize, Deserialize)]
+enum ColdStoreRequest {
+ PutObject(ObjectMetadata),
+ DeleteObject { bucket: String, key: String },
+ UpdateStorageClass { bucket: String, key: String, class: StorageClass },
+ PutArchiveBundle(ArchiveBundle),
+ PutTape(TapeInfo),
+ PutRecallTask(RecallTask),
+ UpdateRecallTask { id: Uuid, status: RestoreStatus },
+ PutArchiveTask(ArchiveTask),
+}
+```
+
+可基于 `openraft-rocksstore` 的 `RocksStore` 进行扩展,或实现自定义 `RaftStorage` 以支持上述业务命令。
+
+### 4.6 读写路径
+
+| 操作类型 | 路径 |
+|----------|------|
+| **写** | Client → Leader → Raft Propose → 多数派 Append → Apply → RocksDB Write |
+| **读** | Client → 任意节点 → 本地 RocksDB Read(需保证 ReadYourWrites 时可选走 Leader) |
+| **线性读** | 通过 Raft 的 ReadIndex 保证读到已提交状态 |
+
+### 4.7 集群部署
+
+- **推荐配置**:3 或 5 节点
+- **Leader 选举**:Raft 自动选举
+- **网络**:节点间 gRPC 或自定义 RPC
+- **持久化路径**:`/var/lib/coldstore/metadata/{raftdb|kvdb}`
+
+### 4.8 与现有代码集成
+
+- 新增 `MetadataBackend::RaftRocksDB` 枚举变体
+- `MetadataService` 通过 Raft 客户端代理写请求
+- 读请求可直接访问本地 RocksDB(Follower 只读)
+
+---
+
+## 5. 数据缓存层:async-spdk 设计
+
+### 5.1 设计目标
+
+- **高性能**:用户态 I/O,绕过内核,降低延迟
+- **高吞吐**:Poll 模式、零拷贝,充分发挥 NVMe 性能
+- **解冻数据缓存**:承载从磁带取回的数据,供 GET 快速响应
+- **原生 async/await**:与 Tokio 无缝集成,无需回调桥接
+
+### 5.2 技术选型:async-spdk + SPDK Blob
+
+采用 [madsys-dev/async-spdk](https://github.com/madsys-dev/async-spdk):
+
+- **异步 Rust 绑定**:原生 `async`/`await`,基于 Tokio
+- **存储方式**:**SPDK Blobstore (Blob)**,非 raw bdev;Blobstore 构建于 bdev 之上
+- **优势**:Blob 提供持久化块分配、xattrs 元数据、thin provisioning,适合对象缓存
+
+### 5.3 架构概览
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ ColdStore 缓存服务进程 │
+├─────────────────────────────────────────────────────────────────┤
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ Cache Manager (Rust) │ │
+│ │ - cache_key → blob_id 索引 │ │
+│ │ - LRU/LFU/TTL 淘汰策略 │ │
+│ │ - 容量与并发控制 │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │ │
+│ ▼ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ async-spdk (SPDK Blobstore / Blob API) │ │
+│ │ - 1 对象 = 1 Blob,xattrs 存 bucket/key/expire_at │ │
+│ │ - blob_write / blob_read 按 page 读写 │ │
+│ │ - 构建于 bdev 之上 │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │ │
+│ ▼ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ SPDK Bdev (Blobstore 底层) │ │
+│ │ - Malloc0: 测试用内存 bdev │ │
+│ │ - Nvme0n1: 生产 NVMe 设备 │ │
+│ └─────────────────────────────────────────────────────────┘ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+### 5.4 集成方式与启动模型
+
+async-spdk 采用 **SPDK 事件循环 + Tokio** 的混合模型:
+
+```rust
+use async_spdk::{event::AppOpts, bdev::*, env};
+
+fn main() {
+ event::AppOpts::new()
+ .name("coldstore_cache")
+ .config_file(&std::env::args().nth(1).expect("config file"))
+ .block_on(async_main())
+ .unwrap();
+}
+
+async fn async_main() -> Result<()> {
+ // Blobstore 方式:先加载/初始化 Blobstore,再通过 Blob API 读写
+ // let bs = Blobstore::load("Malloc0").await?;
+ // let blob_id = bs.create_blob().await?;
+ // bs.blob_write(blob_id, offset_pages, buf).await?;
+ // bs.blob_read(blob_id, offset_pages, buf).await?;
+ app_stop();
+ Ok(())
+}
+```
+
+**注意**:ColdStore 主进程若已使用 Tokio(Axum),需将 async-spdk 作为**独立子模块**或**专用线程**运行,因 SPDK 需独占 reactor。可选方案:
+
+- **方案 A**:缓存服务作为独立二进制,通过 gRPC/HTTP 与主服务通信
+- **方案 B**:主进程在 SPDK `block_on` 内启动 Axum(需调整启动顺序)
+- **方案 C**:缓存层先使用文件后端,待架构稳定后再接入 async-spdk
+
+### 5.5 SPDK JSON 配置示例
+
+**测试用 Malloc bdev**(`cache_spdk.json`):
+
+```json
+{
+ "subsystems": [
+ {
+ "subsystem": "bdev",
+ "config": [
+ {
+ "method": "bdev_malloc_create",
+ "params": {
+ "name": "Malloc0",
+ "num_blocks": 32768,
+ "block_size": 512
+ }
+ }
+ ]
+ }
+ ]
+}
+```
+
+**生产 NVMe**:根据 [async-spdk README](https://github.com/madsys-dev/async-spdk),需配置对应 bdev 名称(如 `Nvme0n1`),并确保:
+
+- 以 root 运行
+- 大页内存:`echo "1024" > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages`
+- SPDK 编译:`./configure --without-isal` 可避免部分 isa-l 错误
+
+### 5.6 缓存数据布局(SPDK Blob 方式)
+
+| 层级 | 存储介质 | 用途 |
+|------|----------|------|
+| L1 | NVMe SSD (SPDK Blobstore on bdev) | 热解冻数据,低延迟 |
+| L2 (可选) | 文件系统 | 兼容旧实现,温数据 |
+
+对象与 Blob 的对应:
+
+- **1 对象 = 1 Blob**:每个解冻对象对应一个 Blobstore Blob
+- **元数据**:Blob xattrs 存储 bucket、key、size、checksum、expire_at
+- **索引**:自维护 `cache_key → blob_id` 映射(Blobstore 不提供按名查找)
+- **读写单位**:Page(4KB),Cluster(1MB)为分配单元
+
+### 5.7 缓存策略
+
+| 策略 | 说明 |
+|------|------|
+| **LRU** | 淘汰最久未访问对象 |
+| **LFU** | 淘汰访问频率最低对象 |
+| **TTL** | 按解冻过期时间淘汰 |
+| **容量** | 总容量上限,超限触发淘汰 |
+
+### 5.8 配置项
+
+```yaml
+cache:
+ backend: "spdk" # spdk | file (兼容旧实现)
+ spdk:
+ enabled: true
+ config_file: "/etc/coldstore/cache_spdk.json"
+ bdev_name: "Malloc0" # 测试用; 生产改为 "Nvme0n1"
+ max_size_gb: 100
+ block_size: 4096
+ ttl_secs: 86400
+ eviction_policy: "Lru"
+```
+
+### 5.9 Cargo 依赖
+
+```toml
+[dependencies]
+async-spdk = { git = "https://github.com/madsys-dev/async-spdk" }
+# 或发布到 crates.io 后: async-spdk = "0.1"
+```
+
+---
+
+## 6. 磁带管理层:自研 SDK 抽象层
+
+### 6.1 设计目标
+
+- **自研 SDK 抽象层**:不依赖厂商闭源 SDK,统一磁带、驱动、库的访问接口
+- **前期对接 Linux SCSI**:基于 Linux 内核 SCSI 磁带驱动(st/ sg)实现
+- **可扩展**:后期可替换为厂商 SDK、LTFS 等实现,无需改动上层调度逻辑
+
+### 6.2 架构分层
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ TapeManager (调度层) │
+│ - 磁带库抽象、驱动调度、归档包聚合、取回合并 │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ Tape SDK 抽象层 (自研) │
+│ - TapeDrive trait: read, write, seek, status, eject │
+│ - TapeLibrary trait: list_slots, load, unload, inventory │
+│ - 与具体实现解耦 │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ 实现层 (前期) │
+│ Linux SCSI: st 驱动 (/dev/nst0) + MTIO ioctl │
+│ - 顺序读写、定位、状态查询 │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+### 6.3 前期实现:Linux SCSI 协议
+
+#### 6.3.1 设备与接口
+
+| 接口 | 设备节点 | 说明 |
+|------|----------|------|
+| **st 驱动** | `/dev/nst0` | 非自动回卷,推荐用于顺序读写(避免每次操作回卷) |
+| **st 驱动** | `/dev/st0` | 自动回卷,操作后回卷到 BOT |
+| **SCSI Generic** | `/dev/sg*` | 透传 SCSI 命令,用于高级控制(可选) |
+
+#### 6.3.2 MTIO 控制接口
+
+基于 ``,主要 ioctl:
+
+| ioctl | 用途 |
+|-------|------|
+| `MTIOCTOP` | 执行磁带操作:`MTFSF`(前进文件)、`MTBSF`(后退文件)、`MTFSR`(前进记录)、`MTBSR`(后退记录)、`MTREW`(回卷)、`MTEOM`(到卷尾) |
+| `MTIOCGET` | 获取驱动状态 |
+| `MTIOCPOS` | 获取当前磁带位置 |
+
+#### 6.3.3 数据读写
+
+- **块模式**:可变块或固定块(通过 ioctl 设置)
+- **读写**:标准 `read()` / `write()` 系统调用
+- **顺序性**:磁带必须顺序写入,随机读需 seek 后再读
+
+#### 6.3.4 依赖与工具
+
+- `mt-st`:mt 命令,用于控制磁带(可选,SDK 自实现 ioctl)
+- 内核:`st` 模块(`modprobe st`)
+- 权限:设备节点需可读写(通常 root 或 tape 组)
+
+### 6.4 SDK 抽象接口定义
+
+```rust
+/// 磁带驱动抽象
+pub trait TapeDrive: Send + Sync {
+ fn drive_id(&self) -> &str;
+ async fn read(&self, offset: u64, length: u64) -> Result>;
+ async fn write(&self, data: &[u8]) -> Result<()>;
+ async fn seek(&self, position: u64) -> Result<()>;
+ async fn status(&self) -> Result;
+ async fn rewind(&self) -> Result<()>;
+ async fn eject(&self) -> Result<()>;
+}
+
+/// 磁带库抽象(带机械臂的库,后期扩展)
+pub trait TapeLibrary: Send + Sync {
+ async fn list_slots(&self) -> Result>;
+ async fn load(&self, slot_id: &str, drive_id: &str) -> Result<()>;
+ async fn unload(&self, drive_id: &str) -> Result<()>;
+ async fn inventory(&self) -> Result>;
+}
+
+/// Linux SCSI 实现(前期)
+pub struct ScsiTapeDrive {
+ device_path: PathBuf, // e.g. /dev/nst0
+ // ...
+}
+impl TapeDrive for ScsiTapeDrive { ... }
+```
+
+### 6.5 实现路径
+
+| 阶段 | 实现 | 说明 |
+|------|------|------|
+| **前期** | `ScsiTapeDrive` | 基于 st 驱动 + MTIO,单驱动直连 |
+| **中期** | `ScsiTapeLibrary` | 带库场景,通过 sg 或厂商 MMC 协议控制机械臂 |
+| **后期** | `VendorTapeDrive` | 可选对接厂商 SDK(如 IBM、HP 等) |
+
+### 6.6 配置项
+
+```yaml
+tape:
+ sdk:
+ backend: "scsi" # scsi | vendor (后期)
+ scsi:
+ device: "/dev/nst0"
+ block_size: 262144 # 256KB,LTO 常用
+ buffer_size_mb: 64
+ library_path: null # 带库场景时配置
+ supported_formats:
+ - "LTO-9"
+ - "LTO-10"
+```
+
+### 6.7 模块结构
+
+```
+src/tape/
+├── mod.rs
+├── sdk/ # 自研 SDK 抽象层
+│ ├── mod.rs
+│ ├── drive.rs # TapeDrive trait
+│ ├── library.rs # TapeLibrary trait (可选)
+│ └── types.rs # DriveStatus, SlotInfo 等
+├── scsi/ # Linux SCSI 实现
+│ ├── mod.rs
+│ ├── drive.rs # ScsiTapeDrive
+│ └── mtio.rs # MTIO ioctl 封装
+├── manager.rs # TapeManager
+└── driver.rs # 兼容旧接口,委托给 sdk
+```
+
+---
+
+## 7. 模块交互与数据流
+
+### 7.1 Restore 完整流程
+
+```
+1. Client: POST /bucket/key?restore + RestoreRequest
+2. S3 Handler: 解析 Days/Tier,校验对象为 Cold
+3. Metadata (Raft): 检查是否已有进行中 Restore,写入 RecallTask
+4. Recall Scheduler: 按 archive_id 合并任务,调度磁带读取
+5. Tape: 顺序读取 → 数据块
+6. Cache (SPDK): 写入 SPDK bdev,更新索引
+7. Metadata (Raft): 更新 ObjectMetadata.restore_status=Completed, restore_expire_at
+8. Client: HEAD 可见 x-amz-restore: expiry-date="..."
+9. Client: GET 从 SPDK 缓存返回数据
+```
+
+### 7.2 Archive 完整流程
+
+```
+1. PutObject → 对象直接标记 ColdPending(写入即归档)
+2. Archive Scheduler: 扫描 ColdPending,按策略聚合为 ArchiveBundle
+3. Tape: 顺序写入磁带
+4. Metadata (Raft): 写入 ArchiveBundle, 更新 ObjectMetadata (Cold, archive_id, tape_id)
+5. 清理缓存层临时数据
+```
+
+---
+
+## 8. 非功能性设计
+
+### 8.1 高可用
+
+- 元数据:Raft 3/5 节点,多数派存活即可
+- 缓存:单点故障时,解冻数据需重新从磁带取回(可接受)
+- S3 接入:无状态,可多实例 + 负载均衡
+
+### 8.2 可观测性
+
+- 日志:tracing + structured logging
+- 指标:归档/取回队列长度、缓存命中率、Raft 提交延迟
+- 追踪:关键路径分布式 trace
+
+### 8.3 安全
+
+- S3 签名 v4 认证
+- 传输加密 TLS
+- 元数据集群节点间 mTLS(可选)
+
+---
+
+## 9. 实施路线图
+
+| 阶段 | 内容 |
+|------|------|
+| **Phase 1** | S3 RestoreObject 协议完整实现,错误码与 x-amz-restore 头 |
+| **Phase 2** | Raft + RocksDB 元数据集群,替换 Postgres/Etcd |
+| **Phase 3** | SPDK 缓存层集成,替换文件系统缓存 |
+| **Phase 4** | 磁带 SDK 抽象层 + Linux SCSI 实现(ScsiTapeDrive) |
+| **Phase 5** | 性能调优、生产就绪 |
+
+---
+
+## 10. Cargo 依赖汇总
+
+```toml
+[dependencies]
+# 元数据集群
+openraft = "0.9"
+openraft-rocksstore = "0.9"
+
+# 数据缓存层
+async-spdk = { git = "https://github.com/madsys-dev/async-spdk" }
+
+# 现有依赖保持不变
+tokio = { version = "1.35", features = ["full"] }
+axum = "0.7"
+rocksdb = "0.22" # openraft-rocksstore 会引入,可按需显式指定版本
+```
+
+---
+
+## 11. 参考资料
+
+- [AWS S3 RestoreObject API](https://docs.aws.amazon.com/AmazonS3/latest/API/API_RestoreObject.html)
+- [S3 Glacier Retrieval Options](https://docs.aws.amazon.com/AmazonS3/latest/userguide/restoring-objects-retrieval-options.html)
+- [OpenRaft](https://github.com/databendlabs/openraft) - databendlabs/openraft
+- [OpenRaft RocksStore](https://crates.io/crates/openraft-rocksstore)
+- [async-spdk](https://github.com/madsys-dev/async-spdk) - madsys-dev/async-spdk
+- [SPDK Documentation](https://spdk.io/doc/)
+- [Linux st(4) - SCSI tape device](https://man7.org/linux/man-pages/man4/st.4.html)
+- [Linux SCSI tape driver (kernel.org)](https://kernel.org/doc/Documentation/scsi/st.txt)
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..b8ca9fc
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,28 @@
+# ColdStore 设计文档
+
+## 文档索引
+
+| 文档 | 说明 |
+|------|------|
+| [DESIGN.md](./DESIGN.md) | **总架构设计** - 协议、元数据集群、缓存层、磁带层完整设计 |
+| [modules/](./modules/) | **模块设计** - 按架构分层拆分的独立设计文档 |
+
+## 模块设计文档
+
+| 模块 | 文档 |
+|------|------|
+| 接入层 | [01-access-layer.md](./modules/01-access-layer.md) |
+| 协议适配层 | [02-protocol-adapter.md](./modules/02-protocol-adapter.md) |
+| 元数据集群 | [03-metadata-cluster.md](./modules/03-metadata-cluster.md) |
+| 数据缓存层 | [04-cache-layer.md](./modules/04-cache-layer.md) |
+| 归档取回调度层 | [05-scheduler-layer.md](./modules/05-scheduler-layer.md) |
+| 磁带管理层 | [06-tape-layer.md](./modules/06-tape-layer.md) |
+
+## 设计要点摘要
+
+- **协议**:兼容 S3 Glacier 冷归档协议(RestoreObject、x-amz-restore、取回层级)
+- **元数据**:[OpenRaft](https://github.com/databendlabs/openraft) + [openraft-rocksstore](https://crates.io/crates/openraft-rocksstore),强一致性集群
+- **缓存**:[async-spdk](https://github.com/madsys-dev/async-spdk) 用户态 NVMe 缓存,原生 async/await
+- **磁带**:自研 SDK 抽象层,前期对接 Linux SCSI(st 驱动 + MTIO)
+
+详见 [DESIGN.md](./DESIGN.md) 与 [modules/README.md](./modules/README.md)。
diff --git a/docs/modules/01-access-layer.md b/docs/modules/01-access-layer.md
new file mode 100644
index 0000000..f501d87
--- /dev/null
+++ b/docs/modules/01-access-layer.md
@@ -0,0 +1,122 @@
+# 接入层模块设计
+
+> 所属架构:ColdStore 冷存储系统
+> 参考:[总架构设计](../DESIGN.md)
+
+## 1. 模块概述
+
+接入层是 ColdStore 对外提供对象存储服务的入口,负责接收并分发 S3 兼容的 HTTP 请求。
+
+### 1.1 职责
+
+- 提供 HTTP 服务,监听 S3 端点
+- 路由解析:将请求分发到对应 Handler
+- 请求/响应透传与基础校验
+- 与协议适配层、元数据、缓存、调度器协同
+
+### 1.2 在架构中的位置
+
+```
+ ┌─────────────────────────────────────┐
+ │ S3 接入层 (本模块) │
+ │ PUT / GET / HEAD / RestoreObject │
+ │ ListBuckets / DeleteObject │
+ └─────────────────────────────────────┘
+ │
+ ▼
+ 协议适配层 / 元数据 / 缓存 / 调度器
+```
+
+---
+
+## 2. 技术选型
+
+| 组件 | 选型 | 说明 |
+|------|------|------|
+| HTTP 框架 | **Axum** | 异步、类型安全、与 Tokio 集成 |
+| 运行时 | **Tokio** | 异步运行时 |
+| 中间件 | **Tower** / **tower-http** | 中间件栈、CORS、trace |
+
+---
+
+## 3. 接口清单
+
+### 3.1 对象操作
+
+| 方法 | 路径 | 说明 |
+|------|------|------|
+| PUT | `/{bucket}/{key}` | 上传对象 |
+| GET | `/{bucket}/{key}` | 下载对象 |
+| HEAD | `/{bucket}/{key}` | 获取对象元数据 |
+| DELETE | `/{bucket}/{key}` | 删除对象 |
+| POST | `/{bucket}/{key}?restore` | 解冻冷对象(RestoreObject) |
+
+### 3.2 桶操作
+
+| 方法 | 路径 | 说明 |
+|------|------|------|
+| GET | `/` | ListBuckets |
+| PUT | `/{bucket}` | CreateBucket |
+| GET | `/{bucket}` | ListObjects |
+| DELETE | `/{bucket}` | DeleteBucket |
+
+### 3.3 路由规则
+
+- 路径参数:`bucket`、`key`(支持多级 key)
+- 查询参数:`versionId`、`restore` 等
+- 虚拟主机风格与路径风格均需支持(可配置)
+
+---
+
+## 4. 模块结构
+
+```
+src/
+├── main.rs # 启动 Axum,加载配置
+├── s3/
+│ ├── mod.rs
+│ ├── server.rs # Axum Router 定义、路由注册
+│ ├── handler.rs # 请求处理入口,委托给协议适配/业务逻辑
+│ └── routes.rs # 路由常量、路径解析
+```
+
+---
+
+## 5. 依赖关系
+
+| 依赖模块 | 通信方式 | 用途 |
+|----------|----------|------|
+| 协议适配层 | 本地调用(同进程) | 解析 RestoreRequest、生成 x-amz-restore 响应、错误码映射 |
+| Scheduler Worker | gRPC(配置地址) | **全部**业务请求代理(PUT/GET/HEAD/DELETE/Restore/List) |
+
+> Gateway **不连接 Metadata**,也**不连接 Cache Worker 和 Tape Worker**。
+> 所有业务流量都通过 Scheduler Worker 中转。Gateway 是纯粹的 S3 HTTP → gRPC 协议前端。
+
+---
+
+## 6. 配置项
+
+```yaml
+gateway:
+ s3:
+ host: "0.0.0.0"
+ port: 9000
+ path_style: true
+
+ # 仅需配置 Scheduler Worker 地址
+ scheduler_addrs:
+ - "10.0.2.1:22001"
+ - "10.0.2.2:22001"
+
+ # 多 Scheduler Worker 时的路由策略
+ routing:
+ strategy: "round_robin" # round_robin | hash | least_pending
+```
+
+---
+
+## 7. 非功能性要求
+
+- **无状态**:可水平扩展,多实例 + 负载均衡
+- **可观测**:请求 trace、耗时、错误率
+- **安全**:支持 S3 签名 v4、TLS
diff --git a/docs/modules/02-protocol-adapter.md b/docs/modules/02-protocol-adapter.md
new file mode 100644
index 0000000..6a24ad4
--- /dev/null
+++ b/docs/modules/02-protocol-adapter.md
@@ -0,0 +1,157 @@
+# 协议适配层模块设计
+
+> 所属架构:ColdStore 冷存储系统
+> 参考:[总架构设计](../DESIGN.md)
+
+## 1. 模块概述
+
+协议适配层负责将 S3 冷归档(Glacier)协议语义转换为 ColdStore 内部模型,并生成符合 S3 规范的响应。
+
+### 1.1 职责
+
+- StorageClass 映射(S3 ↔ 内部状态)
+- RestoreRequest 解析(Days、Tier)
+- x-amz-restore 响应头生成
+- 错误码映射(InvalidObjectState、RestoreAlreadyInProgress 等)
+- PutObject 默认 ColdPending 语义
+
+### 1.2 在架构中的位置
+
+```
+S3 接入层 ──► 协议适配层 ──► Scheduler Worker(业务中枢)
+ │
+ ├─ StorageClass 映射
+ ├─ RestoreRequest 解析
+ ├─ x-amz-restore 响应
+ └─ 错误码映射
+```
+
+---
+
+## 2. StorageClass 映射
+
+ColdStore 是纯冷归档系统,所有对象写入即为 `ColdPending`,归档后变为 `Cold`。与 AWS S3 Glacier Deep Archive 模型一致。
+
+| S3 Storage Class | ColdStore 内部 | 说明 |
+|------------------|----------------|------|
+| `GLACIER` / `DEEP_ARCHIVE` / `STANDARD` / 任意 | `ColdPending` | 写入即排队归档(不区分热/冷,统一冷存储) |
+| 归档完成 | `Cold` | 已归档到磁带 |
+
+---
+
+## 3. RestoreObject 协议适配
+
+### 3.1 请求解析
+
+**输入**:`POST /{bucket}/{key}?restore` + XML Body
+
+```xml
+
+ 7
+
+ Standard
+
+
+```
+
+**输出**(内部结构):
+
+```rust
+pub struct RestoreRequest {
+ pub days: u32, // 最小 1
+ pub tier: RestoreTier, // Expedited | Standard | Bulk
+ pub version_id: Option,
+}
+```
+
+### 3.2 取回层级 (Tier) 映射
+
+| Tier | 内部优先级 | 调度策略 |
+|------|------------|----------|
+| Expedited | 高 | 高优先级队列,可配置预留容量 |
+| Standard | 中 | 默认队列 |
+| Bulk | 低 | 批量合并取回 |
+
+### 3.3 响应规范
+
+| 场景 | HTTP 状态 | 说明 |
+|------|-----------|------|
+| 首次解冻 | 202 Accepted | 任务已接受 |
+| 已解冻且未过期 | 200 OK | 可延长 Days |
+| 解冻进行中 | 409 Conflict | RestoreAlreadyInProgress |
+| Expedited 容量不足 | 503 Service Unavailable | GlacierExpeditedRetrievalNotAvailable |
+| 对象尚未归档(ColdPending) | 409 Conflict | 尚在归档队列中,不可 Restore |
+
+---
+
+## 4. x-amz-restore 响应头
+
+对冷对象执行 HEAD 时,根据解冻状态返回:
+
+| 状态 | 响应头 |
+|------|--------|
+| 解冻中 | `x-amz-restore: ongoing-request="true"` |
+| 已解冻 | `x-amz-restore: ongoing-request="false", expiry-date="Fri, 28 Feb 2025 12:00:00 GMT"` |
+| 未解冻 | 不返回该头(或可省略) |
+
+---
+
+## 5. 错误码映射
+
+| AWS 错误码 | HTTP 状态 | 触发条件 |
+|------------|-----------|----------|
+| InvalidObjectState | 403 | 冷对象未解冻时 GET |
+| RestoreAlreadyInProgress | 409 | 重复 Restore 请求 |
+| GlacierExpeditedRetrievalNotAvailable | 503 | Expedited 容量不足 |
+| ObjectNotYetArchived | 409 | ColdPending 状态对象执行 Restore(尚未归档完成) |
+
+---
+
+## 6. GET Object 冷对象访问控制
+
+| 对象状态 | GET 行为 |
+|----------|----------|
+| Cold + 未解冻 | 403 InvalidObjectState |
+| Cold + 解冻中 | 403 InvalidObjectState |
+| Cold + 已解冻 | 从缓存读取并返回 |
+| Cold + 解冻已过期 | 403 InvalidObjectState |
+
+---
+
+## 7. PutObject 语义
+
+ColdStore 作为纯冷归档系统,PutObject 写入的对象一律标记为 `ColdPending`,
+由调度器扫描后自动归档到磁带。外部热存储系统在需要归档时直接调用 ColdStore 的 PutObject。
+
+---
+
+## 8. 模块结构
+
+```
+src/
+├── s3/
+│ ├── protocol/ # 协议适配(可独立子模块)
+│ │ ├── mod.rs
+│ │ ├── storage_class.rs # StorageClass 映射
+│ │ ├── restore.rs # RestoreRequest 解析、响应生成
+│ │ ├── errors.rs # 错误码映射
+│ │ └── put_object.rs # PutObject → ColdPending 语义
+```
+
+---
+
+## 9. 依赖关系
+
+| 依赖 | 用途 |
+|------|------|
+| Scheduler Worker (gRPC) | 全部业务请求:元数据查询、数据读写、Restore 任务提交 |
+
+> 协议适配层运行在 Gateway 进程内,与接入层同进程。
+> 所有对外通信都经由 Scheduler Worker,不直连 Metadata/Cache/Tape。
+
+---
+
+## 10. 参考资料
+
+- [AWS S3 RestoreObject API](https://docs.aws.amazon.com/AmazonS3/latest/API/API_RestoreObject.html)
+- [S3 Glacier Retrieval Options](https://docs.aws.amazon.com/AmazonS3/latest/userguide/restoring-objects-retrieval-options.html)
diff --git a/docs/modules/03-metadata-cluster.md b/docs/modules/03-metadata-cluster.md
new file mode 100644
index 0000000..5bb35e8
--- /dev/null
+++ b/docs/modules/03-metadata-cluster.md
@@ -0,0 +1,1163 @@
+# 元数据集群模块设计
+
+> 所属架构:ColdStore 冷存储系统
+> 参考:[总架构设计](../DESIGN.md)
+
+## 1. 模块概述
+
+元数据集群提供强一致性的集群元数据存储,基于 OpenRaft + RocksDB 实现,是 ColdStore 的"控制中枢"。
+
+元数据分为两大类:
+
+| 类别 | 内容 | 说明 |
+|------|------|------|
+| **集群元数据** | 节点信息、集群拓扑、Raft 状态 | 集群自身管理 |
+| **归档元数据** | 对象、桶、归档包、磁带、任务 | 业务数据管理 |
+
+### 1.1 职责
+
+- 对象元数据:bucket、key、storage_class、archive_id、tape_id、restore_status 等
+- 桶管理:桶创建/删除
+- 归档包、磁带、取回任务、归档任务索引
+- 集群管理:节点注册、Raft 领导选举、成员变更
+- 强一致性读写、高可用
+
+### 1.2 在架构中的位置
+
+```
+ Console ──管控读写──▶ Metadata
+ ▲
+Gateway ──全部请求──▶ Scheduler Worker ──读+写──────┘
+ │
+ ├──gRPC──▶ Cache Worker (纯数据)
+ └──gRPC──▶ Tape Worker (纯硬件)
+```
+
+> Gateway 不直连 Metadata。Scheduler Worker 是唯一的元数据业务读写入口。
+
+---
+
+## 2. 技术选型
+
+| 组件 | 选型 | 说明 |
+|------|------|------|
+| Raft 共识 | **openraft** | [databendlabs/openraft](https://github.com/databendlabs/openraft) |
+| 存储后端 | **openraft-rocksstore** | RocksDB 后端 |
+| 存储引擎 | RocksDB | LSM-tree,高吞吐 |
+
+---
+
+## 3. 核心数据结构
+
+### 3.1 ObjectMetadata(对象元数据)
+
+系统中最核心的结构,记录每个 S3 对象的完整状态。
+
+```rust
+pub struct ObjectMetadata {
+ pub bucket: String,
+ pub key: String,
+ pub version_id: Option,
+ pub size: u64,
+ pub checksum: String,
+ pub content_type: Option,
+ pub etag: Option,
+ pub storage_class: StorageClass,
+ pub archive_id: Option,
+ pub tape_id: Option,
+ pub tape_set: Option>,
+ pub tape_block_offset: Option,
+ pub restore_status: Option,
+ pub restore_expire_at: Option>,
+ pub created_at: DateTime,
+ pub updated_at: DateTime,
+}
+```
+
+| 字段 | 类型 | 含义 | 写入方 | 消费方 |
+|------|------|------|--------|--------|
+| `bucket` | String | S3 桶名 | 接入层 PutObject | 全部 |
+| `key` | String | S3 对象键 | 接入层 PutObject | 全部 |
+| `version_id` | Option\ | 对象版本 ID | 接入层 PutObject | 协议层、调度层 |
+| `size` | u64 | 对象字节数 | 接入层 PutObject | 调度层(聚合计算)、协议层(Content-Length) |
+| `checksum` | String | SHA256 hex | 接入层 PutObject | 调度层(写磁带/校验)、缓存层(透传) |
+| `content_type` | Option\ | MIME 类型 | 接入层 PutObject | 协议层 GET 响应头、调度层传给缓存层 |
+| `etag` | Option\ | S3 ETag | 接入层 PutObject | 协议层 GET 响应头、调度层传给缓存层 |
+| `storage_class` | StorageClass | 存储类别 | 接入层 PutObject / 调度层归档完成 | 协议层(判断是否可 GET)、调度层(扫描 ColdPending) |
+| `archive_id` | Option\ | 所属 ArchiveBundle ID | 调度层归档完成 | 调度层取回时定位 Bundle |
+| `tape_id` | Option\ | 主副本所在磁带 ID | 调度层归档完成 | 调度层取回时定位磁带 |
+| `tape_set` | Option\\> | 所有副本磁带列表 | 调度层归档完成 | 调度层副本切换 |
+| `tape_block_offset` | Option\ | 对象在磁带上的块偏移 | 调度层归档完成 | 调度层取回时 seek 定位 |
+| `restore_status` | Option\ | 解冻状态 | 调度层取回流程 | 协议层(判断 GET/HEAD 响应) |
+| `restore_expire_at` | Option\\> | 解冻过期时间 | 调度层取回完成 | 协议层(x-amz-restore 头)、缓存层(TTL) |
+| `created_at` | DateTime\ | 创建时间 | 接入层 PutObject | 调度层(聚合排序) |
+| `updated_at` | DateTime\ | 最后更新时间 | 每次写入 | 审计 |
+
+### 3.2 枚举类型
+
+```rust
+pub enum StorageClass {
+ ColdPending, // 待归档(PutObject 写入即标记,等待调度器扫描写入磁带)
+ Cold, // 已归档到磁带
+}
+
+pub enum RestoreStatus {
+ Pending, // 已入队,等待调度
+ WaitingForMedia, // 磁带离线,等待人工上线
+ InProgress, // 正在从磁带读取
+ Completed, // 已写入缓存,可 GET
+ Expired, // 解冻过期
+ Failed, // 取回失败
+}
+
+pub enum RestoreTier {
+ Expedited, // 加急:1-5 分钟
+ Standard, // 标准:3-5 小时
+ Bulk, // 批量:5-12 小时
+}
+```
+
+### 3.3 ArchiveBundle(归档包)
+
+与 05-scheduler-layer §8.3 定义一致。
+
+```rust
+pub struct ArchiveBundle {
+ pub id: Uuid,
+ pub tape_id: String,
+ pub tape_set: Vec,
+ pub entries: Vec,
+ pub total_size: u64,
+ pub filemark_start: u32,
+ pub filemark_end: u32,
+ pub checksum: Option,
+ pub status: ArchiveBundleStatus,
+ pub created_at: DateTime,
+ pub completed_at: Option>,
+}
+```
+
+| 字段 | 类型 | 含义 | 写入方 |
+|------|------|------|--------|
+| `id` | Uuid | 归档包唯一标识 | 调度层 |
+| `tape_id` | String | 主副本磁带 ID | 调度层 |
+| `tape_set` | Vec\ | 所有副本磁带 | 调度层 |
+| `entries` | Vec\ | 包内对象列表与偏移 | 调度层 |
+| `total_size` | u64 | 总字节数 | 调度层 |
+| `filemark_start` | u32 | 起始 FileMark | 调度层(从 WriteResult 获取) |
+| `filemark_end` | u32 | 结束 FileMark | 调度层 |
+| `checksum` | Option\ | 整包 SHA256 | 调度层(可选) |
+| `status` | ArchiveBundleStatus | 状态 | 调度层 |
+| `created_at` | DateTime\ | 创建时间 | 调度层 |
+| `completed_at` | Option\\> | 完成时间 | 调度层 |
+
+```rust
+pub struct BundleEntry {
+ pub bucket: String,
+ pub key: String,
+ pub version_id: Option,
+ pub size: u64,
+ pub offset_in_bundle: u64,
+ pub tape_block_offset: u64,
+ pub checksum: String,
+}
+
+pub enum ArchiveBundleStatus {
+ Pending, Writing, Completed, Failed,
+}
+```
+
+### 3.4 TapeInfo(磁带信息)
+
+与 06-tape-layer §4.6 定义一致。由调度层写入元数据。
+
+```rust
+pub struct TapeInfo {
+ pub id: String,
+ pub barcode: Option,
+ pub format: String,
+ pub status: TapeStatus,
+ pub location: Option,
+ pub capacity_bytes: u64,
+ pub used_bytes: u64,
+ pub remaining_bytes: u64,
+ pub archive_bundles: Vec,
+ pub last_verified_at: Option>,
+ pub error_count: u32,
+ pub registered_at: DateTime,
+}
+```
+
+| 字段 | 类型 | 含义 | 写入方 |
+|------|------|------|--------|
+| `id` | String | 磁带唯一标识 | 调度层(注册时) |
+| `barcode` | Option\ | 条码 | 调度层 |
+| `format` | String | "LTO-9"、"LTO-10" | 调度层 |
+| `status` | TapeStatus | Online/Offline/Error/Retired | 调度层 |
+| `location` | Option\ | 位置描述 | 调度层 |
+| `capacity_bytes` | u64 | 总容量 | 调度层 |
+| `used_bytes` | u64 | 已使用 | 调度层(每次写入后更新) |
+| `remaining_bytes` | u64 | 剩余 | 调度层 |
+| `archive_bundles` | Vec\ | 已写入的归档包列表 | 调度层 |
+| `last_verified_at` | Option\\> | 最近校验时间 | 调度层 |
+| `error_count` | u32 | 累计错误次数 | 调度层 |
+| `registered_at` | DateTime\ | 注册时间 | 调度层 |
+
+```rust
+pub enum TapeStatus {
+ Online, Offline, Error, Retired, Unknown,
+}
+```
+
+### 3.5 RecallTask(取回任务)
+
+与 05-scheduler-layer §8.7 定义一致。
+
+```rust
+pub struct RecallTask {
+ pub id: Uuid,
+ pub bucket: String,
+ pub key: String,
+ pub version_id: Option,
+ pub archive_id: Uuid,
+ pub tape_id: String,
+ pub tape_set: Vec,
+ pub tape_block_offset: u64,
+ pub object_size: u64,
+ pub checksum: String,
+ pub tier: RestoreTier,
+ pub days: u32,
+ pub expire_at: DateTime,
+ pub status: RestoreStatus,
+ pub drive_id: Option,
+ pub retry_count: u32,
+ pub created_at: DateTime,
+ pub started_at: Option>,
+ pub completed_at: Option>,
+ pub error: Option,
+}
+```
+
+### 3.6 ArchiveTask(归档任务)
+
+与 05-scheduler-layer §8.6 定义一致。
+
+```rust
+pub struct ArchiveTask {
+ pub id: Uuid,
+ pub bundle_id: Uuid,
+ pub tape_id: String,
+ pub drive_id: Option,
+ pub object_count: u32,
+ pub total_size: u64,
+ pub bytes_written: u64,
+ pub status: ArchiveTaskStatus,
+ pub retry_count: u32,
+ pub created_at: DateTime,
+ pub started_at: Option>,
+ pub completed_at: Option>,
+ pub error: Option,
+}
+
+pub enum ArchiveTaskStatus {
+ Pending, InProgress, Completed, Failed,
+}
+```
+
+### 3.7 BucketInfo(桶信息)
+
+```rust
+pub struct BucketInfo {
+ pub name: String,
+ pub created_at: DateTime,
+ pub owner: Option,
+ pub versioning_enabled: bool,
+ pub object_count: u64,
+ pub total_size: u64,
+}
+```
+
+| 字段 | 类型 | 含义 | 写入方 |
+|------|------|------|--------|
+| `name` | String | 桶名 | 接入层 CreateBucket |
+| `created_at` | DateTime\ | 创建时间 | 接入层 |
+| `owner` | Option\ | 拥有者 | 接入层 |
+| `versioning_enabled` | bool | 是否启用多版本 | 接入层 |
+| `object_count` | u64 | 对象数 | 写入时增减 |
+| `total_size` | u64 | 总大小 | 写入时增减 |
+
+### 3.8 ListObjectsResult
+
+```rust
+pub struct ListObjectsResult {
+ pub objects: Vec,
+ pub next_marker: Option,
+ pub is_truncated: bool,
+}
+```
+
+| 字段 | 类型 | 含义 |
+|------|------|------|
+| `objects` | Vec\ | 当前页对象列表 |
+| `next_marker` | Option\ | 分页游标(用于下次请求的 marker) |
+| `is_truncated` | bool | 是否还有更多结果 |
+
+### 3.10 集群元数据与部署模型
+
+#### 3.10.1 物理部署模型
+
+ColdStore 由五类节点组成。**Scheduler Worker 是唯一业务中枢**:Gateway 仅连 Scheduler,
+Console 仅连 Metadata,三类 Worker 向 Metadata 注册并上报心跳。
+
+```
+┌────────────────┐ ┌────────────────┐
+│ Gateway (N台) │ │ Console (1台) │
+│ S3 HTTP 前端 │ │ 管控面 Web UI │
+│ 无状态 │ │ 无状态 │
+└──────┬─────────┘ └──────┬─────────┘
+ │ 配置: scheduler_addrs │ 配置: metadata_addrs
+ ▼ ▼
+ ┌─────────────────────────────┐ ┌──────────────────────────┐
+ │ 同一物理节点 │ │ Metadata 节点 (3/5 台) │
+ │ ┌─────────────────────────┐ │ │ Raft 共识 + RocksDB │
+ │ │ Scheduler Worker │─┼──►│ 元数据 + Worker 注册中心 │
+ │ │ 业务中枢:调度编排 │ │ └─────────────────────────┘
+ │ ├─────────────────────────┤ │ ▲
+ │ │ Cache Worker │ │ │ 心跳
+ │ │ SPDK Blobstore 缓存 │ │ ┌──────────┴──────────────┐
+ │ └─────────────────────────┘ │ │ Tape Worker (独立物理节点) │
+ └─────────────────────────────┘ │ 磁带驱动 /dev/nst* │
+ │ 带库 /dev/sg* │
+ └────────────────────────────┘
+```
+
+| 节点类型 | 职责 | 数量 | 连接对象 |
+|----------|------|------|----------|
+| **Metadata** | Raft 共识、元数据存储、Worker 注册中心 | 3/5 | Scheduler (读写)、三类 Worker (心跳)、Console (管控) |
+| **Scheduler Worker** | **业务中枢**:调度编排、对接全部层(与 Cache 同机) | 1~N | Metadata, Cache Worker, Tape Worker, Gateway |
+| **Cache Worker** | SPDK NVMe 数据缓存(与 Scheduler 同机) | 1~N | Scheduler Worker (gRPC)、Metadata (心跳) |
+| **Tape Worker** | 磁带驱动管理、数据读写(独立物理节点) | 1~N | Scheduler Worker (gRPC)、Metadata (心跳) |
+| **Gateway** | S3 HTTP 接入 + 协议适配(无状态) | N | **仅 Scheduler Worker** |
+| **Console** | 管控面 Web UI + Admin API(无状态) | 1 | **仅 Metadata** |
+
+**关键设计决策**:
+
+- **Gateway 不直连 Metadata**:Gateway 是纯 S3 协议前端,所有业务请求都发往 Scheduler Worker。
+ Gateway 配置文件中只需写 Scheduler Worker 地址。
+- **Scheduler Worker 是唯一业务中枢**:持有 MetadataClient,编排 Cache Worker 和 Tape Worker,
+ 接受 Gateway 的全部请求,是唯一的元数据读写业务入口。
+- **Scheduler ↔ Cache 使用 gRPC**:虽然同机部署,仍通过 gRPC 通信,保持架构一致性,
+ 未来可独立扩展。
+- **Console 仅连 Metadata**:管控面直接读写元数据集群,管理 Worker 增删。
+- **Tape Worker 独立部署**:磁带机为独立物理节点,通过 gRPC 接收 Scheduler 的读写指令。
+
+#### 3.10.2 节点间通信拓扑
+
+```
+Gateway Console
+ │ 配置: scheduler_addrs │ 配置: metadata_addrs
+ │ │
+ └──(gRPC)──► Scheduler Worker └──(gRPC)──► Metadata
+ │ ▲
+ ├──(gRPC)──► Metadata ───────────┘ (元数据读写)
+ ├──(gRPC)──► Cache Worker (同机,gRPC)
+ └──(gRPC)──► Tape Worker (远程,gRPC)
+ │
+ 三类 Worker ──(gRPC)──► Metadata (心跳注册)
+```
+
+| 通信路径 | 协议 | 场景 |
+|----------|------|------|
+| Gateway → Scheduler Worker | gRPC | **全部** S3 请求(PUT/GET/HEAD/DELETE/Restore) |
+| Scheduler → Metadata | gRPC | 元数据读写(PutObject、扫描 ColdPending、状态更新等) |
+| Scheduler → Cache Worker | gRPC(同机) | 缓存写入/读取(PutObject 暂存、GET 解冻数据) |
+| Scheduler → Tape Worker | gRPC(远程) | 磁带写入/读取指令下发 |
+| Console → Metadata | gRPC | 集群管理、Worker 增删、元数据查询 |
+| 三类 Worker → Metadata | gRPC | 心跳上报(资源状态) |
+| Metadata ↔ Metadata | Raft RPC | 共识协议 |
+
+> Gateway **不连接** Metadata,也**不连接** Cache Worker 和 Tape Worker。
+> 所有业务流量都经过 Scheduler Worker 中转。
+
+#### 3.10.3 集群全局信息
+
+```rust
+pub struct ClusterInfo {
+ pub cluster_id: String,
+ pub metadata_nodes: Vec,
+ pub scheduler_workers: Vec,
+ pub cache_workers: Vec,
+ pub tape_workers: Vec,
+ pub leader_id: Option,
+ pub term: u64,
+ pub committed_index: u64,
+}
+```
+
+| 字段 | 类型 | 含义 |
+|------|------|------|
+| `cluster_id` | String | 集群唯一标识 |
+| `metadata_nodes` | Vec\ | Raft 元数据节点列表 |
+| `scheduler_workers` | Vec\ | 调度 Worker 列表 |
+| `cache_workers` | Vec\ | 缓存 Worker 列表 |
+| `tape_workers` | Vec\ | 磁带 Worker 列表 |
+| `leader_id` | Option\ | 当前 Raft Leader 节点 ID |
+| `term` | u64 | 当前 Raft 任期 |
+| `committed_index` | u64 | 已提交日志索引 |
+
+> Gateway / Console 不出现在 `ClusterInfo` 中。
+> Gateway 通过配置直连 Scheduler Worker;Console 通过配置直连 Metadata。
+
+#### 3.10.4 公共类型
+
+```rust
+pub enum NodeStatus {
+ Online, // 正常服务
+ Offline, // 失联(心跳超时)
+ Draining, // 排空中(不再分配新任务,等待现有任务完成)
+ Maintenance, // 维护模式(管理员手动设置)
+}
+
+pub enum RaftRole {
+ Leader,
+ Follower,
+ Learner,
+}
+
+pub enum WorkerType {
+ Scheduler,
+ Cache,
+ Tape,
+}
+```
+
+#### 3.10.5 MetadataNodeInfo(元数据节点)
+
+```rust
+pub struct MetadataNodeInfo {
+ pub node_id: u64,
+ pub addr: String, // Raft RPC + gRPC 服务地址
+ pub raft_role: RaftRole,
+ pub last_heartbeat: Option>,
+ pub status: NodeStatus,
+}
+```
+
+| 字段 | 类型 | 含义 |
+|------|------|------|
+| `node_id` | u64 | Raft 节点 ID |
+| `addr` | String | 服务地址(`host:port`),供 Console/Worker 连接 |
+| `raft_role` | RaftRole | 当前 Raft 角色 |
+| `last_heartbeat` | Option\\> | Raft 内部心跳时间 |
+| `status` | NodeStatus | 节点状态 |
+
+#### 3.10.6 SchedulerWorkerInfo(调度 Worker)
+
+调度 Worker 是 ColdStore 的**业务中枢**,接受 Gateway 全部 S3 请求,编排 Cache Worker 和 Tape Worker。
+物理上与 Cache Worker **同机部署**,通过 gRPC 通信(保持架构一致性)。
+
+```rust
+pub struct SchedulerWorkerInfo {
+ pub node_id: u64,
+ pub addr: String, // gRPC 服务地址
+ pub status: NodeStatus,
+ pub last_heartbeat: Option>,
+
+ // ── 调度状态 ──
+ pub is_active: bool, // 主备模式下是否为 active scheduler
+ pub pending_archive_tasks: u64, // 待归档任务数
+ pub pending_recall_tasks: u64, // 待取回任务数
+ pub active_jobs: u64, // 正在执行的作业数
+
+ // ── 关联的 Cache Worker ──
+ pub paired_cache_worker_id: u64, // 同机部署的 Cache Worker ID
+}
+```
+
+| 字段 | 类型 | 含义 | 心跳更新 |
+|------|------|------|:---:|
+| `node_id` | u64 | 全局唯一 ID | 否 |
+| `addr` | String | gRPC 地址 | 否 |
+| `status` | NodeStatus | 节点状态 | 是 |
+| `is_active` | bool | 是否为活跃调度器(多 Scheduler 时仅一个 active) | 是 |
+| `pending_archive_tasks` | u64 | 待归档任务数 | 是 |
+| `pending_recall_tasks` | u64 | 待取回任务数 | 是 |
+| `active_jobs` | u64 | 正在执行作业数 | 是 |
+| `paired_cache_worker_id` | u64 | 同机 Cache Worker 的 ID | 否(注册时固定) |
+
+#### 3.10.7 CacheWorkerInfo(缓存 Worker)
+
+缓存 Worker 运行 SPDK Blobstore,提供高性能 NVMe 数据缓存。
+物理上与 Scheduler Worker **同机部署**。
+
+```rust
+pub struct CacheWorkerInfo {
+ pub node_id: u64,
+ pub addr: String, // gRPC 服务地址(供远程 GET 读取解冻数据)
+ pub status: NodeStatus,
+ pub last_heartbeat: Option>,
+
+ // ── 缓存资源 ──
+ pub bdev_name: String, // SPDK bdev 名称(如 "NVMe0n1")
+ pub total_capacity: u64, // 总容量 (bytes)
+ pub used_capacity: u64, // 已用容量 (bytes)
+ pub blob_count: u64, // 当前缓存对象数
+ pub io_unit_size: u32, // SPDK io_unit 大小 (bytes)
+}
+```
+
+| 字段 | 类型 | 含义 | 心跳更新 |
+|------|------|------|:---:|
+| `node_id` | u64 | 全局唯一 ID | 否 |
+| `addr` | String | gRPC 地址(Gateway GET 时连接此地址) | 否 |
+| `status` | NodeStatus | 节点状态 | 是 |
+| `bdev_name` | String | SPDK bdev 名称 | 否(注册时固定) |
+| `total_capacity` | u64 | NVMe 总容量 | 否 |
+| `used_capacity` | u64 | 已用容量 | 是 |
+| `blob_count` | u64 | 缓存对象数 | 是 |
+| `io_unit_size` | u32 | SPDK io_unit 大小 | 否 |
+
+#### 3.10.8 TapeWorkerInfo(磁带 Worker)
+
+磁带 Worker 独立部署于挂载磁带驱动和带库的物理节点上,
+通过 gRPC 接收调度器的读写指令。
+
+```rust
+pub struct TapeWorkerInfo {
+ pub node_id: u64,
+ pub addr: String, // gRPC 服务地址
+ pub status: NodeStatus,
+ pub last_heartbeat: Option>,
+
+ // ── 磁带驱动 ──
+ pub drives: Vec,
+
+ // ── 带库(可选,无带库时为 None) ──
+ pub library: Option,
+}
+
+/// 磁带驱动端点
+pub struct DriveEndpoint {
+ pub drive_id: String, // 驱动唯一标识
+ pub device_path: String, // 设备路径 /dev/nst0
+ pub drive_type: String, // 驱动类型 "LTO-9" / "LTO-10"
+ pub status: DriveStatus,
+ pub current_tape: Option, // 当前装载的磁带 ID
+}
+
+pub enum DriveStatus {
+ Idle, // 空闲可分配
+ InUse, // 正在执行读写任务
+ Loading, // 正在装载磁带
+ Unloading, // 正在卸载磁带
+ Error, // 故障
+ Offline, // 离线维护
+}
+
+/// 带库端点
+pub struct LibraryEndpoint {
+ pub device_path: String, // /dev/sg5
+ pub slot_count: u32, // 存储槽位数
+ pub import_export_count: u32, // 进出槽位数(邮箱)
+ pub drive_count: u32, // 带库中驱动数量
+}
+```
+
+| 字段 | 类型 | 含义 | 心跳更新 |
+|------|------|------|:---:|
+| `node_id` | u64 | 全局唯一 ID | 否 |
+| `addr` | String | gRPC 地址(调度器连接此地址下发指令) | 否 |
+| `status` | NodeStatus | 节点状态 | 是 |
+| `drives[].status` | DriveStatus | 每个驱动的当前状态 | 是 |
+| `drives[].current_tape` | Option\ | 驱动中当前磁带 ID | 是 |
+| `drives[].drive_type` | String | 驱动型号 | 否 |
+| `library.slot_count` | u32 | 槽位总数 | 否 |
+
+#### 3.10.9 节点注册、心跳与管理
+
+```
+ Console (添加/下线操作)
+ │
+ ▼
+ ┌──────────────┐ RegisterWorker ┌──────────────────────┐
+ │ Scheduler │ ─────────────────► │ │
+ │ Worker │ Heartbeat (5s) │ Metadata Cluster │
+ ├──────────────┤ ─────────────────► │ (Raft + RocksDB) │
+ │ Cache Worker │ Heartbeat (5s) │ │
+ ├──────────────┤ ─────────────────► │ cf_scheduler_workers │
+ │ Tape Worker │ Heartbeat (5s) │ cf_cache_workers │
+ └──────────────┘ │ cf_tape_workers │
+ └──────────────────────┘
+```
+
+**注册流程**:
+
+1. 管理员通过 Console 添加 Worker(指定类型、地址等基本信息)
+2. Console 调用 Metadata 的 `RegisterXxxWorker` 写入集群信息
+3. Worker 进程启动后连接 Metadata,确认自身已注册,开始心跳上报
+
+**心跳机制**:
+
+- **频率**:每 5s 一次
+- **内容**:各 Worker 上报自身实时状态(队列深度、缓存容量、驱动状态等)
+- **存储**:`last_heartbeat` 由 Metadata Leader 本地内存更新,**不写 Raft 日志**
+- **状态变更走 Raft**:仅当状态发生实质变更时(Online ↔ Offline、驱动状态切换)
+ 才通过 Raft Propose 持久化
+
+**失联检测**:
+
+- Metadata Leader 每 15s 扫描,连续 3 次未收到心跳(>15s)的 Worker 标记为 `Offline`
+- Offline 的 Tape Worker 上的驱动不再被调度器分配任务
+
+**优雅下线**:
+
+1. 管理员通过 Console 发起 Drain(排空)
+2. Worker 状态变为 `Draining`,不再接受新任务
+3. 等待正在执行的任务完成
+4. 管理员确认后通过 Console 执行 Deregister
+
+#### 3.10.10 配置与服务发现
+
+各节点的配置各不相同,遵循"最小知识"原则:
+
+```yaml
+# Gateway 配置 — 仅需 Scheduler Worker 地址
+gateway:
+ scheduler_addrs:
+ - "10.0.2.1:22001"
+ - "10.0.2.2:22001"
+
+# Console 配置 — 仅需 Metadata 地址
+console:
+ metadata_addrs:
+ - "10.0.1.1:21001"
+ - "10.0.1.2:21001"
+ - "10.0.1.3:21001"
+
+# Scheduler Worker 配置 — 需要 Metadata 地址(Cache/Tape 通过 Metadata 发现)
+scheduler_worker:
+ metadata_addrs:
+ - "10.0.1.1:21001"
+ - "10.0.1.2:21001"
+ - "10.0.1.3:21001"
+
+# Cache Worker / Tape Worker 配置 — 仅需 Metadata 地址(用于心跳注册)
+cache_worker:
+ metadata_addrs:
+ - "10.0.1.1:21001"
+ - "10.0.1.2:21001"
+ - "10.0.1.3:21001"
+```
+
+**Scheduler Worker 的服务发现**:Scheduler 启动时通过 Metadata 查询在线的 Cache Worker 和 Tape Worker:
+
+```rust
+async fn discover_workers(metadata: &MetadataClient) -> WorkerTopology {
+ let caches = metadata.list_online_cache_workers().await?;
+ let tapes = metadata.list_online_tape_workers().await?;
+ WorkerTopology { caches, tapes }
+}
+```
+
+| 场景 | 发现路径 |
+|------|----------|
+| Gateway 全部请求 | 配置的 Scheduler 地址 → gRPC 发送请求 |
+| Scheduler 操作缓存 | 查 Metadata → 获取 Cache Worker 地址 → gRPC 读写缓存 |
+| Scheduler 操作磁带 | 查 Metadata → 获取 Tape Worker 地址 → gRPC 下发磁带指令 |
+| Console 管理集群 | 配置的 Metadata 地址 → gRPC 管控操作 |
+
+---
+
+## 4. Column Family 设计
+
+| CF 名称 | Key 格式 | Value 类型 | 说明 |
+|---------|----------|-----------|------|
+| `cf_objects` | `obj:{bucket}:{key}` | ObjectMetadata | 对象元数据(当前版本) |
+| `cf_object_versions` | `objv:{bucket}:{key}:{version_id}` | ObjectMetadata | 多版本对象 |
+| `cf_buckets` | `bkt:{bucket}` | BucketInfo | 桶信息 |
+| `cf_bundles` | `bundle:{uuid}` | ArchiveBundle | 归档包(含 entries) |
+| `cf_tapes` | `tape:{tape_id}` | TapeInfo | 磁带信息 |
+| `cf_recall_tasks` | `recall:{uuid}` | RecallTask | 取回任务 |
+| `cf_archive_tasks` | `archive:{uuid}` | ArchiveTask | 归档任务 |
+| `cf_idx_bundle_objects` | `ibo:{archive_id}:{bucket}:{key}` | 空 | 归档包→对象 反查索引 |
+| `cf_idx_tape_bundles` | `itb:{tape_id}:{bundle_id}` | 空 | 磁带→归档包 反查索引 |
+| `cf_idx_pending` | `pend:{created_at}:{bucket}:{key}` | 空 | ColdPending 对象扫描索引(按时间排序) |
+| `cf_idx_recall_by_tape` | `rbt:{tape_id}:{recall_id}` | 空 | 按磁带查取回任务(合并用) |
+| `cf_scheduler_workers` | `sw:{node_id}` | SchedulerWorkerInfo | 调度 Worker 注册信息 |
+| `cf_cache_workers` | `cw:{node_id}` | CacheWorkerInfo | 缓存 Worker 注册信息 |
+| `cf_tape_workers` | `tw:{node_id}` | TapeWorkerInfo | 磁带 Worker 注册信息 |
+
+---
+
+## 5. MetadataService trait:面向各层的 API
+
+### 5.1 设计原则
+
+MetadataService 拆分为多个子 trait,按消费方组织:
+
+```
+ ┌──────────────────────────────┐
+ │ MetadataService │
+ │ impl ObjectApi │
+ │ impl BucketApi │
+ │ impl ArchiveApi │
+ │ impl RecallApi │
+ │ impl TapeApi │
+ │ impl ClusterApi │
+ └──────────────────────────────┘
+ │ │
+ ┌─────────────┘ └───────────────┐
+ ▼ ▼
+ 接入层/协议层 调度层
+ ObjectApi (读+写) ArchiveApi (读+写)
+ BucketApi (读+写) RecallApi (读+写)
+ RecallApi (只写:创建) TapeApi (读+写)
+ ObjectApi (读+写)
+```
+
+### 5.2 ObjectApi(对象元数据)
+
+```rust
+#[async_trait]
+pub trait ObjectApi: Send + Sync {
+ // ── 接入层使用 ──
+ async fn put_object(&self, meta: ObjectMetadata) -> Result<()>;
+ async fn get_object(&self, bucket: &str, key: &str) -> Result