Skip to content

Commit cef49da

Browse files
authored
feat: Add caching mechanism for latest plugin version retrieval (#14968)
1 parent e164535 commit cef49da

File tree

1 file changed

+70
-7
lines changed

1 file changed

+70
-7
lines changed

api/services/plugin/plugin_service.py

+70-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import logging
2-
from collections.abc import Sequence
2+
from collections.abc import Mapping, Sequence
33
from mimetypes import guess_type
4+
from typing import Optional
5+
6+
from pydantic import BaseModel
47

58
from configs import dify_config
69
from core.helper import marketplace
@@ -18,11 +21,71 @@
1821
from core.plugin.manager.asset import PluginAssetManager
1922
from core.plugin.manager.debugging import PluginDebuggingManager
2023
from core.plugin.manager.plugin import PluginInstallationManager
24+
from extensions.ext_redis import redis_client
2125

2226
logger = logging.getLogger(__name__)
2327

2428

2529
class PluginService:
30+
class LatestPluginCache(BaseModel):
31+
plugin_id: str
32+
version: str
33+
unique_identifier: str
34+
35+
REDIS_KEY_PREFIX = "plugin_service:latest_plugin:"
36+
REDIS_TTL = 60 * 5 # 5 minutes
37+
38+
@staticmethod
39+
def fetch_latest_plugin_version(plugin_ids: Sequence[str]) -> Mapping[str, Optional[LatestPluginCache]]:
40+
"""
41+
Fetch the latest plugin version
42+
"""
43+
result: dict[str, Optional[PluginService.LatestPluginCache]] = {}
44+
45+
try:
46+
cache_not_exists = []
47+
48+
# Try to get from Redis first
49+
for plugin_id in plugin_ids:
50+
cached_data = redis_client.get(f"{PluginService.REDIS_KEY_PREFIX}{plugin_id}")
51+
if cached_data:
52+
result[plugin_id] = PluginService.LatestPluginCache.model_validate_json(cached_data)
53+
else:
54+
cache_not_exists.append(plugin_id)
55+
56+
if cache_not_exists:
57+
manifests = {
58+
manifest.plugin_id: manifest
59+
for manifest in marketplace.batch_fetch_plugin_manifests(cache_not_exists)
60+
}
61+
62+
for plugin_id, manifest in manifests.items():
63+
latest_plugin = PluginService.LatestPluginCache(
64+
plugin_id=plugin_id,
65+
version=manifest.latest_version,
66+
unique_identifier=manifest.latest_package_identifier,
67+
)
68+
69+
# Store in Redis
70+
redis_client.setex(
71+
f"{PluginService.REDIS_KEY_PREFIX}{plugin_id}",
72+
PluginService.REDIS_TTL,
73+
latest_plugin.model_dump_json(),
74+
)
75+
76+
result[plugin_id] = latest_plugin
77+
78+
# pop plugin_id from cache_not_exists
79+
cache_not_exists.remove(plugin_id)
80+
81+
for plugin_id in cache_not_exists:
82+
result[plugin_id] = None
83+
84+
return result
85+
except Exception:
86+
logger.exception("failed to fetch latest plugin version")
87+
return result
88+
2689
@staticmethod
2790
def get_debugging_key(tenant_id: str) -> str:
2891
"""
@@ -40,19 +103,19 @@ def list(tenant_id: str) -> list[PluginEntity]:
40103
plugins = manager.list_plugins(tenant_id)
41104
plugin_ids = [plugin.plugin_id for plugin in plugins if plugin.source == PluginInstallationSource.Marketplace]
42105
try:
43-
manifests = {
44-
manifest.plugin_id: manifest for manifest in marketplace.batch_fetch_plugin_manifests(plugin_ids)
45-
}
106+
manifests = PluginService.fetch_latest_plugin_version(plugin_ids)
46107
except Exception:
47108
manifests = {}
48109
logger.exception("failed to fetch plugin manifests")
49110

50111
for plugin in plugins:
51112
if plugin.source == PluginInstallationSource.Marketplace:
52113
if plugin.plugin_id in manifests:
53-
# set latest_version
54-
plugin.latest_version = manifests[plugin.plugin_id].latest_version
55-
plugin.latest_unique_identifier = manifests[plugin.plugin_id].latest_package_identifier
114+
latest_plugin_cache = manifests[plugin.plugin_id]
115+
if latest_plugin_cache:
116+
# set latest_version
117+
plugin.latest_version = latest_plugin_cache.version
118+
plugin.latest_unique_identifier = latest_plugin_cache.unique_identifier
56119

57120
return plugins
58121

0 commit comments

Comments
 (0)