Skip to content

Commit

Permalink
fix: update LRUcache to not keep thread (#6013)
Browse files Browse the repository at this point in the history
  • Loading branch information
filvecchiato authored Oct 31, 2024
1 parent a6b23ca commit abce780
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 58 deletions.
2 changes: 0 additions & 2 deletions packages/rpc-core/src/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
19 changes: 14 additions & 5 deletions packages/rpc-provider/src/lru.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -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);
});
});
82 changes: 31 additions & 51 deletions packages/rpc-provider/src/lru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand All @@ -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;
}
Expand Down Expand Up @@ -115,17 +105,22 @@ export class LRUCache {
if (data) {
this.#toHead(key);

// Evict TTL once data is refreshed
this.#evictTTL();

return data as T;
}

this.#evictTTL();

return null;
}

set <T> (key: string, value: T): void {
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);

Expand All @@ -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('<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;
}
// 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('<empty>', this.#ttl);
}
}

Expand All @@ -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;
}
}
}

0 comments on commit abce780

Please sign in to comment.