UNPKG

@thermopylae/lib.cache

Version:
131 lines (130 loc) 5.39 kB
import { chrono, number } from '@thermopylae/lib.utils'; import { memoryUsage } from 'process'; const PRIORITY_SYM = Symbol('PRIORITY_SYM'); class PriorityEvictionPolicy { options; numberOfCacheEntriesByPriority; checkMemoryConsumptionIntervalId; deleteFromCache; constructor(options) { this.options = PriorityEvictionPolicy.fillConstructorOptionsWithDefaults(options); this.numberOfCacheEntriesByPriority = new Map(); PriorityEvictionPolicy.fillNumberOfCacheEntriesByPriorityWithStartingValues(this.numberOfCacheEntriesByPriority); this.checkMemoryConsumptionIntervalId = null; } get idle() { return this.checkMemoryConsumptionIntervalId == null; } onHit() { return 1 ; } onMiss() { return undefined; } onSet(entry, options) { entry[PRIORITY_SYM] = options && options.priority != null ? options.priority : 2 ; this.increaseNumberOfEntries(entry[PRIORITY_SYM]); if (this.checkMemoryConsumptionIntervalId == null) { const timeout = chrono.milliseconds(0, 0, this.options.checkInterval); this.checkMemoryConsumptionIntervalId = setInterval(this.performEvictionOnLowMemory, timeout); } } onUpdate(entry, options) { if (options == null || options.priority == null) { return; } if (entry[PRIORITY_SYM] === options.priority) { return; } this.decreaseNumberOfEntries(entry[PRIORITY_SYM]); entry[PRIORITY_SYM] = options.priority; this.increaseNumberOfEntries(entry[PRIORITY_SYM]); } onDelete(entry) { this.decreaseNumberOfEntries(entry[PRIORITY_SYM]); entry[PRIORITY_SYM] = undefined; if (this.options.iterableCacheBackend.size < 2) { this.stopEvictionTimer(); } } onClear() { PriorityEvictionPolicy.fillNumberOfCacheEntriesByPriorityWithStartingValues(this.numberOfCacheEntriesByPriority); if (this.checkMemoryConsumptionIntervalId) { this.stopEvictionTimer(); } } setDeleter(deleter) { this.deleteFromCache = deleter; } performEvictionOnLowMemory = () => { const { heapTotal, heapUsed } = memoryUsage(); const availableMemoryPercentage = (heapTotal - heapUsed) / heapTotal; if (availableMemoryPercentage <= this.options.criticalAvailableMemoryPercentage) { let totalNumberOfEntriesToBeEvicted = number.integerPercentage(this.options.iterableCacheBackend.size, this.options.cacheEvictionPercentage); const numberOfEntriesToEvictByPriority = this.computeNumberOfEntriesToEvictByPriority(totalNumberOfEntriesToBeEvicted); for (const entry of this.options.iterableCacheBackend.values()) { if (totalNumberOfEntriesToBeEvicted <= 0) { break; } if (numberOfEntriesToEvictByPriority[entry[PRIORITY_SYM]]) { this.deleteFromCache(entry); numberOfEntriesToEvictByPriority[entry[PRIORITY_SYM]] -= 1; totalNumberOfEntriesToBeEvicted -= 1; } } } if (this.options.iterableCacheBackend.size === 0) { this.stopEvictionTimer(); } }; computeNumberOfEntriesToEvictByPriority(totalEntriesToBeEvicted) { const toDelete = { [0 ]: 0, [1 ]: 0, [2 ]: 0, [3 ]: 0, [4 ]: 0, [5 ]: 0 }; for (let i = 0 ; i < 5 ; i++) { const numberOfEntriesByPriority = this.numberOfCacheEntriesByPriority.get(i); if (totalEntriesToBeEvicted <= numberOfEntriesByPriority) { toDelete[i] = totalEntriesToBeEvicted; break; } toDelete[i] = numberOfEntriesByPriority; totalEntriesToBeEvicted -= numberOfEntriesByPriority; } return toDelete; } increaseNumberOfEntries(priority) { const actualNumber = this.numberOfCacheEntriesByPriority.get(priority); this.numberOfCacheEntriesByPriority.set(priority, actualNumber + 1); } decreaseNumberOfEntries(priority) { const actualNumber = this.numberOfCacheEntriesByPriority.get(priority); this.numberOfCacheEntriesByPriority.set(priority, actualNumber - 1); } stopEvictionTimer() { clearInterval(this.checkMemoryConsumptionIntervalId); this.checkMemoryConsumptionIntervalId = null; } static fillConstructorOptionsWithDefaults(options) { options.checkInterval = options.checkInterval || 3600; number.assertIsInteger(options.checkInterval); options.criticalAvailableMemoryPercentage = options.criticalAvailableMemoryPercentage || 0.2; number.assertIsPercentage(options.criticalAvailableMemoryPercentage); options.cacheEvictionPercentage = options.cacheEvictionPercentage || 0.2; number.assertIsPercentage(options.cacheEvictionPercentage); return options; } static fillNumberOfCacheEntriesByPriorityWithStartingValues(map) { map.set(0 , 0); map.set(1 , 0); map.set(2 , 0); map.set(3 , 0); map.set(4 , 0); map.set(5 , 0); } } export { PriorityEvictionPolicy, PRIORITY_SYM };