Skip to content

[manager] make CacheReclaimer deletion asynchronous and prevent over-eviction#234

Open
shaohuaxi wants to merge 1 commit into
mainfrom
rc/feat/cache_reclaimer-async-delete-v1
Open

[manager] make CacheReclaimer deletion asynchronous and prevent over-eviction#234
shaohuaxi wants to merge 1 commit into
mainfrom
rc/feat/cache_reclaimer-async-delete-v1

Conversation

@shaohuaxi

@shaohuaxi shaohuaxi commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Background

Before this change, CacheReclaimer submitted a Location deletion by synchronously performing metadata Get, CAS, and Sync. These operations may access a remote metadata backend such as Redis, so a slow metadata operation could block the Reclaimer cron thread and delay reclamation work for every Instance Group handled by that loop.

Deletion may also be delayed by delay_before_delete_ms. During that interval, official storage usage and key count have not decreased yet, so subsequent cron iterations continue to observe an exceeded watermark and select more victims. The accumulated submissions can evict substantially more cache data than required. Making admission asynchronous also creates an accepted-before-CAS window in which a Location still appears selectable, so in-flight work must be accounted for and deduplicated immediately after acceptance.

Summary

  • Add a true end-to-end asynchronous Location deletion API while preserving the existing synchronous Submit interface.
  • Move metadata Get/CAS/Sync, delayed physical deletion, and final CAD onto SchedulePlanExecutor workers; delayed waits use the timer queue and do not occupy a worker.
  • Track instance-scoped pending Locations to close the accepted-before-CAS duplicate-submission window.
  • Credit in-flight delete bytes by Instance Group and base storage type, plus conservatively predicted deleted keys, so delayed deletion does not cause repeated over-eviction.
  • Add credit deadlines, bounded per-Group/type and process-level backpressure, and no-progress polling backoff.
  • Document the lifecycle, failure semantics, configuration defaults, and observability.

Key behavior

  • SubmitAsync returns accepted=false with no valid Future when the initial enqueue is rejected; Reclaimer state is created only after acceptance.
  • Every accepted request has one end-to-end Future. Worker failure, exception, secondary enqueue failure, shutdown cancellation, and normal completion all converge on an exactly-once promise completion path.
  • delay_before_delete_ms starts after metadata Sync succeeds.
  • Pending identity includes instance_id, block key, and location id, preserving strict Instance isolation.
  • Delete bytes use the same URI-size accounting as official usage, with VCNS_HF3FS normalized to HF3FS; all effective-watermark subtraction is saturating.
  • A credit timeout stops bytes/key-count deduction but intentionally retains pending identity and hard-limit quota until the Future reaches a terminal state.
  • A saturated Group/type only blocks that type, while process-level limits stop all new admission.
  • Watermark excess without an accepted non-empty request uses the normal polling interval instead of spinning.

Validation

All compilation and tests were run in a dedicated Linux container.

bazelisk test --jobs=1 --runs_per_test=10 --cache_test_results=no \
  //kv_cache_manager/manager/test:SchedulePlanExecutorTest \
  //kv_cache_manager/manager/test:CacheReclaimerTest \
  //kv_cache_manager/service/test:ServerConfigTest

Results:

  • SchedulePlanExecutorTest: 16 tests, target passed 10/10 runs.
  • CacheReclaimerTest: 75 tests, target passed 10/10 runs.
  • ServerConfigTest: 3 tests, target passed 10/10 runs.
  • //kv_cache_manager:kv_cache_manager_bin: built successfully with --jobs=1.
  • C/C++ changed-line formatting, Python formatting for the separately exercised integration harness, and git diff --check: passed.

@github-actions github-actions Bot added the ai reviewed AI has reviewed this PR label Jul 14, 2026
@shaohuaxi
shaohuaxi force-pushed the rc/feat/cache_reclaimer-async-delete-v1 branch 2 times, most recently from 0ec6ceb to b800f4c Compare July 15, 2026 02:53

@oldsharp oldsharp left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A high-quality, well-tested change with a clear design rationale.

Two questions to concern in this round of review:

  1. about the SubmitAsync()'s accepting task type;
  2. about the integration test implementation.

See the inline comments for more detail.


std::future<PlanExecuteResult> Submit(const CacheMetaDelRequest &task);
std::future<PlanExecuteResult> Submit(const CacheLocationDelRequest &task);
AsyncDeleteSubmitResult SubmitAsync(const CacheLocationDelRequest &task);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should SubmitAsync accept CacheMetaDelRequest too?

No such use case under the current code base, but the case is reasonable.

CacheMetaDelRequest has the same problem the V1 async design was built to solve: Get/CAS/Sync may hit remote Redis and block the caller thread. And by making SubmitAsync accept CacheMetaDelRequest type task can let it align better to the other two Submit variants.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing.
Addressed by adding SubmitAsync(const CacheMetaDelRequest&) and reusing the same admission, delayed physical deletion, shutdown cancellation, and exactly-once Future completion pipeline as CacheLocationDelRequest. Existing synchronous callers remain unchanged. Unit tests cover worker execution, CLS_DELETING idempotency, failures, cancellation, and enqueue rejection.

3. Future 超过 deadline 后,验证 credit 回弹、pending 保留且提交量受硬上限约束。
4. 某个 Group × Type 达到上限后,验证其他未饱和 Type 在 Executor 可用时仍可提交。
5. 没有有效 victim、全部 pending 和入队拒绝时,验证 ReclaimCron 不会零间隔空转。
6. 同一 Group 多 Instance 时,验证一轮内水位满足后停止继续提交。

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong integration test verification design; would be better to add corresponding test implementations (similar to what #161 did in integration_test/reclaimer/reclaiming_test.py).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. Added Reclaimer integration tests covering byte/key-count credit under large delete delay, multiple Instances in the same Group, Future-state metric cleanup, and no-progress backoff. The multi-Instance case now uses metric polling instead of fixed sleep and passed 10 consecutive runs on the 193 test environment.

@shaohuaxi
shaohuaxi force-pushed the rc/feat/cache_reclaimer-async-delete-v1 branch from b800f4c to e0f357e Compare July 17, 2026 03:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai reviewed AI has reviewed this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants