File size: 4,333 Bytes
f171e60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
from typing import Optional, Any, Dict
import json
import redis.asyncio as redis
from app.core.cache.base import BaseCache
from app.config import settings
from app.utils.logger import logger

class RedisCache(BaseCache):
    """
    Redis cache implementation
    """
    
    def __init__(self):
        """Initialize Redis cache"""
        self.ttl = settings.CACHE_TTL
        self._client: Optional[redis.Redis] = None
    
    async def _get_client(self) -> redis.Redis:
        """Get hoặc create Redis client"""
        if self._client is None:
            try:
                self._client = redis.Redis(
                    host=settings.REDIS_HOST,
                    port=settings.REDIS_PORT,
                    db=settings.REDIS_DB,
                    password=settings.REDIS_PASSWORD,
                    decode_responses=True
                )
                # Test connection
                await self._client.ping()
                logger.info(
                    f"Redis cache initialized",
                    extra={
                        "host": settings.REDIS_HOST,
                        "port": settings.REDIS_PORT
                    }
                )
            except Exception as e:
                logger.error(f"Redis connection failed: {e}")
                raise
        
        return self._client
    
    async def get(self, key: str) -> Optional[Any]:
        """Get value từ Redis"""
        try:
            client = await self._get_client()
            value = await client.get(key)
            
            if value is not None:
                logger.debug(f"Redis cache hit: {key}")
                return json.loads(value)
            else:
                logger.debug(f"Redis cache miss: {key}")
                return None
        except Exception as e:
            logger.error(f"Redis get error: {e}")
            return None
    
    async def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool:
        """Set value vào Redis"""
        try:
            client = await self._get_client()
            ttl = ttl or self.ttl
            
            # Serialize value to JSON
            serialized = json.dumps(value)
            
            await client.setex(key, ttl, serialized)
            logger.debug(f"Redis cache set: {key} (TTL: {ttl}s)")
            return True
        except Exception as e:
            logger.error(f"Redis set error: {e}")
            return False
    
    async def delete(self, key: str) -> bool:
        """Delete key từ Redis"""
        try:
            client = await self._get_client()
            result = await client.delete(key)
            logger.debug(f"Redis cache delete: {key}")
            return result > 0
        except Exception as e:
            logger.error(f"Redis delete error: {e}")
            return False
    
    async def clear(self) -> bool:
        """Clear toàn bộ cache (FLUSHDB)"""
        try:
            client = await self._get_client()
            await client.flushdb()
            logger.info("Redis cache cleared")
            return True
        except Exception as e:
            logger.error(f"Redis clear error: {e}")
            return False
    
    async def exists(self, key: str) -> bool:
        """Check key tồn tại"""
        try:
            client = await self._get_client()
            result = await client.exists(key)
            return result > 0
        except Exception as e:
            logger.error(f"Redis exists error: {e}")
            return False
    
    async def get_stats(self) -> Dict[str, Any]:
        """Get Redis statistics"""
        try:
            client = await self._get_client()
            info = await client.info()
            return {
                "type": "redis",
                "connected": True,
                "keys": await client.dbsize(),
                "memory_used": info.get("used_memory_human"),
                "uptime": info.get("uptime_in_seconds")
            }
        except Exception as e:
            logger.error(f"Redis stats error: {e}")
            return {"type": "redis", "connected": False, "error": str(e)}
    
    async def close(self):
        """Close Redis connection"""
        if self._client:
            await self._client.close()
            logger.info("Redis connection closed")