secra_memory/
plugin.rs

1use async_trait::async_trait;
2use serde::Serialize;
3use serde::de::DeserializeOwned;
4/// PluginMemoryCache 实现
5use std::sync::Arc;
6use std::time::Duration;
7
8use crate::error::CacheError;
9use crate::cache::Cache;
10use super::manager::MemoryManager;
11
12/// 插件内存缓存实例
13///
14/// 每个插件拥有一个独立的 PluginMemoryCache 实例,所有操作自动添加命名空间前缀
15pub struct PluginMemoryCache {
16    /// MemoryManager 引用
17    manager: Arc<MemoryManager>,
18
19    /// 插件 ID
20    plugin_id: String,
21
22    /// 命名空间前缀
23    namespace: String,
24}
25
26impl PluginMemoryCache {
27    /// 创建新的 PluginMemoryCache 实例
28    ///
29    /// # Arguments
30    /// * `manager` - MemoryManager 引用
31    /// * `plugin_id` - 插件 ID
32    ///
33    /// # Returns
34    /// * `Self` - PluginMemoryCache 实例
35    pub(crate) fn new(manager: Arc<MemoryManager>, plugin_id: String) -> Self {
36        let namespace = format!("{}:plugin:{}:", manager.system_name(), plugin_id);
37        Self {
38            manager,
39            plugin_id,
40            namespace,
41        }
42    }
43
44    /// 构建完整的 Key(添加命名空间前缀)
45    /// 优化:使用 String::with_capacity 预分配容量,减少内存分配
46    fn build_key(&self, business_key: &str) -> Result<String, CacheError> {
47        // 验证业务 Key 格式
48        self.validate_business_key(business_key)?;
49
50        // 优化:预分配容量,避免多次分配
51        let capacity = self.namespace.len() + business_key.len();
52        let mut full_key = String::with_capacity(capacity);
53        full_key.push_str(&self.namespace);
54        full_key.push_str(business_key);
55        Ok(full_key)
56    }
57
58    /// 验证业务 Key 格式
59    /// 优化:提前返回,减少不必要的检查
60    fn validate_business_key(&self, key: &str) -> Result<(), CacheError> {
61        // 1. 不能为空(最快检查,优先执行)
62        if key.is_empty() {
63            return Err(CacheError::InvalidKey("Key 不能为空".to_string()));
64        }
65
66        // 2. 长度限制(快速检查,避免后续复杂操作)
67        if key.len() > 200 {
68            return Err(CacheError::InvalidKey(
69                "Key 长度不能超过 200 字符".to_string(),
70            ));
71        }
72
73        // 3. 不能包含命名空间分隔符(防止绕过隔离)
74        // 检查是否包含 "plugin:" 模式,这是命名空间的关键部分
75        if key.contains("plugin:") {
76            return Err(CacheError::InvalidKey(
77                "Key 不能包含命名空间前缀".to_string(),
78            ));
79        }
80
81        // 4. 字符限制(只允许字母、数字、下划线、连字符、冒号)
82        // 优化:使用字节检查,比字符迭代更快
83        if !key.as_bytes().iter().all(|&b| {
84            b.is_ascii_alphanumeric() || b == b'_' || b == b'-' || b == b':'
85        }) {
86            return Err(CacheError::InvalidKey(
87                "Key 包含非法字符,只允许字母、数字、下划线、连字符、冒号".to_string(),
88            ));
89        }
90
91        Ok(())
92    }
93
94    /// 验证权限(确保只能操作自己的 Key)
95    fn verify_permission(&self, full_key: &str) -> Result<(), CacheError> {
96        if !full_key.starts_with(&self.namespace) {
97            return Err(CacheError::PermissionDenied(
98                "Key 不属于当前插件".to_string(),
99            ));
100        }
101        Ok(())
102    }
103}
104
105#[async_trait]
106impl Cache for PluginMemoryCache {
107    async fn get<T>(&self, key: &str) -> Result<Option<T>, CacheError>
108    where
109        T: DeserializeOwned,
110    {
111        let full_key = self.build_key(key)?;
112        let cache = self.manager.get_cache();
113
114        // 从内存缓存获取值
115        let json_value = cache.get(&full_key).await;
116
117        // 反序列化
118        match json_value {
119            Some(json) => {
120                let value: T = serde_json::from_str(&json)
121                    .map_err(|e| CacheError::DeserializationFailed(e.to_string()))?;
122                Ok(Some(value))
123            }
124            None => Ok(None),
125        }
126    }
127
128    async fn set<T>(&self, key: &str, value: &T, _ttl: Option<Duration>) -> Result<(), CacheError>
129    where
130        T: Serialize + Sync,
131    {
132        // 优化:先构建 key,避免序列化失败后浪费计算
133        let full_key = self.build_key(key)?;
134        
135        // 序列化为 JSON 字符串
136        let json_value = serde_json::to_string(value)
137            .map_err(|e| CacheError::SerializationFailed(e.to_string()))?;
138
139        let cache = self.manager.get_cache();
140
141        // moka 的 future::Cache 不支持在 insert 时指定 TTL
142        // 我们使用全局 TTL 配置,忽略传入的 ttl 参数
143        // 注意:如果需要支持每个条目独立的 TTL,需要实现 Expiry trait
144        cache.insert(full_key.clone(), json_value).await;
145
146        // 更新索引(优化:DashMap 操作是同步的)
147        self.manager
148            .add_key_to_index(&self.plugin_id, &full_key);
149
150        Ok(())
151    }
152
153    async fn delete(&self, key: &str) -> Result<bool, CacheError> {
154        let full_key = self.build_key(key)?;
155
156        // 验证权限
157        self.verify_permission(&full_key)?;
158
159        let cache = self.manager.get_cache();
160
161        // 删除 Key
162        let deleted = cache.remove(&full_key).await.is_some();
163
164        // 从索引中移除(优化:DashMap 操作是同步的,无需 await)
165        if deleted {
166            self.manager
167                .remove_key_from_index(&self.plugin_id, &full_key);
168        }
169
170        Ok(deleted)
171    }
172
173    async fn exists(&self, key: &str) -> Result<bool, CacheError> {
174        let full_key = self.build_key(key)?;
175        let cache = self.manager.get_cache();
176
177        // 通过 get 方法检查 Key 是否存在(避免使用同步的 contains_key)
178        let exists = cache.get(&full_key).await.is_some();
179
180        Ok(exists)
181    }
182
183    async fn expire(&self, key: &str, _ttl: Duration) -> Result<bool, CacheError> {
184        let full_key = self.build_key(key)?;
185        let cache = self.manager.get_cache();
186
187        // moka 的 future::Cache 不支持直接更新 TTL
188        // 我们通过重新插入来"续期"(使用全局 TTL 配置)
189        // 获取当前值并重新插入
190        if let Some(value) = cache.get(&full_key).await {
191            cache.insert(full_key, value).await;
192            Ok(true)
193        } else {
194            Ok(false)
195        }
196    }
197
198    async fn ttl(&self, key: &str) -> Result<Option<Duration>, CacheError> {
199        let full_key = self.build_key(key)?;
200        let cache = self.manager.get_cache();
201
202        // moka 不直接支持获取剩余 TTL,返回 None 表示存在但无法获取精确时间
203        // 或者可以通过其他方式实现(如维护一个 TTL 映射表)
204        // 这里简化处理:通过 get 检查 Key 是否存在
205        if cache.get(&full_key).await.is_some() {
206            // moka 不支持直接获取 TTL,返回 None 表示存在但无法确定剩余时间
207            Ok(None)
208        } else {
209            Ok(None) // Key 不存在
210        }
211    }
212
213    async fn clear(&self) -> Result<u64, CacheError> {
214        // 清理当前插件的所有缓存
215        self.manager.clear_plugin(&self.plugin_id).await
216    }
217
218    async fn clear_module(&self, module: &str) -> Result<u64, CacheError> {
219        // 清理当前插件的指定模块缓存
220        self.manager.clear_module(&self.plugin_id, module).await
221    }
222}