From 60e32861fe5cbc853c05c111e33c65eb4437fdf4 Mon Sep 17 00:00:00 2001 From: "nick.yi" Date: Tue, 13 Oct 2026 14:42:28 +0800 Subject: [PATCH] perf(agent): cache plugin list instead of reading plugin config per agent - GetPlugin reads a shared cached plugin list; GetAgents loads it once per call - Clear the cache after a plugin is installed or removed --- .../Agents/Services/AgentService.GetAgents.cs | 4 +++- .../Agents/Services/AgentService.GetPlugin.cs | 15 ++++++++++++++- .../BotSharp.Core/Plugins/PluginLoader.cs | 3 +++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs index 035553d99..9eaf62b8e 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs @@ -1,5 +1,6 @@ using BotSharp.Abstraction.Models; using BotSharp.Abstraction.Routing.Settings; +using BotSharp.Core.Plugins; namespace BotSharp.Core.Agents.Services; @@ -11,9 +12,10 @@ public async Task> GetAgents(AgentFilter filter) var agents = await _db.GetAgents(filter); var routeSetting = _services.GetRequiredService(); + var plugins = await _services.GetRequiredService().GetPlugins(_services); foreach (var agent in agents) { - agent.Plugin = await GetPlugin(agent.Id); + agent.Plugin = MatchPlugin(plugins, agent.Id); } agents = agents.Where(x => x.Installed).ToList(); diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetPlugin.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetPlugin.cs index eae5f9140..5e8b86031 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetPlugin.cs @@ -6,9 +6,22 @@ namespace BotSharp.Core.Agents.Services; public partial class AgentService { public async Task GetPlugin(string agentId) + { + var plugins = await GetPlugins(); + return MatchPlugin(plugins, agentId); + } + + // One cache entry shared by all agents. The plugin admin page reads PluginLoader directly, + // so installing or removing a plugin shows up there without waiting for this to expire. + [SharpCache(10)] + private async Task> GetPlugins() { var loader = _services.GetRequiredService(); - var plugins = await loader.GetPlugins(_services); + return await loader.GetPlugins(_services); + } + + private static PluginDef MatchPlugin(List plugins, string agentId) + { return plugins.FirstOrDefault(x => x.AgentIds.Contains(agentId)) ?? new PluginDef { diff --git a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs index 67fe882ca..6d7f92d21 100644 --- a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs +++ b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs @@ -201,6 +201,9 @@ public async Task UpdatePluginStatus(IServiceProvider services, strin await agentService.UpdateAgent(agent, AgentField.Disabled); } } + + // AgentService caches the plugin list; drop it so the new status takes effect right away. + Utilities.ClearCache(); return plugin; }