Skip to content

Commit e287a6e

Browse files
committed
[data_storage] separate TairMempool SSD storage type
1 parent c07d48e commit e287a6e

38 files changed

Lines changed: 508 additions & 32 deletions

docs/BREAK_CHANGE.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,62 @@
22

33
本文档记录需要调用方、部署配置或持久化状态协同升级的不兼容变更。升级前必须按对应条目的前置条件处理,不能仅替换 KVCM 二进制。
44

5+
## TairMempool SSD 独立 Storage Type
6+
7+
Introduced by: PR #293
8+
9+
TairMempool DRAM 继续使用 `ST_TAIRMEMPOOL`/`pace`,LocalSSD 新增
10+
`ST_TAIRMEMPOOL_SSD`/`pace_ssd`。两者仍共享 `pace://` 数据面协议,但 KVCM 从此分别统计
11+
quota 和类型水位,使 DRAM 高水位能够触发到 SSD 的自动迁移。
12+
13+
### 不兼容内容
14+
15+
- Proto/领域枚举新增 `ST_TAIRMEMPOOL_SSD = 9`,配置 JSON 新增持久化类型字符串
16+
`"pace_ssd"`,写入策略新增 `CPS_ALWAYS_TAIR_MEMPOOL_SSD = 9`
17+
`CPS_PREFER_TAIR_MEMPOOL_SSD = 10`
18+
- 老 Worker Client 不认识 `pace_ssd`。Manager 把新 Storage 配置下发给老 Worker 后,老
19+
Client 会把它解析为 UNKNOWN,并可能以 `ER_INVALID_SDKBACKEND_CONFIG` 结束整个
20+
TransferClient 初始化,而不只是跳过 SSD backend。
21+
- 老 KVCM Server 不认识 Registry 中的 `"type":"pace_ssd"`。直接回滚旧二进制会导致该
22+
Storage backend 恢复失败,Registry recovery 无法完成并持续重试。
23+
- Storage 类型属于持久化 CacheLocation 和用量账本的一部分,不支持同名 Storage 从
24+
`pace` 原地改成 `pace_ssd`。服务端现在拒绝涉及 TairMempool DRAM/SSD 的原地类型变更;
25+
修改 timeout、domain 等同类型参数仍可使用 `update_storage`
26+
- 开源占位 backend 已按 `StorageConfig.type()` 返回类型。实际部署使用的 PACE backend 也
27+
必须返回配置中的类型,并在构造 URI 时固定使用 `pace` scheme;若仍硬编码
28+
`ST_TAIRMEMPOOL` 或使用 `ToString(GetType())` 构造 scheme,独立计量会静默失效或生成
29+
不兼容的 `pace_ssd://` URI。
30+
31+
### 启用前置条件和顺序
32+
33+
1. 先升级所有 Worker Client,使其能够解析类型 9、创建对应 TairMempool SDK,并继续识别
34+
`pace://` URI。在确认集群不存在老 Client 后再进入下一步。
35+
2. 升级全部 KVCM Server 和实际 PACE backend,确认 backend 的 `GetType()` 等于
36+
`StorageConfig.type()`,DRAM/SSD 生成的 URI scheme 均为 `pace`
37+
3. 为 SSD **新建不同名称**`pace_ssd` Storage。不要对已有 `pace` Storage 执行类型更新。
38+
`kvcm_ops``pace` 只接受 media type 0/2;`pace_ssd` 子命令固定使用 media type 5,
39+
add/update 请求都会显式携带 StorageType,避免修改 timeout 时静默降回 DRAM 类型。
40+
4. 在每个启用迁移的 Instance Group 中,为 `pace``pace_ssd` 分别配置正数
41+
`quota_config.capacity`。类型水位只遍历显式 quota;缺少源类型 quota 时迁移不会触发,
42+
缺少 SSD quota 时 SSD 没有独立水位和类型容量保护。
43+
5. 迁移规则继续以 DRAM Storage 为 `source_storage_name`、新 SSD Storage 为
44+
`target_storage_name`。SSD 只作迁移目标时无需加入 `storage_candidates`;直接写 SSD 时
45+
才加入候选并选用 SSD 专用 CachePreferStrategy。
46+
6. 用空 Instance Group 或测试 Instance 验证一次 DRAM 写入与 DRAM→SSD 迁移:新 Location
47+
分别记录 type 3/type 9,DRAM 用量下降、SSD 用量上升,且 Worker 能读写 `pace://` URI。
48+
49+
旧的 `ST_TAIRMEMPOOL + media_type=5` 配置仍可读取并继续按旧类型计量,但不能通过原地改类型
50+
获得独立水位。迁移方式是新建 `pace_ssd` Storage,将新迁移流量切到它;旧 Storage 必须保留
51+
到历史 Location 排空,避免 GC/读取找不到原 backend。
52+
53+
### 回滚说明
54+
55+
创建任何 `pace_ssd` Registry 配置前,可以直接回滚,因为新类型尚未进入持久化状态。创建后
56+
不能直接换回不认识类型 9 的旧 Server/Client。回滚前必须停止新写入和迁移,删除或排空所有
57+
type 9 CacheLocation,确认其物理数据已清理,再删除 `pace_ssd` Storage 与相关 quota/迁移配置;
58+
之后才能回滚二进制。仅把新 Storage 改名或原地改回 `pace` 不能修复已有 Location 的类型和
59+
用量,且服务端会拒绝这种类型更新。
60+
561
## Vineyard 事件上报升级为 EventReportBackend
662

