Skip to content

[ISSUE] Refactor MemoryMcpCacheIndex to remove read-path LRU contention and improve cache consistencyย #14932

Closed

Description

@YutShang

Background

McpCacheIndex is the cache abstraction used by MCP-related query paths, and its default implementation is MemoryMcpCacheIndex.

The current implementation maintains:

  • in-memory name -> id and id -> data indexes
  • a hand-rolled LRU doubly linked list
  • capacity eviction
  • periodic cleanup

However, the current design has a concurrency and maintainability problem:

  • cache read paths (getMcpId, getMcpServerById) also update LRU order
  • this makes reads effectively carry write semantics
  • the implementation currently relies on ReadWriteLock plus synchronized linked-list operations to keep the list consistent
  • under high-QPS read scenarios, this can become a hotspot and reduces the benefit of concurrent reads

Problem

There are three main concerns in the current implementation:

  1. Concurrency model mismatch

    • read operations mutate the LRU list
    • multiple concurrent readers can still contend on internal mutation paths
  2. Performance hotspot

    • synchronized protection around LRU node movement serializes hot reads
    • this can degrade throughput in MCP lookup scenarios
  3. Complexity and consistency risk

    • the cache maintains two maps plus a custom linked list
    • expiration, eviction, and invalidation need to keep multiple structures consistent
    • removing by ID may require extra cleanup work to keep name -> id mappings correct

Proposal

Replace the hand-written LRU implementation in MemoryMcpCacheIndex with a Caffeine-backed local cache while keeping the McpCacheIndex interface unchanged.

Recommended direction:

  • keep McpCacheIndex API unchanged
  • keep MemoryMcpCacheIndex as the default implementation class to minimize integration churn
  • use Caffeine for:
    • maximum size based eviction
    • expire-after-write semantics
    • access tracking / eviction policy management
  • keep name -> id mapping as a lightweight secondary index
  • use cache removal callbacks to clean related name mappings and preserve index consistency
  • keep existing configuration properties as much as possible to avoid operational impact

Expected Benefits

  • remove read-path manual LRU mutation and lock contention
  • reduce custom concurrent data structure complexity
  • improve maintainability and correctness
  • keep changes localized to the ai module and minimize PR merge conflict risk
  • preserve current business-facing interface and wiring

Compatibility Notes

To reduce review risk, this refactor should:

  • avoid changing McpCacheIndex consumers
  • preserve current configuration names
  • prefer expireAfterWrite to stay close to current semantics
  • add focused tests for:
    • eviction behavior
    • expiration cleanup
    • name/id index consistency
    • concurrent read/write access
    • shutdown behavior

Scope

Impacted areas are expected to be mainly inside the ai module:

  • MemoryMcpCacheIndex
  • McpServerIndexConfiguration
  • ai/pom.xml
  • related cache tests

Activity

  1. YutShang commented on Apr 15, 2026

    @YutShang
    Author

    I have resloved it #14933

  2. KomachiSion commented on Apr 15, 2026

    @KomachiSion
    Collaborator

    Great suggestion, But I see the solution is use new depend Caffeine.

    Why not use the guava LRU?

  3. YutShang commented on Apr 15, 2026

    @YutShang
    Author

    Thanks for the suggestion.

    I did consider Guava Cache as well. My main reason for leaning toward Caffeine is that this issue is not just about replacing the LRU logic, but also about reducing contention on the read path.

    Compared with Guava Cache, Caffeine is generally a better fit for high-concurrency local cache scenarios, especially when cache access is on a hot path. Since MemoryMcpCacheIndex sits in the MCP lookup path, I felt it was safer to choose the more cache-focused implementation here.

    Another reason is maintainability. If we are already replacing the hand-written concurrent LRU logic with a library, my preference is to use the option that is more specialized for this kind of use case, while still keeping the change local:

    • no change to McpCacheIndex
    • no change to callers
    • only the internal implementation is replaced

    That said, I do not strongly depend on Caffeine itself. The main goal of this proposal is to remove the custom concurrent LRU logic and avoid the current read-path synchronization hotspot. If the project prefers Guava for dependency consistency, Iโ€™m happy to adjust the implementation in that direction.

    Great suggestion, But I see the solution is use new depend Caffeine.

    Why not use the guava LRU?

  4. KomachiSion commented on Apr 16, 2026

    @KomachiSion
    Collaborator

    Infact nacos common depend guava by grpc, and nacos server core depend common so most of nacos server module will auto depend guava.

    I see the todo comment also keep the the LRU cache should be re-designed or use stabled high performance LRU cache such as guava.

    So I think don't need add new depend Caffeine , use guava LRU to refactor this will be better. How about you?

  5. added a commit that references this issue on Jul 14, 2026
  6. added a commit that references this issue on Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/discussionCategory issues related to discussion

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions