@dbs-portal/core-module-registry
Version:
Core module registry system for automatic module discovery and registration
277 lines • 6.92 kB
JavaScript
/**
* Module Cache
*
* High-performance caching system for module registry with TTL support,
* memory management, and cache statistics.
*/
export class ModuleCache {
cache = new Map();
options;
stats;
cleanupTimer = null;
constructor(defaultTTL = 5 * 60 * 1000, options = {}) {
this.options = {
defaultTTL,
maxSize: 1000,
cleanupInterval: 60 * 1000, // 1 minute
enableStats: true,
...options
};
this.stats = {
hits: 0,
misses: 0,
sets: 0,
deletes: 0,
evictions: 0,
size: 0,
hitRate: 0
};
// Start cleanup timer
this.startCleanupTimer();
}
/**
* Get value from cache
*/
get(key) {
const entry = this.cache.get(key);
if (!entry) {
this.updateStats('miss');
return null;
}
// Check if entry has expired
if (Date.now() > entry.expiry) {
this.cache.delete(key);
this.updateStats('miss');
return null;
}
this.updateStats('hit');
return entry.value;
}
/**
* Set value in cache
*/
set(key, value, ttl) {
const actualTTL = ttl ?? this.options.defaultTTL;
const expiry = Date.now() + actualTTL;
// Check if we need to evict entries due to size limit
if (this.cache.size >= this.options.maxSize && !this.cache.has(key)) {
this.evictOldest();
}
const entry = {
value,
expiry,
timestamp: Date.now()
};
this.cache.set(key, entry);
this.updateStats('set');
}
/**
* Check if key exists in cache (without updating access time)
*/
has(key) {
const entry = this.cache.get(key);
if (!entry) {
return false;
}
// Check if expired
if (Date.now() > entry.expiry) {
this.cache.delete(key);
return false;
}
return true;
}
/**
* Delete value from cache
*/
delete(key) {
const deleted = this.cache.delete(key);
if (deleted) {
this.updateStats('delete');
}
return deleted;
}
/**
* Clear all cache entries
*/
clear() {
this.cache.clear();
this.resetStats();
}
/**
* Get cache size
*/
size() {
return this.cache.size;
}
/**
* Get all cache keys
*/
keys() {
return Array.from(this.cache.keys());
}
/**
* Get cache statistics
*/
getStats() {
this.updateHitRate();
return { ...this.stats, size: this.cache.size };
}
/**
* Reset cache statistics
*/
resetStats() {
this.stats = {
hits: 0,
misses: 0,
sets: 0,
deletes: 0,
evictions: 0,
size: this.cache.size,
hitRate: 0
};
}
/**
* Get cache entries that match a pattern
*/
getByPattern(pattern) {
const results = [];
const now = Date.now();
for (const [key, entry] of this.cache.entries()) {
// Skip expired entries
if (now > entry.expiry) {
this.cache.delete(key);
continue;
}
if (pattern.test(key)) {
results.push({ key, value: entry.value });
}
}
return results;
}
/**
* Set multiple values at once
*/
setMany(entries) {
entries.forEach(({ key, value, ttl }) => {
this.set(key, value, ttl);
});
}
/**
* Get multiple values at once
*/
getMany(keys) {
return keys.map(key => ({
key,
value: this.get(key)
}));
}
/**
* Delete multiple keys at once
*/
deleteMany(keys) {
let deletedCount = 0;
keys.forEach(key => {
if (this.delete(key)) {
deletedCount++;
}
});
return deletedCount;
}
/**
* Get cache memory usage estimate (in bytes)
*/
getMemoryUsage() {
let totalSize = 0;
for (const [key, entry] of this.cache.entries()) {
// Rough estimate: key size + value size + overhead
totalSize += key.length * 2; // UTF-16 characters
totalSize += JSON.stringify(entry.value).length * 2;
totalSize += 64; // Overhead for entry object
}
return totalSize;
}
/**
* Cleanup expired entries
*/
cleanup() {
const now = Date.now();
let cleanedCount = 0;
for (const [key, entry] of this.cache.entries()) {
if (now > entry.expiry) {
this.cache.delete(key);
cleanedCount++;
}
}
return cleanedCount;
}
/**
* Destroy cache and cleanup resources
*/
destroy() {
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = null;
}
this.clear();
}
/**
* Start automatic cleanup timer
*/
startCleanupTimer() {
if (this.options.cleanupInterval > 0) {
this.cleanupTimer = setInterval(() => {
this.cleanup();
}, this.options.cleanupInterval);
}
}
/**
* Evict oldest entry when cache is full
*/
evictOldest() {
let oldestKey = null;
let oldestTimestamp = Date.now();
for (const [key, entry] of this.cache.entries()) {
if (entry.timestamp < oldestTimestamp) {
oldestTimestamp = entry.timestamp;
oldestKey = key;
}
}
if (oldestKey) {
this.cache.delete(oldestKey);
this.updateStats('eviction');
}
}
/**
* Update cache statistics
*/
updateStats(operation) {
if (!this.options.enableStats) {
return;
}
switch (operation) {
case 'hit':
this.stats.hits++;
break;
case 'miss':
this.stats.misses++;
break;
case 'set':
this.stats.sets++;
break;
case 'delete':
this.stats.deletes++;
break;
case 'eviction':
this.stats.evictions++;
break;
}
}
/**
* Update hit rate calculation
*/
updateHitRate() {
const total = this.stats.hits + this.stats.misses;
this.stats.hitRate = total > 0 ? (this.stats.hits / total) * 100 : 0;
}
}
//# sourceMappingURL=ModuleCache.js.map