@thermopylae/lib.cache
Version:
94 lines (93 loc) • 2.98 kB
JavaScript
import { chrono } from '@thermopylae/lib.utils';
import { Heap } from "../data-structures/heap.js";
import { EXPIRES_AT_SYM } from "../constants.js";
class HeapGarbageCollector {
entries;
cleanUpInterval;
entryExpiredCb;
constructor(entryExpiredCb) {
this.entries = new Heap((first, second) => {
return first[EXPIRES_AT_SYM] - second[EXPIRES_AT_SYM];
});
this.cleanUpInterval = null;
this.entryExpiredCb = entryExpiredCb || HeapGarbageCollector.defaultEntryExpiredCallback;
}
get size() {
return this.entries.size;
}
get idle() {
return this.cleanUpInterval == null;
}
manage(entry) {
this.entries.push(entry);
this.synchronizeEvictionTimer();
}
update(_oldExpiration, entry) {
if (!Heap.isPartOfHeap(entry)) {
return;
}
this.entries.heapifyUpdatedNode(entry);
this.synchronizeEvictionTimer();
}
leave(entry) {
if (!Heap.isPartOfHeap(entry)) {
return;
}
this.entries.delete(entry);
this.synchronizeEvictionTimer();
}
clear() {
this.entries.clear();
this.synchronizeEvictionTimer();
}
setEntryExpiredCallback(cb) {
this.entryExpiredCb = cb;
}
synchronizeEvictionTimer() {
const rootEntry = this.entries.peek();
if (rootEntry === undefined) {
if (this.cleanUpInterval != null) {
clearTimeout(this.cleanUpInterval.timeoutId);
this.cleanUpInterval = null;
}
return;
}
if (this.cleanUpInterval == null) {
this.cleanUpInterval = {
willCleanUpOn: 0,
timeoutId: null
};
this.scheduleNextGc(rootEntry);
return;
}
if (this.cleanUpInterval.willCleanUpOn !== rootEntry[EXPIRES_AT_SYM]) {
clearTimeout(this.cleanUpInterval.timeoutId);
this.scheduleNextGc(rootEntry);
}
}
scheduleNextGc(rootEntry) {
const runDelay = rootEntry[EXPIRES_AT_SYM] - chrono.unixTime();
this.cleanUpInterval.willCleanUpOn = rootEntry[EXPIRES_AT_SYM];
this.cleanUpInterval.timeoutId = setTimeout(this.evictExpiredEntries, runDelay * 1000);
}
evictExpiredEntries = () => {
let rootEntry;
do {
rootEntry = this.entries.pop();
this.entryExpiredCb(rootEntry);
rootEntry = this.entries.peek();
if (rootEntry === undefined) {
this.cleanUpInterval = null;
return;
}
if (rootEntry[EXPIRES_AT_SYM] !== this.cleanUpInterval.willCleanUpOn) {
this.scheduleNextGc(rootEntry);
return;
}
} while (true);
};
static defaultEntryExpiredCallback() {
return undefined;
}
}
export { HeapGarbageCollector };