fix: update LRUcache to not keep thread (#6013)
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user