@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
JavaScript
;
/**
* 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