Skip to content

Commit dd9ebe7

Browse files
committed
[manager] implement fair eviction across instances
1 parent 9991ecf commit dd9ebe7

15 files changed

Lines changed: 2740 additions & 65 deletions

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- [高可用与选主机制](design/ha_leader_elector.md) - HA 架构、LeaderElector 状态机、CoordinationBackend、Leader 发现
99
- [CacheReclaimer 异步删除与过度逐出优化](design/cache_reclaimer_async_delete.md) - 异步删除生命周期、in-flight credit、反压与无进展退避
1010
- [后台扫描 GC](design/cache_garbage_collector.md) - 基于 authoritative cursor 的后台全量巡检;V1 清理长期 orphan WRITING 和普通 SERVING storage-missing,并提供无副作用读取、精确值条件 CAS 与 HA 生命周期
11+
- [CacheReclaimer 跨 Instance 公平逐出](design/cache_reclaimer_instance_fairness.md) - 按 Instance 用量分配采样与逐出预算,并与异步 credit 协同
1112

1213
### 开发文档
1314
- [开发指南](develop/README.md) - 开发者入门指南和开发环境配置

docs/api/admin_service.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ curl -g -vvv -X POST http://localhost:6492/api/createInstanceGroup \
142142
},
143143
"trigger_period_seconds": 60,
144144
"reclaim_step_size": 1073741824,
145-
"reclaim_step_percentage": 10
145+
"reclaim_step_percentage": 10,
146+
"enable_instance_fairness": true
146147
},
147148
"data_storage_strategy": "CPS_PREFER_3FS",
148149
"meta_indexer_config": {
@@ -193,7 +194,8 @@ curl -g -vvv -X POST http://localhost:6492/api/createInstanceGroup \
193194
"trigger_period_seconds": 60,
194195
"reclaim_step_size": 1073741824,
195196
"reclaim_step_percentage": 10,
196-
"delay_before_delete_ms": 1000
197+
"delay_before_delete_ms": 1000,
198+
"enable_instance_fairness": true
197199
},
198200
"data_storage_strategy": "CPS_PREFER_3FS",
199201
"meta_indexer_config": {
@@ -249,7 +251,8 @@ curl -g -vvv -X POST http://localhost:6492/api/updateInstanceGroup \
249251
"trigger_period_seconds": 60,
250252
"reclaim_step_size": 1073741824,
251253
"reclaim_step_percentage": 10,
252-
"delay_before_delete_ms": 1000
254+
"delay_before_delete_ms": 1000,
255+
"enable_instance_fairness": true
253256
},
254257
"data_storage_strategy": "CPS_PREFER_3FS",
255258
"meta_indexer_config": {

docs/configuration.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,8 @@ timeout,因此不会使用配置数组顺序作为隐式优先级。
243243
"trigger_strategy": {
244244
"used_percentage": 0.8 # 控制数据用量水位,当用量达到或超过quota * percentage时将触发回收(逐出)
245245
},
246-
"delay_before_delete_ms": 1000 # 控制从提交删除请求到实际执行删除动作的间隔,类似于租约概念
246+
"delay_before_delete_ms": 1000, # 控制从提交删除请求到实际执行删除动作的间隔,类似于租约概念
247+
"enable_instance_fairness": true # 是否按各 Instance 当前用量分配本轮采样/逐出预算;缺省为 true,false 完整回退旧的固定 per-instance 行为
247248
},
248249
# cache_prefer_strategy与storage candidates一起控制storage backend选择策略,可选值如下:
249250
# enum class CachePreferStrategy {

docs/design/cache_reclaimer_instance_fairness.md

Lines changed: 855 additions & 0 deletions
Large diffs are not rendered by default.

integration_test/admin_service/admin_interface_cases.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ def make_sample_instance_group(self) -> Dict:
187187
"trigger_period_seconds": 60,
188188
"reclaim_step_size": 512 * 1024 * 1024,
189189
"reclaim_step_percentage": 5,
190+
"enable_instance_fairness": True,
190191
},
191192
"data_storage_strategy": 2, # CPS_PREFER_3FS
192193
"meta_indexer_config": {

kv_cache_manager/config/cache_reclaim_strategy.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ bool CacheReclaimStrategy::FromRapidValue(const rapidjson::Value &rapid_value) {
1212
KVCM_JSON_GET_MACRO(rapid_value, "reclaim_step_size", reclaim_step_size_);
1313
KVCM_JSON_GET_MACRO(rapid_value, "reclaim_step_percentage", reclaim_step_percentage_);
1414
KVCM_JSON_GET_MACRO(rapid_value, "delay_before_delete_ms", delay_before_delete_ms_);
15+
KVCM_JSON_GET_DEFAULT_MACRO(rapid_value, "enable_instance_fairness", enable_instance_fairness_, true);
1516
return true;
1617
}
1718

@@ -23,6 +24,7 @@ void CacheReclaimStrategy::ToRapidWriter(rapidjson::Writer<rapidjson::StringBuff
2324
Put(writer, "reclaim_step_size", reclaim_step_size_);
2425
Put(writer, "reclaim_step_percentage", reclaim_step_percentage_);
2526
Put(writer, "delay_before_delete_ms", delay_before_delete_ms_);
27+
Put(writer, "enable_instance_fairness", enable_instance_fairness_);
2628
}
2729

2830
bool CacheReclaimStrategy::ValidateRequiredFields(std::string &invalid_fields) const {
@@ -37,4 +39,4 @@ bool CacheReclaimStrategy::ValidateRequiredFields(std::string &invalid_fields) c
3739
}
3840
return valid;
3941
}
40-
} // namespace kv_cache_manager
42+
} // namespace kv_cache_manager

