fix: update LRUcache to not keep thread (#6013)

This commit is contained in:
filvecchiato
2024-10-31 08:59:27 -04:00
committed by GitHub
parent a6b23ca635
commit abce780033
3 changed files with 44 additions and 57 deletions
-2
View File
@@ -146,8 +146,6 @@ export class RpcCore {
* @description Manually disconnect from the attached provider
*/
public async disconnect (): Promise<void> {
await this.#storageCache.clearInterval();
return this.provider.disconnect();
}
+14 -5
View File
@@ -9,11 +9,7 @@ describe('LRUCache', (): void => {
let lru: LRUCache | undefined;
beforeEach((): void => {
lru = new LRUCache(4);
});
afterEach(async () => {
await lru?.clearInterval();
lru = undefined;
lru = new LRUCache(4, 500);
});
it('allows getting of items below capacity', (): void => {
@@ -62,4 +58,17 @@ describe('LRUCache', (): void => {
expect(lru?.entries()).toEqual([['6', '666'], ['4', '4433'], ['3', '333'], ['5', '555']]);
expect(lru?.length === lru?.lengthData && lru?.length === lru?.lengthRefs).toBe(true);
});
it('evicts items with TTL', (): void => {
const keys = ['1', '2', '3', '4', '5'];
keys.forEach((k) => lru?.set(k, `${k}${k}${k}`));
expect(lru?.entries()).toEqual([['5', '555'], ['4', '444'], ['3', '333'], ['2', '222']]);
setTimeout((): void => {
lru?.get('3');
expect(lru?.entries()).toEqual([['3', '333']]);
}, 800);
});
});
+30 -50
View File
@@ -9,25 +9,27 @@ export const DEFAULT_CAPACITY = 64;
class LRUNode {
readonly key: string;
#lastAccess: number;
#expires: number;
#ttl: number;
readonly createdAt: number;
public next: LRUNode;
public prev: LRUNode;
constructor (key: string) {
constructor (key: string, ttl: number) {
this.key = key;
this.#lastAccess = Date.now();
this.createdAt = this.#lastAccess;
this.#ttl = ttl;
this.#expires = Date.now() + ttl;
this.createdAt = Date.now();
this.next = this.prev = this;
}
public refresh (): void {
this.#lastAccess = Date.now();
this.#expires = Date.now() + this.#ttl;
}
public get lastAccess (): number {
return this.#lastAccess;
public get expiry (): number {
return this.#expires;
}
}
@@ -43,29 +45,17 @@ export class LRUCache {
#tail: LRUNode;
readonly #ttl: number;
readonly #ttlInterval: number;
#ttlTimerId: ReturnType<typeof setInterval> | null = null;
constructor (capacity = DEFAULT_CAPACITY, ttl = 30000, ttlInterval = 15000) {
constructor (capacity = DEFAULT_CAPACITY, ttl = 30000) {
this.capacity = capacity;
this.#ttl = ttl;
this.#ttlInterval = ttlInterval;
this.#head = this.#tail = new LRUNode('<empty>');
// make sure the interval is not longer than the ttl
if (this.#ttlInterval > this.#ttl) {
this.#ttlInterval = this.#ttl;
}
this.#head = this.#tail = new LRUNode('<empty>', ttl);
}
get ttl (): number {
return this.#ttl;
}
get ttlInterval (): number {
return this.#ttlInterval;
}
get length (): number {
return this.#length;
}
@@ -115,9 +105,14 @@ export class LRUCache {
if (data) {
this.#toHead(key);
// Evict TTL once data is refreshed
this.#evictTTL();
return data as T;
}
this.#evictTTL();
return null;
}
@@ -125,7 +120,7 @@ export class LRUCache {
if (this.#data.has(key)) {
this.#toHead(key);
} else {
const node = new LRUNode(key);
const node = new LRUNode(key, this.#ttl);
this.#refs.set(node.key, node);
@@ -148,32 +143,25 @@ export class LRUCache {
}
}
if (this.#ttl > 0 && !this.#ttlTimerId) {
this.#ttlTimerId = setInterval(() => {
this.#ttlClean();
}, this.#ttlInterval);
}
// Evict TTL once data is refreshed or added
this.#evictTTL();
this.#data.set(key, value);
}
#ttlClean () {
#evictTTL () {
// Find last node to keep
const expires = Date.now() - this.#ttl;
// traverse map to find the expired nodes
while (this.#tail.expiry && this.#tail.expiry < Date.now() && this.#length > 0) {
this.#refs.delete(this.#tail.key);
this.#data.delete(this.#tail.key);
this.#length -= 1;
this.#tail = this.#tail.prev;
this.#tail.next = this.#head;
}
// traverse map to find the lastAccessed
while (this.#tail.lastAccess && this.#tail.lastAccess < expires && this.#length > 0) {
if (this.#ttlTimerId && this.#length === 0) {
clearInterval(this.#ttlTimerId);
this.#ttlTimerId = null;
this.#head = this.#tail = new LRUNode('<empty>');
} else {
this.#refs.delete(this.#tail.key);
this.#data.delete(this.#tail.key);
this.#length -= 1;
this.#tail = this.#tail.prev;
this.#tail.next = this.#head;
}
if (this.#length === 0) {
this.#head = this.#tail = new LRUNode('<empty>', this.#ttl);
}
}
@@ -190,12 +178,4 @@ export class LRUCache {
this.#head = ref;
}
}
// eslint-disable-next-line @typescript-eslint/require-await
public async clearInterval (): Promise<void> {
if (this.#ttlTimerId) {
clearInterval(this.#ttlTimerId);
this.#ttlTimerId = null;
}
}
}