@thermopylae/lib.cache
Version:
102 lines (101 loc) • 3.29 kB
JavaScript
import { chrono } from '@thermopylae/lib.utils';
import { HashMapBucketList } from "../data-structures/bucket-list/hash-map-bucket-list.js";
import { EXPIRES_AT_SYM } from "../constants.js";
class BucketGarbageCollector {
static EVICTION_TIMEOUT = 1;
buckets;
evictionTimer;
entryExpiredCb;
constructor(entryExpiredCb) {
this.buckets = new HashMapBucketList();
this.evictionTimer = new EvictionTimer(this.evictionTimerHandler);
this.entryExpiredCb = entryExpiredCb || BucketGarbageCollector.defaultEntryExpiredCallback;
}
get idle() {
return this.evictionTimer.idle;
}
get size() {
return this.buckets.size;
}
manage(entry) {
this.buckets.add(entry[EXPIRES_AT_SYM], entry);
this.evictionTimer.restart(entry[EXPIRES_AT_SYM]);
}
update(oldExpiration, entry) {
this.buckets.move(oldExpiration, entry[EXPIRES_AT_SYM], entry);
this.evictionTimer.synchronize(entry[EXPIRES_AT_SYM]);
}
leave(entry) {
this.buckets.remove(entry[EXPIRES_AT_SYM], entry);
if (this.buckets.numberOfBuckets === 0) {
this.evictionTimer.stop();
}
}
clear() {
this.buckets.clear();
this.evictionTimer.stop();
}
setEntryExpiredCallback(cb) {
this.entryExpiredCb = cb;
}
evictionTimerHandler = () => {
let now = chrono.unixTime();
let evictedBucketId;
while (true) {
evictedBucketId = now;
this.buckets.dropBucket(now, this.entryExpiredCb);
now = chrono.unixTime();
if (evictedBucketId === now) {
break;
}
}
if (this.buckets.numberOfBuckets === 0) {
return this.evictionTimer.stop();
}
this.evictionTimer.startAfter(BucketGarbageCollector.EVICTION_TIMEOUT, now);
};
static defaultEntryExpiredCallback() {
return undefined;
}
}
class EvictionTimer {
evictionInterval;
onTick;
constructor(onTick) {
this.evictionInterval = {
timeoutId: null,
willTickOn: -1
};
this.onTick = onTick;
}
get idle() {
return this.evictionInterval.timeoutId == null;
}
startAt(startTimestamp) {
this.evictionInterval.willTickOn = startTimestamp;
this.evictionInterval.timeoutId = setTimeout(this.onTick, chrono.secondsToMilliseconds(startTimestamp - chrono.unixTime()));
}
startAfter(timeout, now) {
this.evictionInterval.willTickOn = now + timeout;
this.evictionInterval.timeoutId = setTimeout(this.onTick, chrono.secondsToMilliseconds(timeout));
}
synchronize(withTimestamp) {
if (this.evictionInterval.willTickOn > withTimestamp) {
this.stop();
this.startAt(withTimestamp);
}
}
restart(startTimestamp) {
if (this.evictionInterval.timeoutId == null) {
this.startAt(startTimestamp);
return;
}
this.synchronize(startTimestamp);
}
stop() {
clearTimeout(this.evictionInterval.timeoutId);
this.evictionInterval.timeoutId = null;
this.evictionInterval.willTickOn = -1;
}
}
export { BucketGarbageCollector };