763
Introduced by: PR #249

docs/configuration.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ timeout,因此不会使用配置数组顺序作为隐式优先级。
177177
```TEXT
178178
{
179179
"storage_config": {
180-
"type": "file", # 后端类型,可选值file,pace,mooncake,hf3fs,vcns_hf3fs
180+
"type": "file", # 后端类型,可选值file,pace,pace_ssd,mooncake,hf3fs,vcns_hf3fs
181181
"global_unique_name": "nfs_01", # storage backend的名字,需要全局唯一
182182
"storage_spec": { # storage spec 需根据不同backend类型相应配置,TODO:具体每个type的spec配置文档
183183
"root_path": "/tmp/nfs/",
@@ -205,6 +205,10 @@ timeout,因此不会使用配置数组顺序作为隐式优先级。
205205
{
206206
"storage_type": "pace",
207207
"capacity": 10000000000
208+
},
209+
{
210+
"storage_type": "pace_ssd",
211+
"capacity": 10000000000
208212
}
209213
]
210214
},
@@ -227,6 +231,8 @@ timeout,因此不会使用配置数组顺序作为隐式优先级。
227231
# CPS_PREFER_TAIR_MEMPOOL = 6,
228232
# CPS_ALWAYS_VCNS_3FS = 7,
229233
# CPS_PREFER_VCNS_3FS = 8,
234+
# CPS_ALWAYS_TAIR_MEMPOOL_SSD = 9,
235+
# CPS_PREFER_TAIR_MEMPOOL_SSD = 10,
230236
# };
231237
"cache_prefer_strategy": 2,
232238
"meta_indexer_config": {
@@ -254,6 +260,28 @@ timeout,因此不会使用配置数组顺序作为隐式优先级。
254260
}
255261
```
256262

