Skip to content

Conversation

@misselvexu
Copy link
Contributor

@misselvexu misselvexu commented Jul 12, 2025

Please do not create a Pull Request without creating an issue first.

What is the purpose of the change

Fixes #13526

This PR adds MCP (Model Context Protocol) indexing cache support to improve performance when handling MCP server index operations. The implementation provides a configurable caching mechanism that can be enabled/disabled via the nacos.mcp.cache.enabled property.

Brief changelog

  • Added CachedMcpServerIndex implementation with configurable caching support
  • Added McpServerIndexConfiguration for unified conditional bean creation
  • Added McpCacheIndex for managing cached MCP server data
  • Modified PlainMcpServerIndex to remove @Service annotation to avoid bean conflicts
  • Added comprehensive unit tests for the new configuration and caching logic
  • Added configuration property nacos.mcp.cache.enabled to control caching behavior

Verifying this change

Unit Tests

  • Added McpServerIndexConfigurationTest to verify conditional bean creation
  • Added CachedMcpServerIndexTest to verify caching behavior
  • Added McpCacheIndexTest to verify cache management functionality

Manual Testing

  1. Cache Enabled Mode (nacos.mcp.cache.enabled=true):

    • Verify CachedMcpServerIndex is injected as the primary implementation
    • Verify cache operations work correctly
    • Verify scheduled cache refresh functionality
  2. Cache Disabled Mode (nacos.mcp.cache.enabled=false):

    • Verify PlainMcpServerIndex is injected as the fallback implementation
    • Verify no cache-related beans are created
    • Verify direct MCP server operations work without caching

Performance Testing

  • Compare response times between cached and non-cached modes
  • Verify memory usage is reasonable when caching is enabled
  • Test cache eviction and refresh mechanisms

Configuration Testing

  • Verify both true and false values for nacos.mcp.cache.enabled work correctly
  • Verify default behavior when property is not set
  • Test configuration hot-reload if applicable

