@thermopylae/lib.cache
Version:
131 lines (130 loc) • 5.39 kB
JavaScript
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 };