263+
TairMempool DRAM 使用 `pace`(proto `ST_TAIRMEMPOOL`),LocalSSD 使用
264+
`pace_ssd`(proto `ST_TAIRMEMPOOL_SSD`,同时要求 `media_type=5`)。两类 storage
265+
仍使用 `pace://` 数据面 URI,但 quota、类型水位和迁移触发用量分别统计。旧的
266+
`ST_TAIRMEMPOOL + media_type=5` 配置仍可读取,迁移到新类型后才能获得独立 SSD 水位。
267+
268+
启用独立 SSD 类型时还需遵守以下配置约束:
269+
270+
- 使用 `kvcm_ops add_storage ... pace_ssd` 创建一个新的、全局唯一的 Storage;不要把已有
271+
`pace` Storage 原地更新为 `pace_ssd`。Storage 类型会写入 CacheLocation 和用量账本,
272+
服务端会拒绝同名 Storage 的类型变更。
273+
- Instance Group 的 `quota.quota_config` 必须同时为 `pace``pace_ssd` 配置正容量。
274+
类型水位只遍历这里显式出现的类型;缺少 `pace_ssd` 时不会形成 SSD 类型水位和容量上限,
275+
缺少迁移源 `pace` 时迁移规则不会触发。
276+
- SSD 仅作为迁移目标时,不要求放入 `storage_candidates`;迁移规则按
277+
`target_storage_name` 精确选择它。若需要把普通新写入直接落到 SSD,则必须把 SSD Storage
278+
放入 `storage_candidates`,并使用 `CPS_ALWAYS_TAIR_MEMPOOL_SSD`
279+
`CPS_PREFER_TAIR_MEMPOOL_SSD`
280+
- `pace``pace_ssd` 的数据面 URI scheme 都是 `pace://``pace_ssd` 只用于配置、计量
281+
和选择,不能生成 `pace_ssd://` URI。
282+
283+
详细升级顺序和回滚限制见 [Breaking Changes](BREAK_CHANGE.md#tairmempool-ssd-独立-storage-type)
284+
257285
## Logger Config
258286

259287
```TEXT

kv_cache_manager/client/src/internal/config/sdk_config.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ std::string MooncakeSdkConfig::ToString() const {
103103
return oss.str();
104104
}
105105

106-
TairMempoolSdkConfig::TairMempoolSdkConfig() { set_type(DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL); }
106+
TairMempoolSdkConfig::TairMempoolSdkConfig(DataStorageType type) { set_type(type); }
107107

108108
bool TairMempoolSdkConfig::FromRapidValue(const rapidjson::Value &rapid_value) {
109109
return SdkBackendConfig::FromRapidValue(rapid_value);
@@ -146,7 +146,8 @@ bool SdkWrapperConfig::FromRapidValue(const rapidjson::Value &rapid_value) {
146146
sdk_backend_config = std::make_shared<MooncakeSdkConfig>();
147147
break;
148148
case DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL:
149-
sdk_backend_config = std::make_shared<TairMempoolSdkConfig>();
149+
case DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL_SSD:
150+
sdk_backend_config = std::make_shared<TairMempoolSdkConfig>(type);
150151
break;
151152
case DataStorageType::DATA_STORAGE_TYPE_NFS:
152153
sdk_backend_config = std::make_shared<NfsSdkConfig>();

kv_cache_manager/client/src/internal/config/sdk_config.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ class MooncakeSdkConfig : public SdkBackendConfig {
150150

151151
class TairMempoolSdkConfig : public SdkBackendConfig {
152152
public:
153-
TairMempoolSdkConfig();
153+
explicit TairMempoolSdkConfig(DataStorageType type = DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL);
154154
bool FromRapidValue(const rapidjson::Value &rapid_value) override;
155155
void ToRapidWriter(rapidjson::Writer<rapidjson::StringBuffer> &writer) const noexcept override;
156156

@@ -205,6 +205,8 @@ class SdkWrapperConfig : public Jsonizable {
205205
{DataStorageType::DATA_STORAGE_TYPE_HF3FS, std::make_shared<Hf3fsSdkConfig>()},
206206
{DataStorageType::DATA_STORAGE_TYPE_VCNS_HF3FS, std::make_shared<Hf3fsSdkConfig>()},
207207
{DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL, std::make_shared<TairMempoolSdkConfig>()},
208+
{DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL_SSD,
209+
std::make_shared<TairMempoolSdkConfig>(DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL_SSD)},
208210
{DataStorageType::DATA_STORAGE_TYPE_NFS, std::make_shared<NfsSdkConfig>()}};
209211
SdkTimeoutConfig timeout_config_;
210212
};

kv_cache_manager/client/src/internal/config/test/client_config_test.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ TEST_F(ClientConfigTest, TestClientConfigTairMemPoolSuccess) {
5454
auto sdk_wrapper_config = client_config.sdk_wrapper_config();
5555
ASSERT_TRUE(sdk_wrapper_config);
5656
auto sdk_backend_configs_map = sdk_wrapper_config->sdk_backend_configs_map();
57-
// 默认就加了3个
58-
ASSERT_EQ(4, sdk_backend_configs_map.size());
57+
ASSERT_EQ(5, sdk_backend_configs_map.size());
5958
auto tair_mempool_config = sdk_backend_configs_map[DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL];
6059
ASSERT_TRUE(tair_mempool_config);
6160
ASSERT_EQ("logs/pace_client.log", tair_mempool_config->sdk_log_file_path());

kv_cache_manager/client/src/internal/config/test/sdk_config_test.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,37 @@ TEST_F(SdkBackendConfigTest, TestSdkWrapperConfigGetSdkConfig) {
107107
auto tair_config = sdk_wrapper_config.GetSdkBackendConfig(DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL);
108108
ASSERT_NE(tair_config, nullptr);
109109

110+
auto tair_ssd_config = sdk_wrapper_config.GetSdkBackendConfig(DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL_SSD);
111+
ASSERT_NE(tair_ssd_config, nullptr);
112+
EXPECT_EQ(DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL_SSD, tair_ssd_config->type());
113+
110114
auto nfs_config = sdk_wrapper_config.GetSdkBackendConfig(DataStorageType::DATA_STORAGE_TYPE_NFS);
111115
ASSERT_NE(nfs_config, nullptr);
112116

113117
auto unknown_config = sdk_wrapper_config.GetSdkBackendConfig(DataStorageType::DATA_STORAGE_TYPE_UNKNOWN);
114118
ASSERT_EQ(unknown_config, nullptr);
115119
}
116120

121+
TEST_F(SdkBackendConfigTest, TestSdkWrapperConfigParsesTairMempoolSsd) {
122+
SdkWrapperConfig sdk_wrapper_config;
123+
ASSERT_TRUE(sdk_wrapper_config.FromJsonString(R"({
124+
"thread_num": 8,
125+
"queue_size": 2000,
126+
"sdk_backend_configs": [{
127+
"type": "pace_ssd",
128+
"sdk_log_file_path": "logs/pace_ssd_client.log",
129+
"sdk_log_level": "INFO",
130+
"spec_byte_sizes_per_block": {}
131+
}],
132+
"timeout_config": {"put_timeout_ms": 15000, "get_timeout_ms": 15000}
133+
})"));
134+
135+
const auto config = sdk_wrapper_config.GetSdkBackendConfig(DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL_SSD);
136+
ASSERT_NE(nullptr, config);
137+
EXPECT_EQ(DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL_SSD, config->type());
138+
EXPECT_EQ("logs/pace_ssd_client.log", config->sdk_log_file_path());
139+
}
140+
117141
TEST_F(SdkBackendConfigTest, TestDuplicatedSdkConfig) {
118142
SdkWrapperConfig sdk_wrapper_config;
119143
std::string file_content = getFileContent("sdk_wrapper_config_duplicated_sdk_config.json");

kv_cache_manager/client/src/internal/sdk/sdk_factory.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ std::shared_ptr<SdkInterface> SdkFactory::CreateSdk(const DataStorageType &type,
4343
#endif
4444
#ifdef ENABLE_TAIR_MEMPOOL
4545
case DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL:
46+
case DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL_SSD:
4647
sdk = std::make_shared<TairMempoolSdk>();
4748
break;
4849
#endif
@@ -69,4 +70,4 @@ std::shared_ptr<SdkInterface> SdkFactory::CreateSdk(const DataStorageType &type,
6970
return sdk;
7071
}
7172

72-
} // namespace kv_cache_manager
73+
} // namespace kv_cache_manager

kv_cache_manager/config/cache_config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ enum class CachePreferStrategy {
3232
CPS_PREFER_TAIR_MEMPOOL = 6,
3333
CPS_ALWAYS_VCNS_3FS = 7,
3434
CPS_PREFER_VCNS_3FS = 8,
35+
CPS_ALWAYS_TAIR_MEMPOOL_SSD = 9,
36+
CPS_PREFER_TAIR_MEMPOOL_SSD = 10,
3537
};
3638

3739
/*

kv_cache_manager/config/registry_manager.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,18 @@ ErrorCode RegistryManager::UpdateStorage(RequestContext *request_context,
186186
std::unique_lock<std::shared_mutex> lock(mutex_);
187187
const auto &trace_id = request_context->request_id();
188188
const auto &global_unique_name = storage_config.global_unique_name();
189+
const auto current_backend = data_storage_manager_->GetDataStorageBackend(global_unique_name);
190+
const auto current_type = current_backend == nullptr ? DataStorageType::DATA_STORAGE_TYPE_UNKNOWN
191+
: current_backend->GetStorageConfig().type();
192+
if (current_backend != nullptr && current_type != storage_config.type() &&
193+
(IsTairMempoolStorageType(current_type) || IsTairMempoolStorageType(storage_config.type()))) {
194+
PREFIX_LOG_S(WARN,
195+
"update storage rejected: TairMempool storage type is immutable, current type: [%d], requested "
196+
"type: [%d]",
197+
static_cast<int>(current_type),
198+
static_cast<int>(storage_config.type()));
199+
return EC_BADARGS;
200+
}
189201
// 重建期间短暂不可用
190202
auto ec = RemoveStorage(request_context, global_unique_name);
191203
RETURN_IF_EC_NOT_OK_WITH_LOG_S(WARN, ec, "update storage failed: remove storage failed");

kv_cache_manager/config/test/instance_group_test.cc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,43 @@ TEST_F(InstanceGroupTest, EventReportStorageSpecProtoRoundTripPreservesSnapshotS
230230
EXPECT_EQ(8765, restored_spec->snapshot_delta_drain_timeout_ms());
231231
}
232232

233+
TEST_F(InstanceGroupTest, TairMempoolSsdStorageProtoRoundTripPreservesTypeAndMedia) {
234+
auto spec = std::make_shared<TairMemPoolStorageSpec>();
235+
spec->set_domain("pace.meta");
236+
spec->set_timeout(5000);
237+
spec->set_service_discovery_url("spectrum://pace-meta");
238+
spec->set_media_type(kTairMemPoolMediaTypeSsd);
239+
StorageConfig original(DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL_SSD, "pace_ssd_1", spec);
240+
241+
proto::admin::StorageConfig proto_config;
242+
ProtoConvert::StorageConfigToProto(original, &proto_config);
243+
ASSERT_TRUE(proto_config.has_tair_mem_pool());
244+
EXPECT_EQ(proto::admin::ST_TAIRMEMPOOL_SSD, proto_config.storage_type());
245+
EXPECT_EQ(kTairMemPoolMediaTypeSsd, proto_config.tair_mem_pool().media_type());
246+
247+
StorageConfig restored;
248+
ProtoConvert::StorageFromProto(&proto_config, restored);
249+
EXPECT_EQ(DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL_SSD, restored.type());
250+
const auto restored_spec = std::dynamic_pointer_cast<TairMemPoolStorageSpec>(restored.storage_spec());
251+
ASSERT_NE(nullptr, restored_spec);
252+
EXPECT_EQ(kTairMemPoolMediaTypeSsd, restored_spec->media_type());
253+
EXPECT_EQ("spectrum://pace-meta", restored_spec->service_discovery_url());
254+
}
255+
256+
TEST_F(InstanceGroupTest, LegacyTairMempoolProtoWithoutStorageTypeRemainsDramType) {
257+
proto::admin::StorageConfig legacy;
258+
legacy.set_global_unique_name("legacy_pace");
259+
auto *spec = legacy.mutable_tair_mem_pool();
260+
spec->set_domain("pace.meta");
261+
spec->set_timeout(5000);
262+
spec->set_media_type(kTairMemPoolMediaTypeSsd);
263+
264+
StorageConfig restored;
265+
ProtoConvert::StorageFromProto(&legacy, restored);
266+
EXPECT_EQ(DataStorageType::DATA_STORAGE_TYPE_TAIR_MEMPOOL, restored.type());
267+
const auto restored_spec = std::dynamic_pointer_cast<TairMemPoolStorageSpec>(restored.storage_spec());
268+
ASSERT_NE(nullptr, restored_spec);
269+
EXPECT_EQ(kTairMemPoolMediaTypeSsd, restored_spec->media_type());
270+
}
271+
233272
} // namespace kv_cache_manager

0 commit comments

Comments
 (0)