Checklist

  • Make sure there is a Github issue filed for the change (usually before you start working on it). Trivial changes like typos do not require a Github issue. Your pull request should address just this issue, without pulling in other changes - one PR resolves one issue.
  • Format the pull request title like [ISSUE #123] Fix UnknownException when host config not exist. Each commit in the pull request should have a meaningful subject line and body.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Write necessary unit-test to verify your logic correction, more mock a little better when cross module dependency exist. If the new feature or significant change is committed, please remember to add integration-test in test module.
  • Run mvn -B clean package apache-rat:check findbugs:findbugs -Dmaven.test.skip=true to make sure basic checks pass. Run mvn clean install -DskipITs to make sure unit-test pass. Run mvn clean test-compile failsafe:integration-test to make sure integration-test pass.

Additional Notes

  • The implementation ensures only one MCP server index implementation is active at a time
  • Cache configuration is centralized in McpServerIndexConfiguration for better maintainability
  • The solution provides backward compatibility by defaulting to the plain implementation when caching is disabled
  • All cache-related beans are conditionally created only when caching is enabled to avoid unnecessary resource usage

@CLAassistant
Copy link

CLAassistant commented Jul 12, 2025

CLA assistant check
All committers have signed the CLA.

@github-actions
Copy link

Thanks for your this PR. 🙏
Please check again for your PR changes whether contains any usage/api/configuration change such as Add new API , Add new configuration, Change default value of configuration.
If so, please add or update documents(markdown type) in docs/next/ for repository nacos-group/nacos-group.github.io


感谢您提交的PR。 🙏
请再次查看您的PR内容,确认是否包含任何使用方式/API/配置参数的变更,如:新增API新增配置参数修改默认配置等操作。
如果是,请确保在提交之前,在仓库nacos-group/nacos-group.github.io中的docs/next/目录下添加或更新文档(markdown格式)。

@lingma-agents
Copy link

lingma-agents bot commented Jul 12, 2025

添加MCP索引缓存支持及配置管理功能

变更概述

新功能

  • 缓存机制实现
    • 新增CachedMcpServerIndex实现类,提供基于内存的LRU缓存和数据库查询的混合索引方案
    • 实现McpCacheIndex接口及MemoryMcpCacheIndex内存缓存实现,支持缓存统计和自动清理
    • 添加McpServerIndexConfiguration配置类,通过nacos.mcp.cache.enabled动态切换缓存启用状态
    • 引入缓存同步任务,定期执行数据库全量同步(ScheduledExecutorService实现)
  • 配置管理增强
    • 新增配置属性:nacos.mcp.cache.max-sizeexpire-time-secondscleanup-interval-seconds
    • 支持通过Spring条件注解动态创建Bean(@ConditionalOnProperty

性能优化

  • 缓存命中时减少数据库查询次数,提升MCP服务器索引查询性能
  • 内存缓存实现LRU淘汰策略和过期时间控制,优化资源使用

测试更新

  • 新增配置类单元测试McpServerIndexConfigurationTest验证Bean切换逻辑
  • 完整覆盖缓存核心功能的单元测试CachedMcpServerIndexTestMemoryMcpCacheIndexTest
  • 添加性能基准测试McpCachePerformanceTest验证缓存吞吐量和并发表现

重构

  • 移除PlainMcpServerIndex类的@Service注解,避免与缓存实现冲突

配置调整

  • 新增缓存相关配置属性,提供完整的缓存行为控制参数
变更文件
文件路径 变更说明
ai/​src/​main/​java/​com/​alibaba/​nacos/​ai/​config/​McpServerIndexConfiguration.​java 新增MCP索引配置类,通过Spring条件注解动态创建缓存启用/禁用时的Bean实现
ai/​src/​main/​java/​com/​alibaba/​nacos/​ai/​index/​CachedMcpServerIndex.​java 实现缓存优先的MCP服务器索引查询逻辑,包含缓存同步和数据库回退机制
ai/​src/​main/​java/​com/​alibaba/​nacos/​ai/​index/​McpCacheIndex.​java 定义MCP缓存接口规范,包含索引增删改查和统计功能
ai/​src/​main/​java/​com/​alibaba/​nacos/​ai/​index/​MemoryMcpCacheIndex.​java 实现内存缓存具体实现,包含LRU淘汰策略和过期时间控制
ai/​src/​main/​java/​com/​alibaba/​nacos/​ai/​index/​McpServerIndex.​java 增加方法注释说明,明确接口行为定义
ai/​src/​main/​java/​com/​alibaba/​nacos/​ai/​index/​PlainMcpServerIndex.​java 移除@service注解避免与缓存实现冲突
ai/​src/​test/​java/​com/​alibaba/​nacos/​ai/​config/​McpServerIndexConfigurationTest.​java 新增配置类单元测试,验证Bean条件创建逻辑
ai/​src/​test/​java/​com/​alibaba/​nacos/​ai/​index/​CachedMcpServerIndexTest.​java 覆盖缓存命中/未命中、数据库回退等核心场景的单元测试
时序图
sequenceDiagram
    participant Client as MCP客户端
    participant Cache as MemoryMcpCacheIndex
    participant DB as 数据库查询层
    participant Sync as 缓存同步任务
    Client->>Cache: getMcpServerById(mcpId)
    Cache->>Cache: 检查缓存是否存在
    Cache-->>Client: 返回缓存数据(命中时)
    Cache--xClient: 缓存未命中时
    Client->>DB: 执行数据库查询
    DB-->>Client: 返回数据库结果
    Client->>Cache: 更新缓存数据
    Cache-->>Client: 返回最新数据
    Sync->>DB: 定期全量同步任务
    DB-->>Cache: 提交同步数据
    Cache->>Cache: 更新本地缓存
Loading

💡 小贴士

与 lingma-agents 交流的方式

📜 直接回复评论
直接回复本条评论,lingma-agents 将自动处理您的请求。例如:

  • 在当前代码中添加详细的注释说明。

  • 请详细介绍一下你说的 LRU 改造方案,并使用伪代码加以说明。

📜 在代码行处标记
在文件的特定位置创建评论并 @lingma-agents。例如:

  • @lingma-agents 分析这个方法的性能瓶颈并提供优化建议。

  • @lingma-agents 对这个方法生成优化代码。

📜 在讨论中提问
在任何讨论中 @lingma-agents 来获取帮助。例如:

  • @lingma-agents 请总结上述讨论并提出解决方案。

  • @lingma-agents 请根据讨论内容生成优化代码。

Copy link

@lingma-agents lingma-agents bot left a comment

Choose a reason for hiding this comment

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

🔎 代码评审报告

🎯 评审意见概览
严重度 数量 说明
🔴 Blocker 1 阻断性问题,需立即修复。例如:系统崩溃、关键功能不可用或严重安全漏洞。
🟠 Critical 1 严重问题,高优先级修复。例如:核心功能异常或性能瓶颈影响用户体验。
🟡 Major 3 主要问题,建议修复。例如:非核心功能缺陷或代码维护性较差。
🟢 Minor 5 次要问题,酬情优化。例如:代码格式不规范或注释缺失。

总计: 10 个问题

⚠️ 需要立即关注的阻断性问题

ai/src/main/java/com/alibaba/nacos/ai/index/PlainMcpServerIndex.java

📋 评审意见详情
💡 代码实现建议
以下是文件级别的代码建议,聚焦于代码的可读性、可维护性和潜在问题。
☕ ai/src/main/java/com/alibaba/nacos/ai/index/CachedMcpServerIndex.java (3 💬)
☕ ai/src/main/java/com/alibaba/nacos/ai/index/MemoryMcpCacheIndex.java (3 💬)
☕ ai/src/main/java/com/alibaba/nacos/ai/index/PlainMcpServerIndex.java (1 💬)
☕ ai/src/test/java/com/alibaba/nacos/ai/index/CachedMcpServerIndexTest.java (1 💬)
☕ ai/src/test/java/com/alibaba/nacos/ai/index/McpCachePerformanceTest.java (1 💬)
☕ ai/src/test/java/com/alibaba/nacos/ai/index/MemoryMcpCacheIndexTest.java (1 💬)
🚀 架构设计建议
以下是对代码架构和设计的综合分析,聚焦于跨文件交互、系统一致性和潜在优化空间。
🔍1. 配置属性获取方式不一致导致潜在配置冲突

在McpServerIndexConfiguration配置类中,缓存配置属性通过@value注解读取,而在CachedMcpServerIndex构造函数中,syncInterval参数却通过System.getProperty获取。这种不一致的配置获取方式可能导致配置值不一致,例如当系统属性与Spring配置文件配置不同时,会出现不可预测的行为。应统一使用Spring的依赖注入方式获取所有配置属性。

📌 关键代码

this.syncInterval = Long.parseLong(System.getProperty("nacos.mcp.cache.sync.interval", "300"));
@Value("${nacos.mcp.cache.sync-interval-seconds:300}")
private long syncIntervalSeconds;

⚠️ 潜在风险

配置值不一致可能导致缓存同步任务执行频率与预期不符,引发缓存数据不一致或资源浪费问题

🔍2. 缓存过期逻辑未实现导致内存泄漏风险

MemoryMcpCacheIndex的Entry类中isExpired()方法始终返回false,未实现基于expireTimeSeconds的过期检查逻辑。虽然配置了缓存过期时间,但实际未生效,导致缓存项永久驻留内存,可能引发内存泄漏风险。

📌 关键代码

boolean isExpired() {
    return false;
}

⚠️ 潜在风险

未清理过期缓存项将导致内存占用持续增长,最终引发系统OOM错误

🔍3. 缓存同步线程池未正确关闭

CachedMcpServerIndex中的ScheduledExecutorService未在Spring应用上下文关闭时正确关闭。虽然syncTask任务可被取消,但线程池本身未实现关闭逻辑,可能导致资源泄漏。

📌 关键代码

public ScheduledExecutorService mcpCacheScheduledExecutor() {
    return new ScheduledThreadPoolExecutor(...);
}

⚠️ 潜在风险

线程池未关闭可能导致进程退出时残留线程,影响系统资源回收和稳定性

🔍4. 核心搜索方法未使用缓存导致性能浪费

CachedMcpServerIndex的searchMcpServerByName方法在缓存开启时直接返回数据库查询结果,未利用缓存加速。该方法注释提到'优先查询缓存'但实际未实现缓存预热逻辑,完全绕过了缓存机制。

📌 关键代码

if (cacheEnabled) {
    // Priority query from cache (only hit statistics here, actual pagination still queries database)
    return searchFromDatabase(...)
}

⚠️ 潜在风险

缓存未被有效利用,导致高频查询直接冲击数据库,无法发挥缓存应有的性能优化作用

🔍5. 并发性能测试生成数据超出缓存容量

McpCachePerformanceTest的并发测试中,当THREAD_COUNT=10且OPERATION_COUNT=10000时,总写入量达10万次,远超配置的CACHE_SIZE=1000。这会导致大量缓存淘汰,测试结果无法准确反映正常负载下的性能表现。

📌 关键代码

private static final int THREAD_COUNT = 10;
private static final int OPERATION_COUNT = 10000;
private static final int CACHE_SIZE = 1000;

⚠️ 潜在风险

测试数据设计不合理可能导致测试结果偏差,无法真实反映缓存在合理容量下的性能表现

🔍6. 缓存清理任务未实现自动过期

MemoryMcpCacheIndex未实现基于expireTimeSeconds的自动过期清理机制,仅依赖LRU淘汰策略。当缓存项存活时间超过配置的expireTimeSeconds时,无效数据仍可能长期占用内存空间。

📌 关键代码

this.expireTimeSeconds = expireTimeSeconds;

⚠️ 潜在风险

无法及时清理过期数据将导致缓存污染,影响查询准确性并加剧内存压力

🔍7. 缓存统计指标未包含过期淘汰次数

CacheStats类中缺少evictionCount统计项的实际计算逻辑。虽然定义了evictionCount字段,但现有代码未在LRU淘汰时更新该计数器,导致统计信息不完整。

📌 关键代码

private final AtomicLong evictionCount = new AtomicLong();

⚠️ 潜在风险

无法准确监控缓存淘汰事件,影响系统性能调优和问题诊断

🔍8. 核心接口方法缺乏参数有效性校验

McpCacheIndex接口的updateIndex方法未在接口层定义参数有效性约束,导致实现类需要重复处理空值校验。应在接口方法文档中明确参数约束,避免多处重复校验逻辑。

📌 关键代码

void updateIndex(String namespaceId, String mcpName, String mcpId);

⚠️ 潜在风险

参数校验分散可能导致部分实现遗漏校验,引发空指针异常或无效数据写入

审查详情
📒 文件清单 (10 个文件)
新增: 8 个文件
📝 变更: 2 个文件

✅ 新增文件:

  • ai/src/main/java/com/alibaba/nacos/ai/config/McpServerIndexConfiguration.java
  • ai/src/main/java/com/alibaba/nacos/ai/index/CachedMcpServerIndex.java
  • ai/src/main/java/com/alibaba/nacos/ai/index/McpCacheIndex.java
  • ai/src/main/java/com/alibaba/nacos/ai/index/MemoryMcpCacheIndex.java
  • ai/src/test/java/com/alibaba/nacos/ai/config/McpServerIndexConfigurationTest.java
  • ai/src/test/java/com/alibaba/nacos/ai/index/CachedMcpServerIndexTest.java
  • ai/src/test/java/com/alibaba/nacos/ai/index/McpCachePerformanceTest.java
  • ai/src/test/java/com/alibaba/nacos/ai/index/MemoryMcpCacheIndexTest.java

📝 变更文件:

  • ai/src/main/java/com/alibaba/nacos/ai/index/McpServerIndex.java
  • ai/src/main/java/com/alibaba/nacos/ai/index/PlainMcpServerIndex.java

💡 小贴士

与 lingma-agents 交流的方式

📜 直接回复评论
直接回复本条评论,lingma-agents 将自动处理您的请求。例如:

  • 在当前代码中添加详细的注释说明。

  • 请详细介绍一下你说的 LRU 改造方案,并使用伪代码加以说明。

📜 在代码行处标记
在文件的特定位置创建评论并 @lingma-agents。例如:

  • @lingma-agents 分析这个方法的性能瓶颈并提供优化建议。

  • @lingma-agents 对这个方法生成优化代码。

📜 在讨论中提问
在任何讨论中 @lingma-agents 来获取帮助。例如:

  • @lingma-agents 请总结上述讨论并提出解决方案。

  • @lingma-agents 请根据讨论内容生成优化代码。

Comment on lines 168 to 169
// Test update operations with null parameters (should not throw exceptions)
assertDoesNotThrow(() -> cacheIndex.updateIndex(null, "test", "id"));
Copy link

Choose a reason for hiding this comment

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

空参数测试未验证缓存容量限制

🟢 Minor | 🧹 Code Smells

📋 问题详情

在testNullAndEmptyParameters测试中,未验证更新空参数是否会影响缓存容量统计,可能导致缓存统计逻辑缺陷。

💡 解决方案

添加容量验证断言:

@@ -168,2 +168,4 @@
168         assertDoesNotThrow(() -> cacheIndex.updateIndex("test", null, "id"));
169         assertDoesNotThrow(() -> cacheIndex.updateIndex("test", "name", null));
+ 
+        assertEquals(0, cacheIndex.getSize(), "空参数更新不应占用缓存空间");

您的反馈对我们很重要!(建议右键在新标签页中打开以下链接)

有用意见👍无用意见👎错误意见❌

@misselvexu
Copy link
Contributor Author

@lingma-agents Regarding your question about removing the @Service annotation from PlainMcpServerIndex, the initialization process becomes a configuration, and is unified in the McpServerIndexConfiguration configuration class, so you can check if there is any missing logic.

@lingma-agents
Copy link

lingma-agents bot commented Jul 12, 2025

这个改动的实现是合理的,通过配置类统一管理Bean创建有以下优势:

  1. 条件化配置:使用@ConditionalOnProperty根据nacos.mcp.cache.enabled属性动态选择实现类,实现了缓存开关的灵活控制
  2. 依赖注入显式化:所有依赖项(如ConfigDetailService)通过构造参数显式声明,比注解方式更清晰
  3. 单一职责原则:将Bean创建逻辑集中到配置类,保持业务类PlainMcpServerIndex的纯粹性

关键验证点:

  • cache.enabled=true时,会创建CachedMcpServerIndex作为主Bean(通过@Primary标注)
  • cache.enabled=false时,PlainMcpServerIndex通过条件注解被正确创建
  • 配置类中的@Bean方法保证了依赖项的正确注入顺序

补充说明:

  • 测试类McpServerIndexConfigurationTest通过不同系统属性验证了两种场景下的Bean类型
  • CachedMcpServerIndex的构造函数参数包含了缓存配置参数和执行器,确保了完整的依赖注入
  • PlainMcpServerIndex不再需要@Service注解,因为其Bean由配置类显式声明创建

这种设计模式符合Spring框架的最佳实践,通过条件化配置实现了灵活的组件切换,同时保持了代码的可测试性和可维护性。

@misselvexu
Copy link
Contributor Author

这个改动的实现是合理的,通过配置类统一管理Bean创建有以下优势:

  1. 条件化配置:使用@ConditionalOnProperty根据nacos.mcp.cache.enabled属性动态选择实现类,实现了缓存开关的灵活控制
  2. 依赖注入显式化:所有依赖项(如ConfigDetailService)通过构造参数显式声明,比注解方式更清晰
  3. 单一职责原则:将Bean创建逻辑集中到配置类,保持业务类PlainMcpServerIndex的纯粹性

关键验证点:

  • cache.enabled=true时,会创建CachedMcpServerIndex作为主Bean(通过@Primary标注)
  • cache.enabled=false时,PlainMcpServerIndex通过条件注解被正确创建
  • 配置类中的@Bean方法保证了依赖项的正确注入顺序

补充说明:

  • 测试类McpServerIndexConfigurationTest通过不同系统属性验证了两种场景下的Bean类型
  • CachedMcpServerIndex的构造函数参数包含了缓存配置参数和执行器,确保了完整的依赖注入
  • PlainMcpServerIndex不再需要@Service注解,因为其Bean由配置类显式声明创建

这种设计模式符合Spring框架的最佳实践,通过条件化配置实现了灵活的组件切换,同时保持了代码的可测试性和可维护性。

@lingma-agents So does your Code Review Report need to be re-optimized and tweaked a bit?

@lingma-agents
Copy link

lingma-agents bot commented Jul 12, 2025

您的实现设计合理且符合Spring最佳实践,以下是对优化方向的详细分析:

  1. 配置属性管理优化建议 当前使用@Value分散注入配置项,可考虑改用@ConfigurationProperties集中管理: java @ConfigurationProperties(prefix = "nacos.mcp.cache") private CacheProperties cacheProperties; 这样能提升配置管理的可维护性,尤其当配置项较多时优势明显。
  2. 线程池关闭完善建议CachedMcpServerIndex#destroy()中,建议增加线程池关闭等待: java if (!scheduledExecutor.isTerminated()) { scheduledExecutor.shutdownNow(); try { if (!scheduledExecutor.awaitTermination(30, TimeUnit.SECONDS)) { LOGGER.warn("Failed to shutdown executor within 30s"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } 这能确保资源释放更彻底,避免潜在泄漏。
  3. 缓存结构线程安全增强 MemoryMcpCacheIndex#idToEntryLruMap在高并发场景下可能因removeEldestEntry触发频繁导致竞争。建议改用ConcurrentHashMap结合自定义淘汰策略,或采用Caffeine等成熟缓存库提升性能与线程安全。
  4. 测试覆盖补充建议 - 增加缓存容量达上限时的LRU淘汰测试 - 验证缓存项过期后自动清理机制 - 压力测试(如10k并发访问+缓存命中率监控)
  5. 日志增强建议CachedMcpServerIndex#syncCacheFromDatabase中添加同步任务进度日志: java LOGGER.debug("Synced {} MCP servers for namespace: {}", entriesSynced, namespaceId);
    目前实现已满足核心需求,以上优化可按项目扩展性要求逐步实施。当前设计已通过单元测试验证,建议保持现有结构,仅在后续迭代中逐步完善上述细节。

Copy link
Collaborator

@KomachiSion KomachiSion left a comment

Choose a reason for hiding this comment

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

I have some confused about McpCachedIndex and McpServerIndex

I think the McpCachedIndex is a implementation of McpServerIndex .

But I see you abstract and new McpCachedIndex which be covered and proxyed by CachedMcpServerIndex of implementation of McpServerIndex.

What's the diff about McpCachedIndex and McpServerIndex in design?

@Value("${nacos.mcp.cache.expire-time-seconds:3600}")
private long expireTimeSeconds;

@Value("${nacos.mcp.cache.cleanup-interval-seconds:300}")
Copy link
Collaborator

Choose a reason for hiding this comment

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

default 300 ms whether too fast and frequently?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This part of the code is still being optimized and needs some more suggested values based on the results of the benchmarking.

@Value("${nacos.mcp.cache.cleanup-interval-seconds:300}")
private long cleanupIntervalSeconds;

@Value("${nacos.mcp.cache.sync-interval-seconds:300}")
Copy link
Collaborator

Choose a reason for hiding this comment

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

default 300 ms whether too fast and frequently?

*
* @author misselvexu
*/
public interface McpCacheIndex {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's the diffenent with McpServerIndex?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See specifically the response below, "MCP Server Index Architecture - Core Relationships".

@misselvexu
Copy link
Contributor Author

I have some confused about McpCachedIndex and McpServerIndex

I think the McpCachedIndex is a implementation of McpServerIndex .

But I see you abstract and new McpCachedIndex which be covered and proxyed by CachedMcpServerIndex of implementation of McpServerIndex.

What's the diff about McpCachedIndex and McpServerIndex in design?

MCP Server Index Architecture - Core Relationships

Interface Hierarchy

McpServerIndex (Business Interface)
├── PlainMcpServerIndex (Direct DB Implementation)
└── CachedMcpServerIndex (Cache-Enhanced Implementation)
    └── Composes McpCacheIndex (Cache Strategy Interface)
        └── MemoryMcpCacheIndex (Memory Cache Implementation)

Component Responsibilities

McpServerIndex - Core Business Interface

public interface McpServerIndex {
    Page<McpServerIndexData> searchMcpServerByName(...);
    McpServerIndexData getMcpServerById(String id);
    McpServerIndexData getMcpServerByName(String namespaceId, String name);
}
  • Purpose: Defines business operations for MCP server queries
  • Focus: What operations are available

McpCacheIndex - Cache Strategy Interface

public interface McpCacheIndex {
    String getMcpId(String namespaceId, String mcpName);
    McpServerIndexData getMcpServerById(String mcpId);
    void updateIndex(String namespaceId, String mcpName, String mcpId);
    void removeIndex(String mcpId);
    CacheStats getStats();
}
  • Purpose: Defines cache operations and management
  • Focus: How to store and retrieve cached data

Implementation Relationships

PlainMcpServerIndex - No Cache

public class PlainMcpServerIndex implements McpServerIndex {
    // Direct database queries - no caching
    public McpServerIndexData getMcpServerById(String id) {
        // Query database directly for each request
        return queryDatabase(id);
    }
}
  • Role: Simple implementation without caching
  • Use Case: When cache is disabled or for testing

CachedMcpServerIndex - Cache Decorator

public class CachedMcpServerIndex implements McpServerIndex {
    private final McpCacheIndex cacheIndex; // Composition
    
    public McpServerIndexData getMcpServerById(String id) {
        // 1. Try cache first
        McpServerIndexData cached = cacheIndex.getMcpServerById(id);
        if (cached != null) return cached;
        
        // 2. Fallback to database
        McpServerIndexData dbData = queryDatabase(id);
        if (dbData != null) {
            cacheIndex.updateIndex(dbData.getNamespaceId(), dbData.getId(), dbData.getId());
        }
        return dbData;
    }
}
  • Role: Decorates business logic with caching
  • Strategy: Cache-first, database fallback

MemoryMcpCacheIndex - Cache Implementation

public class MemoryMcpCacheIndex implements McpCacheIndex {
    private final ConcurrentHashMap<String, CacheNode> idToEntry;
    private final ConcurrentHashMap<String, String> nameKeyToId;
    
    // LRU eviction, expiration, statistics
}
  • Role: In-memory cache with LRU eviction
  • Features: Thread-safe, configurable size/expiration

Configuration-Driven Selection

@Configuration
public class McpServerIndexConfiguration {
    
    @Bean
    @ConditionalOnProperty(name = "nacos.mcp.cache.enabled", havingValue = "true")
    public McpServerIndex cachedMcpServerIndex(McpCacheIndex cacheIndex, ...) {
        return new CachedMcpServerIndex(..., cacheIndex, ...);
    }
    
    @Bean
    @ConditionalOnProperty(name = "nacos.mcp.cache.enabled", havingValue = "false")
    public McpServerIndex plainMcpServerIndex(...) {
        return new PlainMcpServerIndex(...);
    }
}

Key Design Patterns

  1. Decorator Pattern: CachedMcpServerIndex adds caching to core functionality
  2. Strategy Pattern: McpCacheIndex allows different cache implementations
  3. Composition: CachedMcpServerIndex composes McpCacheIndex instead of inheriting

Flow Summary

Request → McpServerIndex (Interface)
    ↓
CachedMcpServerIndex (Implementation)
    ↓
McpCacheIndex (Cache Strategy)
    ↓
MemoryMcpCacheIndex (Cache Implementation)
    ↓
Return cached data OR fallback to database

Core Principle: Business logic (McpServerIndex) is separated from caching strategy (McpCacheIndex), enabling flexible, testable, and maintainable code.

@KomachiSion
Copy link
Collaborator

KomachiSion commented Jul 15, 2025

OK, I will merged this PR after CI passed.

@misselvexu CI can't pass, please fix.

@misselvexu
Copy link
Contributor Author

OK, I will merged this PR after CI passed.

@misselvexu CI can't pass, please fix.

Hold on, I'll fix it right away.

@wuyfee
Copy link

wuyfee commented Jul 15, 2025

$\color{red}{FAILURE}$
DETAILS
✅ - docker: success
❌ - deploy (standalone & cluster & standalone_auth): failure
❌ - e2e-java-test (standalone & cluster & standalone_auth): skipped
❌ - e2e-go-test (standalone & cluster): skipped
❌ - e2e-cpp-test (standalone & cluster): skipped
❌ - e2e-csharp-test (standalone & cluster): skipped
❌ - e2e-nodejs-test (standalone & cluster): skipped
❌ - e2e-python-test (standalone & cluster): skipped
✅ - clean (standalone & cluster & standalone_auth): success

@misselvexu misselvexu requested a review from KomachiSion July 15, 2025 03:47
@wuyfee
Copy link

wuyfee commented Jul 15, 2025

$\color{red}{FAILURE}$
DETAILS
✅ - docker: success
❌ - deploy (standalone & cluster & standalone_auth): failure
❌ - e2e-java-test (standalone & cluster & standalone_auth): skipped
❌ - e2e-go-test (standalone & cluster): skipped
❌ - e2e-cpp-test (standalone & cluster): skipped
❌ - e2e-csharp-test (standalone & cluster): skipped
❌ - e2e-nodejs-test (standalone & cluster): skipped
❌ - e2e-python-test (standalone & cluster): skipped
✅ - clean (standalone & cluster & standalone_auth): success

@KomachiSion
Copy link
Collaborator

UT problem should be fixed by develop branch, maybe you can merged develop branch and retry.

@misselvexu
Copy link
Contributor Author

misselvexu commented Jul 16, 2025

UT problem should be fixed by develop branch, maybe you can merged develop branch and retry.

There is something odd about the fact that I utilize the commands in our ci to do UT in the local environment.

Mainly because I haven't changed or tweaked the code for the Nacos Console module either yay!

Ci Error (nacos-console) module :

Error:    ConsoleExceptionHandlerTest.handleException » IllegalState Failed to load ApplicationContext for [WebMergedContextConfiguration@452d25ef testClass = com.alibaba.nacos.console.exception.ConsoleExceptionHandlerTest, locations = [], classes = [com.alibaba.nacos.console.NacosConsole], contextInitializerClasses = [], activeProfiles = [], propertySourceDescriptors = [], propertySourceProperties = ["org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.autoconfigure.OnFailureConditionReportContextCustomizerFactory$OnFailureConditionReportContextCustomizer@3e71f228, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@64d1bf9, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@211d1474, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@2577ecf9, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizer@4c8ccc57, [ImportsContextCustomizer@439e956c key = [org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration, org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration, org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration, org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration, org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration, org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.boot.test.autoconfigure.web.reactive.WebTestClientAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration, org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration, org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration, org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration, org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration, org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration, org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration, org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration, org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration, org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@71fd650b, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@4d6f0460, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.reactor.netty.DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactory$DisableReactorResourceFactoryGlobalResourcesContextCustomizerCustomizer@7445fdb2, org.springframework.test.context.bean.override.BeanOverrideContextCustomizer@1473d6e7, org.springframework.test.context.support.DynamicPropertiesContextCustomizer@0, org.springframework.boot.test.context.SpringBootTestAnnotation@fe3a29e], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]

Command:

mvn -Prelease-nacos clean test -DtrimStackTrace=false -e -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn

Output:

[INFO] Reactor Summary for Alibaba NACOS 3.0.2 3.0.2:
[INFO] 
[INFO] Alibaba NACOS 3.0.2 ................................ SUCCESS [  4.930 s]
[INFO] nacos-api 3.0.2 .................................... SUCCESS [ 48.972 s]
[INFO] nacos-common 3.0.2 ................................. SUCCESS [01:37 min]
[INFO] nacos-consistency 3.0.2 ............................ SUCCESS [ 11.995 s]
[INFO] nacos-plugin 3.0.2 ................................. SUCCESS [  0.497 s]
[INFO] nacos-datasource-plugin 3.0.2 ...................... SUCCESS [ 10.104 s]
[INFO] nacos-custom-environment-plugin 3.0.2 .............. SUCCESS [  3.233 s]
[INFO] nacos-sys 3.0.2 .................................... SUCCESS [01:20 min]
[INFO] nacos-persistence 3.0.2 ............................ SUCCESS [ 29.747 s]
[INFO] nacos-auth-plugin 3.0.2 ............................ SUCCESS [  7.301 s]
[INFO] nacos-auth 3.0.2 ................................... SUCCESS [ 10.736 s]
[INFO] nacos-trace-plugin 3.0.2 ........................... SUCCESS [  5.042 s]
[INFO] nacos-control-plugin 3.0.2 ......................... SUCCESS [  9.892 s]
[INFO] nacos-core 3.0.2 ................................... SUCCESS [01:18 min]
[INFO] nacos-encryption-plugin 3.0.2 ...................... SUCCESS [  3.088 s]
[INFO] nacos-config-plugin 3.0.2 .......................... SUCCESS [  3.350 s]
[INFO] nacos-config 3.0.2 ................................. SUCCESS [01:34 min]
[INFO] nacos-cmdb 3.0.2 ................................... SUCCESS [  0.863 s]
[INFO] nacos-naming 3.0.2 ................................. SUCCESS [01:32 min]
[INFO] nacos-address 3.0.2 ................................ SUCCESS [ 12.676 s]
[INFO] nacos-client-basic 3.0.2 ........................... SUCCESS [ 26.902 s]
[INFO] nacos-logger-adapter-impl 3.0.2 .................... SUCCESS [  2.309 s]
[INFO] nacos-logback-adapter-12 3.0.2 ..................... SUCCESS [  6.928 s]
[INFO] nacos-log4j2-adapter 3.0.2 ......................... SUCCESS [  9.208 s]
[INFO] nacos-client 3.0.2 ................................. SUCCESS [01:36 min]
[INFO] nacos-ai 3.0.2 ..................................... SUCCESS [ 21.316 s]
[INFO] nacos-lock 3.0.2 ................................... SUCCESS [ 10.083 s]
[INFO] nacos-maintainer-client 3.0.2 ...................... SUCCESS [ 14.224 s]
[INFO] nacos-plugin-default-impl 3.0.2 .................... SUCCESS [  0.082 s]
[INFO] nacos-default-auth-plugin 3.0.2 .................... SUCCESS [ 23.292 s]
[INFO] nacos-default-control-plugin 3.0.2 ................. SUCCESS [  5.312 s]
[INFO] nacos-default-plugin-all ........................... SUCCESS [  0.214 s]
[INFO] nacos-istio 3.0.2 .................................. SUCCESS [ 18.495 s]
[INFO] nacos-prometheus 3.0.2 ............................. SUCCESS [ 20.745 s]
[INFO] nacos-k8s-sync 3.0.2 ............................... SUCCESS [  5.040 s]
[INFO] nacos-console 3.0.2 ................................ SUCCESS [ 43.189 s]
[INFO] nacos-test 3.0.2 ................................... SUCCESS [  0.378 s]
[INFO] nacos-config-test 3.0.2 ............................ SUCCESS [  1.805 s]
[INFO] nacos-naming-test 3.0.2 ............................ SUCCESS [  1.625 s]
[INFO] nacos-core-test 3.0.2 .............................. SUCCESS [  2.335 s]
[INFO] nacos-example 3.0.2 ................................ SUCCESS [  1.035 s]
[INFO] nacos-server 3.0.2 ................................. SUCCESS [  1.148 s]
[INFO] nacos-mcp-regsitry-adaptor ......................... SUCCESS [ 33.080 s]
[INFO] nacos-bootstrap 3.0.2 .............................. SUCCESS [  2.945 s]
[INFO] nacos-distribution 3.0.2 ........................... SUCCESS [  0.751 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  15:59 min
[INFO] Finished at: 2025-07-16T10:23:10+08:00
[INFO] ------------------------------------------------------------------------

Process finished with exit code 0

🤔🤔🤔

@KomachiSion
Copy link
Collaborator

It might be diff in some unit test order. In github action env, ConsoleExceptionHandlerTest maybe run with high order and without Environment so that problem occus.

Develop branch has enhance it, I suggest you merged develop branch.

@KomachiSion
Copy link
Collaborator

Error: Failures:
Error: MemoryMcpCacheIndexTest.testConcurrentPutAndGet:152 All threads should complete within timeout ==> expected: but was:

@KomachiSion KomachiSion merged commit 3191d17 into alibaba:develop Jul 16, 2025
3 checks passed
@KomachiSion KomachiSion added this to the 3.0.3 milestone Jul 16, 2025
@KomachiSion
Copy link
Collaborator

@misselvexu Thanks for contribution.

Do you have dingtalk number? I would like invite you into nacos constribution group.

@wuyfee
Copy link

wuyfee commented Jul 16, 2025

$\color{red}{FAILURE}$
DETAILS
✅ - docker: success
❌ - deploy (standalone & cluster & standalone_auth): failure
❌ - e2e-java-test (standalone & cluster & standalone_auth): skipped
❌ - e2e-go-test (standalone & cluster): skipped
❌ - e2e-cpp-test (standalone & cluster): skipped
❌ - e2e-csharp-test (standalone & cluster): skipped
❌ - e2e-nodejs-test (standalone & cluster): skipped
❌ - e2e-python-test (standalone & cluster): skipped
✅ - clean (standalone & cluster & standalone_auth): success

@misselvexu
Copy link
Contributor Author

@misselvexu Thanks for contribution.

Do you have dingtalk number? I would like invite you into nacos constribution group.

Dingtalk number is misselvexu .

@wuyfee
Copy link

wuyfee commented Jul 16, 2025

$\color{red}{FAILURE}$
DETAILS
✅ - docker: success
❌ - deploy (standalone & cluster & standalone_auth): failure
❌ - e2e-java-test (standalone & cluster & standalone_auth): skipped
❌ - e2e-go-test (standalone & cluster): skipped
❌ - e2e-cpp-test (standalone & cluster): skipped
❌ - e2e-csharp-test (standalone & cluster): skipped
❌ - e2e-nodejs-test (standalone & cluster): skipped
❌ - e2e-python-test (standalone & cluster): skipped
✅ - clean (standalone & cluster & standalone_auth): success

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ADD new index implementation between MCP Nameand MCP Id

4 participants