kv_cache_manager/config/cache_reclaim_strategy.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@ class CacheReclaimStrategy : public Jsonizable {
3030
int32_t trigger_period_seconds,
3131
int32_t reclaim_step_size,
3232
int32_t reclaim_step_percentage,
33-
int32_t delay_before_delete_ms = 0)
33+
int32_t delay_before_delete_ms = 0,
34+
bool enable_instance_fairness = true)
3435
: storage_unique_name_(storage_unique_name)
3536
, reclaim_policy_(reclaim_policy)
3637
, trigger_strategy_(trigger_strategy)
3738
, trigger_period_seconds_(trigger_period_seconds)
3839
, reclaim_step_size_(reclaim_step_size)
3940
, reclaim_step_percentage_(reclaim_step_percentage)
40-
, delay_before_delete_ms_(delay_before_delete_ms) {}
41+
, delay_before_delete_ms_(delay_before_delete_ms)
42+
, enable_instance_fairness_(enable_instance_fairness) {}
4143

4244
~CacheReclaimStrategy() override;
4345

@@ -52,6 +54,7 @@ class CacheReclaimStrategy : public Jsonizable {
5254
int32_t reclaim_step_size() const { return reclaim_step_size_; }
5355
int32_t reclaim_step_percentage() const { return reclaim_step_percentage_; }
5456
int32_t delay_before_delete_ms() const { return delay_before_delete_ms_; }
57+
bool enable_instance_fairness() const { return enable_instance_fairness_; }
5558

5659
// Setters
5760
void set_storage_unique_name(const std::string &storage_unique_name) { storage_unique_name_ = storage_unique_name; }
@@ -67,6 +70,9 @@ class CacheReclaimStrategy : public Jsonizable {
6770
void set_delay_before_delete_ms(int32_t delay_before_delete_ms) {
6871
delay_before_delete_ms_ = delay_before_delete_ms;
6972
}
73+
void set_enable_instance_fairness(bool enable_instance_fairness) {
74+
enable_instance_fairness_ = enable_instance_fairness;
75+
}
7076

7177
public:
7278
bool FromRapidValue(const rapidjson::Value &rapid_value) override;
@@ -82,6 +88,7 @@ class CacheReclaimStrategy : public Jsonizable {
8288
int32_t reclaim_step_size_;
8389
int32_t reclaim_step_percentage_;
8490
int32_t delay_before_delete_ms_;
91+
bool enable_instance_fairness_{true};
8592
};
8693

87-
} // namespace kv_cache_manager
94+
} // namespace kv_cache_manager

kv_cache_manager/config/test/instance_group_test.cc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,40 @@ TEST_F(InstanceGroupTest, UnknownProtoStorageTypeFailsClosed) {
255255
EXPECT_EQ(DataStorageType::DATA_STORAGE_TYPE_UNKNOWN, location.type());
256256
}
257257

258+
TEST_F(InstanceGroupTest, CacheReclaimFairnessProtoRoundTripPreservesExplicitFalse) {
259+
CacheConfig original;
260+
auto reclaim_strategy = std::make_shared<CacheReclaimStrategy>();
261+
reclaim_strategy->set_enable_instance_fairness(false);
262+
original.set_reclaim_strategy(reclaim_strategy);
263+
264+
proto::admin::CacheConfig proto_config;
265+
ProtoConvert::CacheConfigToProto(original, &proto_config);
266+
ASSERT_TRUE(proto_config.reclaim_strategy().has_enable_instance_fairness());
267+
EXPECT_FALSE(proto_config.reclaim_strategy().enable_instance_fairness().value());
268+
269+
CacheConfig restored;
270+
ProtoConvert::CacheConfigFromProto(&proto_config, restored);
271+
ASSERT_NE(nullptr, restored.reclaim_strategy());
272+
EXPECT_FALSE(restored.reclaim_strategy()->enable_instance_fairness());
273+
}
274+
275+
TEST_F(InstanceGroupTest, CacheReclaimFairnessProtoDefaultsToTrueWhenMissing) {
276+
proto::admin::CacheConfig proto_config;
277+
proto_config.mutable_reclaim_strategy();
278+
279+
CacheConfig restored;
280+
ProtoConvert::CacheConfigFromProto(&proto_config, restored);
281+
ASSERT_NE(nullptr, restored.reclaim_strategy());
282+
EXPECT_TRUE(restored.reclaim_strategy()->enable_instance_fairness());
283+
}
284+
285+
TEST_F(InstanceGroupTest, CacheReclaimFairnessJsonDefaultsToTrueAndAcceptsFalse) {
286+
CacheReclaimStrategy reclaim_strategy;
287+
ASSERT_TRUE(reclaim_strategy.FromJsonString("{}"));
288+
EXPECT_TRUE(reclaim_strategy.enable_instance_fairness());
289+
290+
ASSERT_TRUE(reclaim_strategy.FromJsonString(R"({"enable_instance_fairness": false})"));
291+
EXPECT_FALSE(reclaim_strategy.enable_instance_fairness());
292+
}
293+
258294
} // namespace kv_cache_manager

0 commit comments

Comments
 (0)