UNPKG

@aradox/multi-orm

Version:

Type-safe ORM with multi-datasource support, row-level security, and Prisma-like API for PostgreSQL, SQL Server, and HTTP APIs

165 lines 5 kB
"use strict"; /** * Query Result Cache * * In-memory cache with TTL (time-to-live) and LRU (least recently used) eviction. * Provides caching for query results to reduce database load. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.QueryCache = void 0; const logger_1 = require("../utils/logger"); class QueryCache { cache = new Map(); options; constructor(options = {}) { this.options = { ttl: options.ttl ?? 60000, // 1 minute default maxSize: options.maxSize ?? 1000, enabled: options.enabled ?? true }; // Periodic cleanup of expired entries if (this.options.enabled) { setInterval(() => this.cleanup(), this.options.ttl); } } /** * Get cached value by key */ get(key) { if (!this.options.enabled) return null; const entry = this.cache.get(key); if (!entry) { logger_1.logger.debug('cache', `Cache miss: ${key}`); return null; } // Check if expired if (Date.now() > entry.expiry) { logger_1.logger.debug('cache', `Cache expired: ${key}`); this.cache.delete(key); return null; } // Update access time and hit count entry.lastAccessed = Date.now(); entry.hits++; logger_1.logger.debug('cache', `Cache hit: ${key} (hits: ${entry.hits})`); return entry.value; } /** * Set cache value with key */ set(key, value, ttl) { if (!this.options.enabled) return; // Evict LRU entry if cache is full if (this.cache.size >= this.options.maxSize) { this.evictLRU(); } const entryTTL = ttl ?? this.options.ttl; const entry = { key, value, expiry: Date.now() + entryTTL, lastAccessed: Date.now(), hits: 0 }; this.cache.set(key, entry); logger_1.logger.debug('cache', `Cache set: ${key} (TTL: ${entryTTL}ms)`); } /** * Invalidate (delete) cache entry by key */ invalidate(key) { const deleted = this.cache.delete(key); if (deleted) { logger_1.logger.debug('cache', `Cache invalidated: ${key}`); } return deleted; } /** * Invalidate cache entries matching pattern */ invalidatePattern(pattern) { const regex = typeof pattern === 'string' ? new RegExp(pattern) : pattern; let count = 0; for (const key of this.cache.keys()) { if (regex.test(key)) { this.cache.delete(key); count++; } } if (count > 0) { logger_1.logger.debug('cache', `Invalidated ${count} entries matching pattern: ${pattern}`); } return count; } /** * Clear all cache entries */ clear() { const size = this.cache.size; this.cache.clear(); logger_1.logger.debug('cache', `Cache cleared (${size} entries removed)`); } /** * Get cache statistics */ stats() { let totalHits = 0; for (const entry of this.cache.values()) { totalHits += entry.hits; } return { size: this.cache.size, maxSize: this.options.maxSize, hitRate: 0, // Would need to track misses separately totalHits, totalMisses: 0 // Would need to track separately }; } /** * Evict least recently used entry */ evictLRU() { let oldestKey = null; let oldestTime = Infinity; for (const [key, entry] of this.cache.entries()) { if (entry.lastAccessed < oldestTime) { oldestTime = entry.lastAccessed; oldestKey = key; } } if (oldestKey) { this.cache.delete(oldestKey); logger_1.logger.debug('cache', `Evicted LRU entry: ${oldestKey}`); } } /** * Remove expired entries */ cleanup() { const now = Date.now(); const keysToDelete = []; for (const [key, entry] of this.cache.entries()) { if (now > entry.expiry) { keysToDelete.push(key); } } for (const key of keysToDelete) { this.cache.delete(key); } if (keysToDelete.length > 0) { logger_1.logger.debug('cache', `Cleanup: removed ${keysToDelete.length} expired entries`); } } /** * Generate cache key from query parameters */ static generateKey(model, operation, args) { // Sort object keys for consistent key generation const sortedArgs = JSON.stringify(args, Object.keys(args || {}).sort()); return `${model}:${operation}:${sortedArgs}`; } } exports.QueryCache = QueryCache; //# sourceMappingURL=cache.js.map