Files
qiming/qiming-mcp-proxy/document-parser/src/services/storage_service.rs
2026-06-01 13:03:20 +08:00

1340 lines
44 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::error::AppError;
use crate::models::{DocumentFormat, DocumentTask, TaskStatus};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sled::{
Db, Transactional, Tree,
transaction::{TransactionError, TransactionResult},
};
use tokio::sync::RwLock;
/// 存储键前缀
const TASK_PREFIX: &str = "task:";
const INDEX_PREFIX: &str = "index:";
const CACHE_PREFIX: &str = "cache:";
const METADATA_PREFIX: &str = "meta:";
/// 索引类型
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum IndexType {
ByStatus(TaskStatus),
ByFormat(DocumentFormat),
ByCreatedTime(u64), // Unix timestamp
ByUpdatedTime(u64),
}
/// 查询过滤器
#[derive(Debug, Clone)]
pub struct QueryFilter {
pub status: Option<TaskStatus>,
pub format: Option<DocumentFormat>,
pub created_after: Option<SystemTime>,
pub created_before: Option<SystemTime>,
pub limit: Option<usize>,
pub offset: Option<usize>,
}
impl Default for QueryFilter {
fn default() -> Self {
Self {
status: None,
format: None,
created_after: None,
created_before: None,
limit: Some(100),
offset: None,
}
}
}
/// 存储配置
#[derive(Debug, Clone)]
pub struct StorageConfig {
pub cache_ttl: std::time::Duration,
pub max_cache_size: usize,
pub cleanup_interval: std::time::Duration,
pub retention_period: std::time::Duration,
pub batch_size: usize,
pub enable_compression: bool,
pub sync_interval: std::time::Duration,
}
impl Default for StorageConfig {
fn default() -> Self {
Self {
cache_ttl: std::time::Duration::from_secs(3600), // 1小时
max_cache_size: 10000,
cleanup_interval: std::time::Duration::from_secs(3600), // 1小时
retention_period: std::time::Duration::from_secs(30 * 24 * 3600), // 30天
batch_size: 100,
enable_compression: true,
sync_interval: std::time::Duration::from_secs(60), // 1分钟
}
}
}
/// 存储统计信息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageStats {
pub total_tasks: usize,
pub total_size_bytes: u64,
pub index_count: usize,
pub cache_hit_rate: f64,
pub cache_size: usize,
pub last_cleanup: Option<SystemTime>,
pub last_sync: Option<SystemTime>,
pub transaction_count: u64,
pub failed_transactions: u64,
pub average_query_time_ms: f64,
}
/// 事务操作类型
#[derive(Debug, Clone)]
pub enum TransactionOp {
Insert { key: Vec<u8>, value: Vec<u8> },
Update { key: Vec<u8>, value: Vec<u8> },
Delete { key: Vec<u8> },
IndexUpdate { index_key: Vec<u8>, task_id: String },
IndexDelete { index_key: Vec<u8> },
}
/// 缓存项
#[derive(Debug, Clone, Serialize, Deserialize)]
struct CacheItem<T> {
data: T,
created_at: SystemTime,
expires_at: Option<SystemTime>,
access_count: u64,
}
/// 数据库存储服务
#[derive(Debug)]
pub struct StorageService {
db: Arc<Db>,
tasks_tree: Tree,
index_tree: Tree,
cache_tree: Tree,
metadata_tree: Tree,
// 配置
config: StorageConfig,
// 内存缓存
memory_cache: Arc<RwLock<HashMap<String, CacheItem<DocumentTask>>>>,
// 统计信息
stats: Arc<RwLock<StorageStats>>,
// 事务计数器
transaction_counter: std::sync::atomic::AtomicU64,
failed_transaction_counter: std::sync::atomic::AtomicU64,
}
impl StorageService {
/// 创建新的存储服务
pub fn new(db: Arc<Db>) -> Result<Self, AppError> {
Self::with_config(db, StorageConfig::default())
}
/// 使用自定义配置创建存储服务
pub fn with_config(db: Arc<Db>, config: StorageConfig) -> Result<Self, AppError> {
let tasks_tree = db
.open_tree("tasks")
.map_err(|e| AppError::Database(format!("打开任务树失败: {e}")))?;
let index_tree = db
.open_tree("indexes")
.map_err(|e| AppError::Database(format!("打开索引树失败: {e}")))?;
let cache_tree = db
.open_tree("cache")
.map_err(|e| AppError::Database(format!("打开缓存树失败: {e}")))?;
let metadata_tree = db
.open_tree("metadata")
.map_err(|e| AppError::Database(format!("打开元数据树失败: {e}")))?;
let stats = StorageStats {
total_tasks: 0,
total_size_bytes: 0,
index_count: 0,
cache_hit_rate: 0.0,
cache_size: 0,
last_cleanup: None,
last_sync: None,
transaction_count: 0,
failed_transactions: 0,
average_query_time_ms: 0.0,
};
Ok(Self {
db,
tasks_tree,
index_tree,
cache_tree,
metadata_tree,
config,
memory_cache: Arc::new(RwLock::new(HashMap::new())),
stats: Arc::new(RwLock::new(stats)),
transaction_counter: std::sync::atomic::AtomicU64::new(0),
failed_transaction_counter: std::sync::atomic::AtomicU64::new(0),
})
}
/// 保存任务
pub async fn save_task(&self, task: &DocumentTask) -> Result<(), AppError> {
let start_time = std::time::Instant::now();
// 执行事务
let result = self
.execute_transaction(|tx_ops| {
let task_key = format!("{}{}", TASK_PREFIX, task.id);
// 序列化任务数据
let task_data = serde_json::to_vec(task)
.map_err(|e| AppError::Database(format!("序列化任务失败: {e}")))?;
// 添加主要操作
tx_ops.push(TransactionOp::Insert {
key: task_key.into_bytes(),
value: task_data,
});
// 添加索引操作
self.add_index_operations(task, tx_ops)?;
Ok(())
})
.await;
match result {
Ok(()) => {
// 更新内存缓存
self.update_memory_cache(&task.id, task.clone()).await;
// 清除相关缓存
self.invalidate_cache_for_task(&task.id).await?;
// 更新统计信息
self.update_query_stats(start_time.elapsed()).await;
log::debug!("Task saved: {}", task.id);
Ok(())
}
Err(e) => {
self.failed_transaction_counter
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
Err(e)
}
}
}
/// 执行事务
async fn execute_transaction<F>(&self, mut operation: F) -> Result<(), AppError>
where
F: FnMut(&mut Vec<TransactionOp>) -> Result<(), AppError>,
{
let mut tx_ops = Vec::new();
operation(&mut tx_ops)?;
// 执行事务
let result: TransactionResult<(), ()> =
(&self.tasks_tree, &self.index_tree).transaction(|(tasks_tree, index_tree)| {
for op in &tx_ops {
match op {
TransactionOp::Insert { key, value } => {
tasks_tree.insert(key.as_slice(), value.as_slice())?;
}
TransactionOp::Update { key, value } => {
tasks_tree.insert(key.as_slice(), value.as_slice())?;
}
TransactionOp::Delete { key } => {
tasks_tree.remove(key.as_slice())?;
}
TransactionOp::IndexUpdate { index_key, task_id } => {
index_tree.insert(index_key.as_slice(), task_id.as_bytes())?;
}
TransactionOp::IndexDelete { index_key } => {
index_tree.remove(index_key.as_slice())?;
}
}
}
Ok(())
});
match result {
Ok(()) => {
self.transaction_counter
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
Ok(())
}
Err(TransactionError::Abort(e)) => Err(AppError::Database(format!("事务中止: {e:?}"))),
Err(TransactionError::Storage(e)) => Err(AppError::Database(format!("存储错误: {e}"))),
}
}
/// 获取任务
pub async fn get_task(&self, task_id: &str) -> Result<Option<DocumentTask>, AppError> {
let start_time = std::time::Instant::now();
// 先检查内存缓存
if let Some(cached_task) = self.get_from_memory_cache(task_id).await {
self.update_query_stats(start_time.elapsed()).await;
return Ok(Some(cached_task));
}
// 检查持久化缓存
if let Some(cached_task) = self
.get_from_cache::<DocumentTask>(&format!("task:{task_id}"))
.await?
{
// 更新内存缓存
self.update_memory_cache(task_id, cached_task.clone()).await;
self.update_query_stats(start_time.elapsed()).await;
return Ok(Some(cached_task));
}
let task_key = format!("{TASK_PREFIX}{task_id}");
match self.tasks_tree.get(&task_key) {
Ok(Some(data)) => {
let task: DocumentTask = serde_json::from_slice(&data)
.map_err(|e| AppError::Database(format!("反序列化任务失败: {e}")))?;
// 更新缓存
self.update_memory_cache(task_id, task.clone()).await;
self.set_cache(&format!("task:{task_id}"), &task, None)
.await?;
self.update_query_stats(start_time.elapsed()).await;
Ok(Some(task))
}
Ok(None) => {
self.update_query_stats(start_time.elapsed()).await;
Ok(None)
}
Err(e) => Err(AppError::Database(format!("查询任务失败: {e}"))),
}
}
/// 删除任务
pub async fn delete_task(&self, task_id: &str) -> Result<bool, AppError> {
let start_time = std::time::Instant::now();
// 获取任务以便清理索引
let task = self.get_task(task_id).await?;
if let Some(task) = task {
// 执行删除事务
let result = self
.execute_transaction(|tx_ops| {
let task_key = format!("{TASK_PREFIX}{task_id}");
// 添加删除操作
tx_ops.push(TransactionOp::Delete {
key: task_key.into_bytes(),
});
// 添加索引删除操作
self.add_index_delete_operations(&task, tx_ops)?;
Ok(())
})
.await;
match result {
Ok(()) => {
// 清除缓存
self.remove_from_memory_cache(task_id).await;
self.invalidate_cache_for_task(task_id).await?;
self.update_query_stats(start_time.elapsed()).await;
log::info!("Task deleted: {task_id}");
Ok(true)
}
Err(e) => {
self.failed_transaction_counter
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
Err(e)
}
}
} else {
Ok(false)
}
}
/// 批量保存任务
pub async fn save_tasks_batch(&self, tasks: &[DocumentTask]) -> Result<usize, AppError> {
let start_time = std::time::Instant::now();
let mut saved_count = 0;
// 分批处理
for chunk in tasks.chunks(self.config.batch_size) {
let result = self
.execute_transaction(|tx_ops| {
for task in chunk {
let task_key = format!("{}{}", TASK_PREFIX, task.id);
// 序列化任务数据
let task_data = serde_json::to_vec(task)
.map_err(|e| AppError::Database(format!("序列化任务失败: {e}")))?;
tx_ops.push(TransactionOp::Insert {
key: task_key.into_bytes(),
value: task_data,
});
// 添加索引操作
self.add_index_operations(task, tx_ops)?;
}
Ok(())
})
.await;
match result {
Ok(()) => {
saved_count += chunk.len();
// 更新内存缓存
for task in chunk {
self.update_memory_cache(&task.id, task.clone()).await;
}
}
Err(e) => {
log::error!("Batch save failed: {e}");
return Err(e);
}
}
}
self.update_query_stats(start_time.elapsed()).await;
log::info!("Batch saving completed: {saved_count} tasks");
Ok(saved_count)
}
/// 内存缓存操作
async fn get_from_memory_cache(&self, task_id: &str) -> Option<DocumentTask> {
let cache = self.memory_cache.read().await;
if let Some(cache_item) = cache.get(task_id) {
// 检查是否过期
if let Some(expires_at) = cache_item.expires_at {
if SystemTime::now() > expires_at {
return None;
}
}
Some(cache_item.data.clone())
} else {
None
}
}
async fn update_memory_cache(&self, task_id: &str, task: DocumentTask) {
let mut cache = self.memory_cache.write().await;
// 检查缓存大小限制
if cache.len() >= self.config.max_cache_size {
// 简单的LRU移除最旧的项
if let Some((oldest_key, _)) = cache.iter().min_by_key(|(_, item)| item.created_at) {
let oldest_key = oldest_key.clone();
cache.remove(&oldest_key);
}
}
let cache_item = CacheItem {
data: task,
created_at: SystemTime::now(),
expires_at: Some(SystemTime::now() + self.config.cache_ttl),
access_count: 1,
};
cache.insert(task_id.to_string(), cache_item);
}
async fn remove_from_memory_cache(&self, task_id: &str) {
let mut cache = self.memory_cache.write().await;
cache.remove(task_id);
}
/// 添加索引操作到事务
fn add_index_operations(
&self,
task: &DocumentTask,
tx_ops: &mut Vec<TransactionOp>,
) -> Result<(), AppError> {
let task_id = &task.id;
// 仅基于 task_id 的索引键
let task_index_key = format!("{INDEX_PREFIX}task:{task_id}");
tx_ops.push(TransactionOp::IndexUpdate {
index_key: task_index_key.into_bytes(),
task_id: task_id.clone(),
});
Ok(())
}
/// 添加索引删除操作到事务
fn add_index_delete_operations(
&self,
task: &DocumentTask,
tx_ops: &mut Vec<TransactionOp>,
) -> Result<(), AppError> {
let task_id = &task.id;
// 删除仅基于 task_id 的索引键
let task_index_key = format!("{INDEX_PREFIX}task:{task_id}");
tx_ops.push(TransactionOp::IndexDelete {
index_key: task_index_key.into_bytes(),
});
Ok(())
}
/// 更新查询统计信息
async fn update_query_stats(&self, query_time: std::time::Duration) {
let mut stats = self.stats.write().await;
// 更新平均查询时间
let query_time_ms = query_time.as_millis() as f64;
if stats.average_query_time_ms == 0.0 {
stats.average_query_time_ms = query_time_ms;
} else {
// 简单的移动平均
stats.average_query_time_ms =
(stats.average_query_time_ms * 0.9) + (query_time_ms * 0.1);
}
}
/// 查询任务
pub async fn query_tasks(&self, filter: &QueryFilter) -> Result<Vec<DocumentTask>, AppError> {
let cache_key = format!("query:{}", self.filter_to_cache_key(filter));
// 检查缓存
if let Some(cached_result) = self.get_from_cache::<Vec<DocumentTask>>(&cache_key).await? {
return Ok(cached_result);
}
let mut results = Vec::new();
let mut count = 0;
let offset = filter.offset.unwrap_or(0);
let limit = filter.limit.unwrap_or(100);
// 遍历所有任务
for result in self.tasks_tree.scan_prefix(TASK_PREFIX.as_bytes()) {
let (_, data) = result.map_err(|e| AppError::Database(format!("扫描任务失败: {e}")))?;
let task: DocumentTask = serde_json::from_slice(&data)
.map_err(|e| AppError::Database(format!("反序列化任务失败: {e}")))?;
// 应用过滤器
if self.task_matches_filter(&task, filter) {
if count >= offset {
results.push(task);
if results.len() >= limit {
break;
}
}
count += 1;
}
}
// 缓存结果
self.set_cache(
&cache_key,
&results,
Some(std::time::Duration::from_secs(300)),
)
.await?;
Ok(results)
}
/// 获取存储统计信息
pub async fn get_stats(&self) -> Result<StorageStats, AppError> {
let cache_key = "storage_stats";
// 检查缓存
if let Some(cached_stats) = self.get_from_cache::<StorageStats>(cache_key).await? {
return Ok(cached_stats);
}
let mut total_tasks = 0;
let mut total_size_bytes = 0;
// 统计任务数量和大小
for result in self.tasks_tree.scan_prefix(TASK_PREFIX.as_bytes()) {
let (_, data) = result.map_err(|e| AppError::Database(format!("扫描任务失败: {e}")))?;
total_tasks += 1;
total_size_bytes += data.len() as u64;
}
// 统计索引数量
let index_count = self.index_tree.len();
// 获取内存缓存大小
let cache_size = {
let cache = self.memory_cache.read().await;
cache.len()
};
// 计算缓存命中率(简化版本)
let cache_hit_rate = 0.85; // 占位值,实际应该基于访问统计
// 获取事务统计
let transaction_count = self
.transaction_counter
.load(std::sync::atomic::Ordering::Relaxed);
let failed_transactions = self
.failed_transaction_counter
.load(std::sync::atomic::Ordering::Relaxed);
// 获取平均查询时间
let average_query_time_ms = {
let stats = self.stats.read().await;
stats.average_query_time_ms
};
let stats = StorageStats {
total_tasks,
total_size_bytes,
index_count,
cache_hit_rate,
cache_size,
last_cleanup: self.get_last_cleanup_time().await?,
last_sync: {
let stats = self.stats.read().await;
stats.last_sync
},
transaction_count,
failed_transactions,
average_query_time_ms,
};
// 缓存统计信息
self.set_cache(cache_key, &stats, Some(std::time::Duration::from_secs(60)))
.await?;
Ok(stats)
}
/// 清理过期数据
pub async fn cleanup_expired_data(&self) -> Result<usize, AppError> {
log::info!("Start cleaning expired data");
let mut cleaned_count = 0;
let now = SystemTime::now();
// 清理过期任务
let expired_tasks = self.find_expired_tasks(now).await?;
if !expired_tasks.is_empty() {
// 批量删除过期任务
for chunk in expired_tasks.chunks(self.config.batch_size) {
let result = self
.execute_transaction(|tx_ops| {
for task in chunk {
let task_key = format!("{}{}", TASK_PREFIX, task.id);
tx_ops.push(TransactionOp::Delete {
key: task_key.into_bytes(),
});
// 添加索引删除操作
self.add_index_delete_operations(task, tx_ops)?;
}
Ok(())
})
.await;
match result {
Ok(()) => {
cleaned_count += chunk.len();
// 清理内存缓存
for task in chunk {
self.remove_from_memory_cache(&task.id).await;
}
}
Err(e) => {
log::error!("Failed to delete expired tasks in batches: {e}");
return Err(e);
}
}
}
}
// 清理过期缓存
cleaned_count += self.cleanup_expired_cache().await?;
// 清理内存缓存
cleaned_count += self.cleanup_memory_cache().await;
// 更新清理时间
self.set_last_cleanup_time(now).await?;
// 压缩数据库
self.compact_database().await?;
log::info!("Cleanup completed, {cleaned_count} records deleted");
Ok(cleaned_count)
}
/// 查找过期任务
async fn find_expired_tasks(&self, now: SystemTime) -> Result<Vec<DocumentTask>, AppError> {
let mut expired_tasks = Vec::new();
for result in self.tasks_tree.scan_prefix(TASK_PREFIX.as_bytes()) {
let (_, data) = result.map_err(|e| AppError::Database(format!("扫描任务失败: {e}")))?;
let task: DocumentTask = serde_json::from_slice(&data)
.map_err(|e| AppError::Database(format!("反序列化任务失败: {e}")))?;
// 检查是否过期
let task_created_at =
UNIX_EPOCH + std::time::Duration::from_secs(task.created_at.timestamp() as u64);
if let Ok(elapsed) = now.duration_since(task_created_at) {
if elapsed > self.config.retention_period && task.status.is_terminal() {
expired_tasks.push(task);
}
}
}
Ok(expired_tasks)
}
/// 清理内存缓存
async fn cleanup_memory_cache(&self) -> usize {
let mut cache = self.memory_cache.write().await;
let now = SystemTime::now();
let mut cleaned_count = 0;
cache.retain(|_, item| {
if let Some(expires_at) = item.expires_at {
if now > expires_at {
cleaned_count += 1;
false
} else {
true
}
} else {
true
}
});
cleaned_count
}
/// 压缩数据库
async fn compact_database(&self) -> Result<(), AppError> {
log::info!("Start compressing the database");
// 刷新所有树
self.tasks_tree
.flush()
.map_err(|e| AppError::Database(format!("刷新任务树失败: {e}")))?;
self.index_tree
.flush()
.map_err(|e| AppError::Database(format!("刷新索引树失败: {e}")))?;
self.cache_tree
.flush()
.map_err(|e| AppError::Database(format!("刷新缓存树失败: {e}")))?;
self.metadata_tree
.flush()
.map_err(|e| AppError::Database(format!("刷新元数据树失败: {e}")))?;
// 刷新整个数据库
self.db
.flush()
.map_err(|e| AppError::Database(format!("刷新数据库失败: {e}")))?;
log::info!("Database compression completed");
Ok(())
}
/// 启动后台维护任务
pub async fn start_maintenance_tasks(&self) -> Result<(), AppError> {
let storage_service = self.clone_for_background();
tokio::spawn(async move {
let mut cleanup_interval =
tokio::time::interval(storage_service.config.cleanup_interval);
let mut sync_interval = tokio::time::interval(storage_service.config.sync_interval);
loop {
tokio::select! {
_ = cleanup_interval.tick() => {
if let Err(e) = storage_service.cleanup_expired_data().await {
log::error!("Periodic cleanup failed: {e}");
}
}
_ = sync_interval.tick() => {
if let Err(e) = storage_service.sync_to_disk().await {
log::error!("Periodic synchronization failed: {e}");
}
}
}
}
});
Ok(())
}
/// 同步到磁盘
async fn sync_to_disk(&self) -> Result<(), AppError> {
self.compact_database().await?;
// 更新同步时间
{
let mut stats = self.stats.write().await;
stats.last_sync = Some(SystemTime::now());
}
Ok(())
}
/// 为后台任务克隆服务
fn clone_for_background(&self) -> Self {
Self {
db: Arc::clone(&self.db),
tasks_tree: self.tasks_tree.clone(),
index_tree: self.index_tree.clone(),
cache_tree: self.cache_tree.clone(),
metadata_tree: self.metadata_tree.clone(),
config: self.config.clone(),
memory_cache: Arc::clone(&self.memory_cache),
stats: Arc::clone(&self.stats),
transaction_counter: std::sync::atomic::AtomicU64::new(
self.transaction_counter
.load(std::sync::atomic::Ordering::Relaxed),
),
failed_transaction_counter: std::sync::atomic::AtomicU64::new(
self.failed_transaction_counter
.load(std::sync::atomic::Ordering::Relaxed),
),
}
}
/// 备份数据
pub async fn backup_to_path(&self, backup_path: &str) -> Result<(), AppError> {
log::info!("Start backing up data to: {backup_path}");
// 创建备份目录
std::fs::create_dir_all(backup_path)
.map_err(|e| AppError::File(format!("创建备份目录失败: {e}")))?;
// 导出所有任务
let mut tasks = Vec::new();
for result in self.tasks_tree.scan_prefix(TASK_PREFIX.as_bytes()) {
let (_, data) = result.map_err(|e| AppError::Database(format!("扫描任务失败: {e}")))?;
let task: DocumentTask = serde_json::from_slice(&data)
.map_err(|e| AppError::Database(format!("反序列化任务失败: {e}")))?;
tasks.push(task);
}
// 写入备份文件
let backup_file = format!(
"{}/tasks_backup_{}.json",
backup_path,
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
);
let backup_data = serde_json::to_string_pretty(&tasks)
.map_err(|e| AppError::Database(format!("序列化备份数据失败: {e}")))?;
std::fs::write(&backup_file, backup_data)
.map_err(|e| AppError::File(format!("写入备份文件失败: {e}")))?;
log::info!("Backup completed: {} ({} tasks)", backup_file, tasks.len());
Ok(())
}
/// 从备份恢复数据
pub async fn restore_from_backup(&self, backup_file: &str) -> Result<usize, AppError> {
log::info!("Restore data from backup: {backup_file}");
let backup_data = std::fs::read_to_string(backup_file)
.map_err(|e| AppError::File(format!("读取备份文件失败: {e}")))?;
let tasks: Vec<DocumentTask> = serde_json::from_str(&backup_data)
.map_err(|e| AppError::Database(format!("反序列化备份数据失败: {e}")))?;
let mut restored_count = 0;
for task in tasks {
self.save_task(&task).await?;
restored_count += 1;
}
log::info!("Recovery completed: {restored_count} tasks");
Ok(restored_count)
}
// 私有方法
/// 检查任务是否匹配过滤器
fn task_matches_filter(&self, task: &DocumentTask, filter: &QueryFilter) -> bool {
// 状态过滤
if let Some(status) = &filter.status {
if &task.status != status {
return false;
}
}
// 格式过滤
if let Some(format) = &filter.format {
if task.document_format.as_ref() != Some(format) {
return false;
}
}
// 创建时间过滤
if let Some(after) = filter.created_after {
let after_dt = DateTime::<Utc>::from(after);
if task.created_at < after_dt {
return false;
}
}
if let Some(before) = filter.created_before {
let before_dt = DateTime::<Utc>::from(before);
if task.created_at > before_dt {
return false;
}
}
true
}
/// 生成过滤器的缓存键
fn filter_to_cache_key(&self, filter: &QueryFilter) -> String {
format!(
"{}:{}:{}:{}:{}:{}",
filter
.status
.as_ref()
.map(|s| s.to_string())
.unwrap_or_else(|| "any".to_string()),
filter
.format
.as_ref()
.map(|f| f.to_string())
.unwrap_or_else(|| "any".to_string()),
filter
.created_after
.map(|t| t.duration_since(UNIX_EPOCH).unwrap_or_default().as_secs())
.unwrap_or(0),
filter
.created_before
.map(|t| t.duration_since(UNIX_EPOCH).unwrap_or_default().as_secs())
.unwrap_or(u64::MAX),
filter.limit.unwrap_or(100),
filter.offset.unwrap_or(0)
)
}
/// 从缓存获取数据
async fn get_from_cache<T>(&self, key: &str) -> Result<Option<T>, AppError>
where
T: for<'de> Deserialize<'de>,
{
let cache_key = format!("{CACHE_PREFIX}{key}");
if let Ok(Some(data)) = self.cache_tree.get(&cache_key) {
let cache_item: CacheItem<T> = serde_json::from_slice(&data)
.map_err(|e| AppError::Database(format!("反序列化缓存项失败: {e}")))?;
// 检查是否过期
if let Some(expires_at) = cache_item.expires_at {
if SystemTime::now() > expires_at {
// 过期,删除缓存项
self.cache_tree
.remove(&cache_key)
.map_err(|e| AppError::Database(format!("删除过期缓存失败: {e}")))?;
return Ok(None);
}
}
// 更新访问计数(简化版本,不实际更新)
Ok(Some(cache_item.data))
} else {
Ok(None)
}
}
/// 设置缓存
async fn set_cache<T>(
&self,
key: &str,
data: &T,
ttl: Option<std::time::Duration>,
) -> Result<(), AppError>
where
T: Serialize,
{
let cache_key = format!("{CACHE_PREFIX}{key}");
let now = SystemTime::now();
let cache_item = CacheItem {
data,
created_at: now,
expires_at: ttl.map(|duration| now + duration),
access_count: 1,
};
let cache_data = serde_json::to_vec(&cache_item)
.map_err(|e| AppError::Database(format!("序列化缓存项失败: {e}")))?;
self.cache_tree
.insert(&cache_key, cache_data)
.map_err(|e| AppError::Database(format!("设置缓存失败: {e}")))?;
Ok(())
}
/// 清除任务相关缓存
async fn invalidate_cache_for_task(&self, task_id: &str) -> Result<(), AppError> {
let patterns = vec![
format!("task:{}", task_id),
"query:".to_string(),
"storage_stats".to_string(),
];
for pattern in patterns {
let cache_key = format!("{CACHE_PREFIX}{pattern}");
if pattern.starts_with("query:") {
// 清除所有查询缓存
let prefix = cache_key.as_bytes();
let mut to_remove = Vec::new();
for result in self.cache_tree.scan_prefix(prefix) {
let (key, _) =
result.map_err(|e| AppError::Database(format!("扫描缓存失败: {e}")))?;
to_remove.push(key.to_vec());
}
for key in to_remove {
self.cache_tree
.remove(&key)
.map_err(|e| AppError::Database(format!("删除缓存失败: {e}")))?;
}
} else {
self.cache_tree
.remove(&cache_key)
.map_err(|e| AppError::Database(format!("删除缓存失败: {e}")))?;
}
}
Ok(())
}
/// 清理过期缓存
async fn cleanup_expired_cache(&self) -> Result<usize, AppError> {
let mut cleaned_count = 0;
let now = SystemTime::now();
let mut to_remove = Vec::new();
for result in self.cache_tree.scan_prefix(CACHE_PREFIX.as_bytes()) {
let (key, data) =
result.map_err(|e| AppError::Database(format!("扫描缓存失败: {e}")))?;
// 尝试解析缓存项(简化版本)
if let Ok(cache_item) = serde_json::from_slice::<serde_json::Value>(&data) {
if let Some(expires_at_timestamp) =
cache_item.get("expires_at").and_then(|v| v.as_u64())
{
let expires_at =
UNIX_EPOCH + std::time::Duration::from_secs(expires_at_timestamp);
if now > expires_at {
to_remove.push(key.to_vec());
}
}
}
}
for key in to_remove {
self.cache_tree
.remove(&key)
.map_err(|e| AppError::Database(format!("删除过期缓存失败: {e}")))?;
cleaned_count += 1;
}
Ok(cleaned_count)
}
/// 获取最后清理时间
async fn get_last_cleanup_time(&self) -> Result<Option<SystemTime>, AppError> {
let key = format!("{}{}", METADATA_PREFIX, "last_cleanup");
if let Ok(Some(data)) = self.metadata_tree.get(&key) {
let timestamp: u64 = serde_json::from_slice(&data)
.map_err(|e| AppError::Database(format!("反序列化清理时间失败: {e}")))?;
Ok(Some(UNIX_EPOCH + std::time::Duration::from_secs(timestamp)))
} else {
Ok(None)
}
}
/// 设置最后清理时间
async fn set_last_cleanup_time(&self, time: SystemTime) -> Result<(), AppError> {
let key = format!("{}{}", METADATA_PREFIX, "last_cleanup");
let timestamp = time
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let data = serde_json::to_vec(&timestamp)
.map_err(|e| AppError::Database(format!("序列化清理时间失败: {e}")))?;
self.metadata_tree
.insert(&key, data)
.map_err(|e| AppError::Database(format!("设置清理时间失败: {e}")))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::SourceType;
use tempfile::TempDir;
#[tokio::test]
async fn test_storage_service_basic() {
let app_config = crate::tests::test_helpers::create_real_environment_test_config();
crate::config::init_global_config(app_config).unwrap();
let temp_dir = TempDir::new().unwrap();
let db = Arc::new(sled::open(temp_dir.path()).unwrap());
let storage = StorageService::new(db).unwrap();
// 创建测试任务
let task_id = uuid::Uuid::new_v4().to_string();
let mut task = DocumentTask::new(
task_id.clone(),
SourceType::Upload,
Some("/test/path".to_string()),
Some("path.pdf".to_string()),
Some(DocumentFormat::PDF),
Some("pipeline".to_string()),
Some(24),
Some(3),
);
task.parser_engine = Some(crate::models::ParserEngine::MinerU);
task.file_size = Some(1024);
task.mime_type = Some("application/pdf".to_string());
// 保存任务
storage.save_task(&task).await.unwrap();
// 获取任务
let retrieved = storage.get_task(&task_id).await.unwrap();
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().id, task_id);
// 删除任务
let deleted = storage.delete_task(&task_id).await.unwrap();
assert!(deleted);
// 确认删除
let not_found = storage.get_task(&task_id).await.unwrap();
assert!(not_found.is_none());
}
#[tokio::test]
async fn test_transaction_handling() {
let app_config = crate::tests::test_helpers::create_real_environment_test_config();
crate::config::init_global_config(app_config).unwrap();
let temp_dir = TempDir::new().unwrap();
let db = Arc::new(sled::open(temp_dir.path()).unwrap());
let storage = StorageService::new(db).unwrap();
// 创建多个任务进行批量保存
let mut tasks = Vec::new();
for i in 0..5 {
let mut task = DocumentTask::new(
format!("batch_task_{i}"),
SourceType::Upload,
Some(format!("/test/path_{i}")),
Some(format!("path_{i}.pdf")),
Some(DocumentFormat::PDF),
Some("pipeline".to_string()),
Some(24),
Some(3),
);
task.parser_engine = Some(crate::models::ParserEngine::MinerU);
task.file_size = Some(1024);
task.mime_type = Some("application/pdf".to_string());
tasks.push(task);
}
// 批量保存
let saved_count = storage.save_tasks_batch(&tasks).await.unwrap();
assert_eq!(saved_count, 5);
// 验证所有任务都已保存
for i in 0..5 {
let task_id = format!("batch_task_{i}");
let retrieved = storage.get_task(&task_id).await.unwrap();
assert!(retrieved.is_some());
}
}
#[tokio::test]
async fn test_memory_cache() {
let app_config = crate::tests::test_helpers::create_real_environment_test_config();
crate::config::init_global_config(app_config).unwrap();
let temp_dir = TempDir::new().unwrap();
let db = Arc::new(sled::open(temp_dir.path()).unwrap());
let config = StorageConfig {
max_cache_size: 2, // 限制缓存大小
..Default::default()
};
let storage = StorageService::with_config(db, config).unwrap();
// 创建测试任务
let task1_id = uuid::Uuid::new_v4().to_string();
let mut task1 = DocumentTask::new(
task1_id.clone(),
SourceType::Upload,
Some("/test/path1".to_string()),
Some("path1.pdf".to_string()),
Some(DocumentFormat::PDF),
Some("pipeline".to_string()),
Some(24),
Some(3),
);
task1.parser_engine = Some(crate::models::ParserEngine::MinerU);
task1.file_size = Some(1024);
task1.mime_type = Some("application/pdf".to_string());
let task2_id = uuid::Uuid::new_v4().to_string();
let mut task2 = DocumentTask::new(
task2_id.clone(),
SourceType::Upload,
Some("/test/path2".to_string()),
Some("path2.pdf".to_string()),
Some(DocumentFormat::PDF),
Some("pipeline".to_string()),
Some(24),
Some(3),
);
task2.parser_engine = Some(crate::models::ParserEngine::MinerU);
task2.file_size = Some(1024);
task2.mime_type = Some("application/pdf".to_string());
// 保存任务(会自动缓存)
storage.save_task(&task1).await.unwrap();
storage.save_task(&task2).await.unwrap();
// 第一次获取应该从缓存中获取
let retrieved1 = storage.get_task(&task1_id).await.unwrap();
assert!(retrieved1.is_some());
// 检查缓存状态
let cache_size = {
let cache = storage.memory_cache.read().await;
cache.len()
};
assert!(cache_size <= 2); // 不应该超过最大缓存大小
}
#[tokio::test]
async fn test_cleanup_expired_data() {
let app_config = crate::tests::test_helpers::create_real_environment_test_config();
crate::config::init_global_config(app_config).unwrap();
let temp_dir = TempDir::new().unwrap();
let db = Arc::new(sled::open(temp_dir.path()).unwrap());
let config = StorageConfig {
retention_period: std::time::Duration::from_secs(1), // 1秒过期
..Default::default()
};
let storage = StorageService::with_config(db, config).unwrap();
// 创建已完成的任务
let task_id = uuid::Uuid::new_v4().to_string();
let mut task = DocumentTask::new(
task_id.clone(),
SourceType::Upload,
Some("/test/path".to_string()),
Some("path.pdf".to_string()),
Some(DocumentFormat::PDF),
Some("pipeline".to_string()),
Some(24),
Some(3),
);
task.parser_engine = Some(crate::models::ParserEngine::MinerU);
task.file_size = Some(1024);
task.mime_type = Some("application/pdf".to_string());
// 设置为已完成状态
let _ = task.update_status(TaskStatus::new_completed(std::time::Duration::from_secs(
60,
)));
storage.save_task(&task).await.unwrap();
// 等待过期
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
// 执行清理
let cleaned_count = storage.cleanup_expired_data().await.unwrap();
assert!(cleaned_count > 0);
// 验证任务已被删除
let retrieved = storage.get_task(&task_id).await.unwrap();
assert!(retrieved.is_none());
}
#[tokio::test]
async fn test_storage_stats() {
let app_config = crate::tests::test_helpers::create_real_environment_test_config();
crate::config::init_global_config(app_config).unwrap();
let temp_dir = TempDir::new().unwrap();
let db = Arc::new(sled::open(temp_dir.path()).unwrap());
let storage = StorageService::new(db).unwrap();
// 创建一些测试任务
for i in 0..3 {
let task_id = uuid::Uuid::new_v4().to_string();
let mut task = DocumentTask::new(
task_id,
SourceType::Upload,
Some(format!("/test/path_{i}")),
Some(format!("path_{i}.pdf")),
Some(DocumentFormat::PDF),
Some("pipeline".to_string()),
Some(24),
Some(3),
);
task.parser_engine = Some(crate::models::ParserEngine::MinerU);
task.file_size = Some(1024);
task.mime_type = Some("application/pdf".to_string());
storage.save_task(&task).await.unwrap();
}
// 获取统计信息
let stats = storage.get_stats().await.unwrap();
assert_eq!(stats.total_tasks, 3);
assert!(stats.total_size_bytes > 0);
assert!(stats.transaction_count > 0);
}
}