sanket3280/code-execution
0
1/**2 * Caching Utilities3 * Provides caching helpers for Redis and in-memory caching4 */5 6const logger = require('./logger');7 8/**9 * In-memory cache implementation10 * Used as fallback when Redis is not available11 */12class MemoryCache {13 constructor() {14 this.cache = new Map();15 this.timers = new Map();16 }17 18 /**19 * Set a value in cache with optional TTL20 */21 async set(key, value, ttl = null) {22 try {23 this.cache.set(key, value);24 25 // Clear existing timer if any26 if (this.timers.has(key)) {27 clearTimeout(this.timers.get(key));28 }29 30 // Set expiration timer if TTL provided31 if (ttl) {32 const timer = setTimeout(() => {33 this.cache.delete(key);34 this.timers.delete(key);35 }, ttl * 1000);36 this.timers.set(key, timer);37 }38 39 return true;40 } catch (error) {41 logger.error('Memory cache set error', error);42 return false;43 }44 }45 46 /**47 * Get a value from cache48 */49 async get(key) {50 try {51 return this.cache.get(key) || null;52 } catch (error) {53 logger.error('Memory cache get error', error);54 return null;55 }56 }57 58 /**59 * Delete a value from cache60 */61 async del(key) {62 try {63 if (this.timers.has(key)) {64 clearTimeout(this.timers.get(key));65 this.timers.delete(key);66 }67 return this.cache.delete(key);68 } catch (error) {69 logger.error('Memory cache delete error', error);70 return false;71 }72 }73 74 /**75 * Check if key exists76 */77 async exists(key) {78 return this.cache.has(key);79 }80 81 /**82 * Clear all cache83 */84 async clear() {85 try {86 // Clear all timers87 for (const timer of this.timers.values()) {88 clearTimeout(timer);89 }90 this.timers.clear();91 this.cache.clear();92 return true;93 } catch (error) {94 logger.error('Memory cache clear error', error);95 return false;96 }97 }98 99 /**100 * Get cache size101 */102 size() {103 return this.cache.size;104 }105}106 107/**108 * Cache wrapper that uses Redis if available, falls back to memory cache109 */110class CacheManager {111 constructor(redisClient = null) {112 this.redis = redisClient;113 this.memoryCache = new MemoryCache();114 this.useRedis = !!redisClient;115 }116 117 /**118 * Set a value in cache119 */120 async set(key, value, ttl = 300) {121 try {122 const stringValue = typeof value === 'string' ? value : JSON.stringify(value);123 124 if (this.useRedis && this.redis) {125 if (ttl) {126 await this.redis.setex(key, ttl, stringValue);127 } else {128 await this.redis.set(key, stringValue);129 }130 logger.debug(`Cache set (Redis): ${key}`);131 } else {132 await this.memoryCache.set(key, stringValue, ttl);133 logger.debug(`Cache set (Memory): ${key}`);134 }135 return true;136 } catch (error) {137 logger.error(`Cache set error for key: ${key}`, error);138 return false;139 }140 }141 142 /**143 * Get a value from cache144 */145 async get(key) {146 try {147 let value;148 149 if (this.useRedis && this.redis) {150 value = await this.redis.get(key);151 logger.debug(`Cache get (Redis): ${key} - ${value ? 'HIT' : 'MISS'}`);152 } else {153 value = await this.memoryCache.get(key);154 logger.debug(`Cache get (Memory): ${key} - ${value ? 'HIT' : 'MISS'}`);155 }156 157 if (!value) return null;158 159 // Try to parse JSON, return as-is if not JSON160 try {161 return JSON.parse(value);162 } catch {163 return value;164 }165 } catch (error) {166 logger.error(`Cache get error for key: ${key}`, error);167 return null;168 }169 }170 171 /**172 * Delete a value from cache173 */174 async del(key) {175 try {176 if (this.useRedis && this.redis) {177 await this.redis.del(key);178 logger.debug(`Cache delete (Redis): ${key}`);179 } else {180 await this.memoryCache.del(key);181 logger.debug(`Cache delete (Memory): ${key}`);182 }183 return true;184 } catch (error) {185 logger.error(`Cache delete error for key: ${key}`, error);186 return false;187 }188 }189 190 /**191 * Check if key exists192 */193 async exists(key) {194 try {195 if (this.useRedis && this.redis) {196 return await this.redis.exists(key) === 1;197 } else {198 return await this.memoryCache.exists(key);199 }200 } catch (error) {201 logger.error(`Cache exists error for key: ${key}`, error);202 return false;203 }204 }205 206 /**207 * Clear cache by pattern208 */209 async clearPattern(pattern) {210 try {211 if (this.useRedis && this.redis) {212 const keys = await this.redis.keys(pattern);213 if (keys.length > 0) {214 await this.redis.del(...keys);215 logger.debug(`Cache cleared (Redis): ${keys.length} keys matching ${pattern}`);216 }217 } else {218 // Memory cache doesn't support patterns, clear all219 await this.memoryCache.clear();220 logger.debug('Cache cleared (Memory): all keys');221 }222 return true;223 } catch (error) {224 logger.error(`Cache clear pattern error: ${pattern}`, error);225 return false;226 }227 }228 229 /**230 * Get or set pattern - fetch from cache or execute function and cache result231 */232 async getOrSet(key, fetchFn, ttl = 300) {233 try {234 // Try to get from cache235 const cached = await this.get(key);236 if (cached !== null) {237 return cached;238 }239 240 // Not in cache, fetch data241 const data = await fetchFn();242 243 // Cache the result244 await this.set(key, data, ttl);245 246 return data;247 } catch (error) {248 logger.error(`Cache getOrSet error for key: ${key}`, error);249 // If caching fails, still return the fetched data250 return await fetchFn();251 }252 }253}254 255// Export singleton instance256let cacheInstance = null;257 258const initializeCache = (redisClient) => {259 cacheInstance = new CacheManager(redisClient);260 logger.info('Cache manager initialized', {261 type: redisClient ? 'Redis' : 'Memory',262 });263 return cacheInstance;264};265 266const getCache = () => {267 if (!cacheInstance) {268 cacheInstance = new CacheManager();269 logger.warn('Cache manager not initialized, using memory cache');270 }271 return cacheInstance;272};273 274module.exports = {275 MemoryCache,276 CacheManager,277 initializeCache,278 getCache,279};280 