UNPKG

@nivinjoseph/n-data

Version:

Data access library for Postgres based on Knex

120 lines (90 loc) 3.31 kB
import { CacheService } from "./cache-service.js"; import { Disposable, Duration } from "@nivinjoseph/n-util"; import { given } from "@nivinjoseph/n-defensive"; import { ObjectDisposedException } from "@nivinjoseph/n-exception"; import { clearInterval, setInterval } from "node:timers"; export class InMemoryCacheService implements CacheService, Disposable { private readonly _store: Map<string, string>; private readonly _evictionTracking: Map<string, number>; private readonly _timer: NodeJS.Timeout; private _isDisposed: boolean; public constructor() { this._store = new Map<string, string>(); this._evictionTracking = new Map<string, number>(); this._isDisposed = false; this._timer = setInterval(() => this._evict(), Duration.fromMinutes(5).toMilliSeconds()); } public async store<T>(key: string, value: T, expiryDuration?: Duration): Promise<void> { given(key, "key").ensureHasValue().ensureIsString(); given(value, "value").ensureHasValue(); given(expiryDuration as Duration, "expiryDuration").ensureIsObject(); if (this._isDisposed) throw new ObjectDisposedException(this); key = key.trim(); this._store.set(key, JSON.stringify(value)); if (expiryDuration == null) { if (this._evictionTracking.has(key)) this._evictionTracking.delete(key); } else { this._evictionTracking.set(key, Date.now() + expiryDuration.toMilliSeconds()); } } public async retrieve<T>(key: string): Promise<T | null> { given(key, "key").ensureHasValue().ensureIsString(); if (this._isDisposed) throw new ObjectDisposedException(this); key = key.trim(); return this._store.has(key) ? JSON.parse(this._store.get(key)!) as T : null; } public async exists(key: string): Promise<boolean> { given(key, "key").ensureHasValue().ensureIsString(); if (this._isDisposed) throw new ObjectDisposedException(this); return this._store.has(key.trim()); } public async remove(key: string): Promise<void> { given(key, "key").ensureHasValue().ensureIsString(); if (this._isDisposed) throw new ObjectDisposedException(this); key = key.trim(); if (this._store.has(key)) this._store.delete(key); if (this._evictionTracking.has(key)) this._evictionTracking.delete(key); } public dispose(): Promise<void> { if (this._isDisposed) return Promise.resolve(); this._isDisposed = true; clearInterval(this._timer); return Promise.resolve(); } private _evict(): void { if (this._isDisposed) return; for (const entry of this._store.entries()) { const key = entry[0]; if (this._evictionTracking.has(key)) { const expiry = this._evictionTracking.get(key) ?? 0; if (expiry <= Date.now()) { this._store.delete(key); this._evictionTracking.delete(key); } } } } }