UNPKG

@thermopylae/lib.cache

Version:
116 lines (115 loc) 4.06 kB
import { number } from '@thermopylae/lib.utils'; import { DoublyLinkedList } from "../../data-structures/list/doubly-linked.js"; import { createException } from "../../error.js"; const SEGMENT_SYM = Symbol('SEGMENT_SYM'); class SegmentedLRUEvictionPolicy { segments; deleteFromCache; constructor(cacheMaxCapacity, protectedOverProbationRatio = 0.7) { if (cacheMaxCapacity < 2) { throw createException("INVALID_CACHE_MAX_CAPACITY" , `Capacity needs to be at least 2. Given: ${cacheMaxCapacity}.`); } const protectedSegmentSize = Math.round(number.percentage(cacheMaxCapacity, protectedOverProbationRatio) ); if (protectedSegmentSize < 1) { const context = { cacheMaxCapacity, protectedOverProbationRatio, protectedSegmentSize }; throw createException("INVALID_PROTECTED_SEGMENT_SIZE" , `Protected segment size needs to be at least 1. Context: ${JSON.stringify(context)}.`); } this.segments = { [1 ]: { capacity: protectedSegmentSize, items: new DoublyLinkedList() }, [0 ]: { capacity: cacheMaxCapacity - protectedSegmentSize, items: new DoublyLinkedList() } }; if (this.segments[0 ].capacity < 1) { const context = { cacheMaxCapacity, protectedOverProbationRatio, protectedSegmentSize, probationSegmentSize: this.segments[0 ].capacity }; throw createException("INVALID_PROBATION_SEGMENT_SIZE" , `Probation segment size needs to be at least 1. Context: ${JSON.stringify(context)}.`); } } get size() { let size = 0; for (const segment of Object.keys(this.segments)) { size += this.segments[segment].items.size; } return size; } get mostRecent() { return this.getEntryFrom('head'); } get leastRecent() { return this.getEntryFrom('tail'); } onHit(entry) { this.promote(entry); return 1 ; } onMiss() { return undefined; } onSet(entry) { this.demote(entry); } onUpdate() { return undefined; } onDelete(entry) { this.segments[entry[SEGMENT_SYM]].items.remove(entry); entry[SEGMENT_SYM] = undefined; } onClear() { for (const segment of Object.keys(this.segments)) { this.segments[segment].items.clear(); } } setDeleter(deleter) { this.deleteFromCache = deleter; } isFull(segment) { return this.segments[segment].items.size === this.segments[segment].capacity; } demote(entry) { if (this.isFull(0 )) { this.deleteFromCache(this.segments[0 ].items.tail); } this.segments[0 ].items.unshift(entry); entry[SEGMENT_SYM] = 0 ; } promote(entry) { switch (entry[SEGMENT_SYM]) { case 0 : this.segments[0 ].items.remove(entry); if (this.isFull(1 )) { const tail = this.segments[1 ].items.tail; this.segments[1 ].items.remove(tail); this.demote(tail); } this.segments[1 ].items.unshift(entry); entry[SEGMENT_SYM] = 1 ; break; case 1 : this.segments[1 ].items.toFront(entry); break; default: throw createException("UNKNOWN_SEGMENT_TYPE" , `Unknown segment type: ${entry[SEGMENT_SYM]} found in entry: ${JSON.stringify(entry)}.`); } } getEntryFrom(pos) { if (this.segments[1 ].items[pos] == null) { if (this.segments[0 ].items[pos] == null) { return null; } return this.segments[0 ].items[pos]; } return this.segments[1 ].items[pos]; } } export { SegmentedLRUEvictionPolicy, SEGMENT_SYM };