From abce780033d2f1dbfc7c75196bd059454c36ce8f Mon Sep 17 00:00:00 2001 From: filvecchiato Date: Thu, 31 Oct 2024 13:59:27 +0100 Subject: [PATCH] fix: update LRUcache to not keep thread (#6013) --- packages/rpc-core/src/bundle.ts | 2 - packages/rpc-provider/src/lru.spec.ts | 19 +++++-- packages/rpc-provider/src/lru.ts | 82 ++++++++++----------------- 3 files changed, 45 insertions(+), 58 deletions(-) diff --git a/packages/rpc-core/src/bundle.ts b/packages/rpc-core/src/bundle.ts index 6c86702666f..702f22b43a6 100644 --- a/packages/rpc-core/src/bundle.ts +++ b/packages/rpc-core/src/bundle.ts @@ -146,8 +146,6 @@ export class RpcCore { * @description Manually disconnect from the attached provider */ public async disconnect (): Promise { - await this.#storageCache.clearInterval(); - return this.provider.disconnect(); } diff --git a/packages/rpc-provider/src/lru.spec.ts b/packages/rpc-provider/src/lru.spec.ts index d36e05b677c..43169ed7012 100644 --- a/packages/rpc-provider/src/lru.spec.ts +++ b/packages/rpc-provider/src/lru.spec.ts @@ -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); + }); }); diff --git a/packages/rpc-provider/src/lru.ts b/packages/rpc-provider/src/lru.ts index 2a5cc3cdbf4..0a6927ef1d7 100644 --- a/packages/rpc-provider/src/lru.ts +++ b/packages/rpc-provider/src/lru.ts @@ -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 | 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(''); - - // 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('', 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 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(''); - } 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; - } + // 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; + } + + if (this.#length === 0) { + this.#head = this.#tail = new LRUNode('', this.#ttl); } } @@ -190,12 +178,4 @@ export class LRUCache { this.#head = ref; } } - - // eslint-disable-next-line @typescript-eslint/require-await - public async clearInterval (): Promise { - if (this.#ttlTimerId) { - clearInterval(this.#ttlTimerId); - this.#ttlTimerId = null; - } - } }