This is consistent with what we're pretty much already doing, and saves us from making the request during code review. These were all autofixed and most of them seem easier to read. Some in the legacy services should be rewritten in more legible forms during refactor (ie using intermediate variables, or using request’s qs option). There are some in helper functions and elsewhere that should get rewritten separately. I don't want to change them in this PR because the changes will get lost in this diff, though we could identify them here and fix them before or just after.
139 lines
3.3 KiB
JavaScript
139 lines
3.3 KiB
JavaScript
'use strict'
|
|
|
|
// In-memory KV, remove the oldest data when the capacity is reached.
|
|
|
|
const typeEnum = {
|
|
unit: 0,
|
|
heap: 1,
|
|
}
|
|
|
|
function CacheSlot(key, value) {
|
|
this.key = key
|
|
this.value = value
|
|
this.older = null // Newest slot that is older than this slot.
|
|
this.newer = null // Oldest slot that is newer than this slot.
|
|
}
|
|
|
|
function Cache(capacity, type) {
|
|
if (!(this instanceof Cache)) {
|
|
return new Cache(capacity, type)
|
|
}
|
|
type = type || 'unit'
|
|
this.capacity = capacity
|
|
this.type = typeEnum[type]
|
|
this.cache = new Map() // Maps cache keys to CacheSlots.
|
|
this.newest = null // Newest slot in the cache.
|
|
this.oldest = null
|
|
}
|
|
|
|
Cache.prototype = {
|
|
set: function addToCache(cacheKey, cached) {
|
|
let slot = this.cache.get(cacheKey)
|
|
if (slot === undefined) {
|
|
slot = new CacheSlot(cacheKey, cached)
|
|
this.cache.set(cacheKey, slot)
|
|
}
|
|
this.makeNewest(slot)
|
|
const numItemsToRemove = this.limitReached()
|
|
if (numItemsToRemove > 0) {
|
|
for (let i = 0; i < numItemsToRemove; i++) {
|
|
this.removeOldest()
|
|
}
|
|
}
|
|
},
|
|
|
|
get: function getFromCache(cacheKey) {
|
|
const slot = this.cache.get(cacheKey)
|
|
if (slot !== undefined) {
|
|
this.makeNewest(slot)
|
|
return slot.value
|
|
}
|
|
},
|
|
|
|
has: function hasInCache(cacheKey) {
|
|
return this.cache.has(cacheKey)
|
|
},
|
|
|
|
makeNewest: function makeNewestSlot(slot) {
|
|
const previousNewest = this.newest
|
|
if (previousNewest === slot) {
|
|
return
|
|
}
|
|
const older = slot.older
|
|
const newer = slot.newer
|
|
|
|
if (older !== null) {
|
|
older.newer = newer
|
|
} else if (newer !== null) {
|
|
this.oldest = newer
|
|
}
|
|
if (newer !== null) {
|
|
newer.older = older
|
|
}
|
|
this.newest = slot
|
|
|
|
if (previousNewest !== null) {
|
|
slot.older = previousNewest
|
|
slot.newer = null
|
|
previousNewest.newer = slot
|
|
} else {
|
|
// If previousNewest is null, the cache used to be empty.
|
|
this.oldest = slot
|
|
}
|
|
},
|
|
|
|
removeOldest: function removeOldest() {
|
|
const cacheKey = this.oldest.key
|
|
if (this.oldest !== null) {
|
|
this.oldest = this.oldest.newer
|
|
if (this.oldest !== null) {
|
|
this.oldest.older = null
|
|
}
|
|
}
|
|
this.cache.delete(cacheKey)
|
|
},
|
|
|
|
// Returns the number of elements to remove if we're past the limit.
|
|
limitReached: function heuristic() {
|
|
if (this.type === typeEnum.unit) {
|
|
// Remove the excess.
|
|
return Math.max(0, this.cache.size - this.capacity)
|
|
} else if (this.type === typeEnum.heap) {
|
|
if (getHeapSize() >= this.capacity) {
|
|
console.log('LRU HEURISTIC heap:', getHeapSize())
|
|
// Remove half of them.
|
|
return this.cache.size >> 1
|
|
} else {
|
|
return 0
|
|
}
|
|
} else {
|
|
console.error(`Unknown heuristic '${this.type}' for LRU cache.`)
|
|
return 1
|
|
}
|
|
},
|
|
|
|
clear: function() {
|
|
this.cache.clear()
|
|
this.newest = null
|
|
this.oldest = null
|
|
},
|
|
}
|
|
|
|
// In bytes.
|
|
let heapSize
|
|
let heapSizeTimeout
|
|
function getHeapSize() {
|
|
if (heapSizeTimeout == null) {
|
|
// Compute the heap size every 60 seconds.
|
|
heapSizeTimeout = setInterval(computeHeapSize, 60 * 1000)
|
|
return computeHeapSize()
|
|
} else {
|
|
return heapSize
|
|
}
|
|
}
|
|
function computeHeapSize() {
|
|
return (heapSize = process.memoryUsage().heapTotal)
|
|
}
|
|
|
|
module.exports = Cache
|