Last active
January 2, 2026 16:00
-
-
Save SparK-Cruz/d8e6c3b0bf04019c982fcf99a65d7d6e to your computer and use it in GitHub Desktop.
Generic cache service singleton with variable TTL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const DEFAULT_CACHE_TTL = 5000; | |
| const now = () => { | |
| return (new Date()).getTime(); | |
| }; | |
| interface CacheStoreDictionary { | |
| [key: string]: { loaded: number, row: any } | |
| } | |
| export class CacheService { | |
| private static _instance: CacheService; | |
| private _cache: { [storeKey: string]: CacheStoreDictionary } = {}; | |
| public constructor() { | |
| if (CacheService._instance) { | |
| return CacheService._instance; | |
| } | |
| CacheService._instance = this; | |
| } | |
| public getStore(storeKey: string, timeToLive: number = DEFAULT_CACHE_TTL): CacheStore { | |
| this._cache[storeKey] ??= {}; | |
| return new CacheStore(this._cache[storeKey], timeToLive); | |
| } | |
| } | |
| export class CacheStore { | |
| public static DUMMY: CacheStore = new CacheStore(Object.freeze({}), 0); | |
| private _ttl: number; | |
| private _store: CacheStoreDictionary; | |
| public constructor(store: CacheStoreDictionary, timeToLive: number) { | |
| this._store = store; | |
| this._ttl = timeToLive; | |
| } | |
| public async readOrAdd(key: string, provider: () => any): Promise<any> { | |
| const cached = this.read(key); | |
| if (cached) return cached; | |
| const model = await provider(); | |
| this.add(key, model); | |
| return model; | |
| } | |
| public add(key: string, model: any): void { | |
| this._store[key] ??= { loaded: 0, row: null }; | |
| const item = this._store[key]; | |
| item.loaded = now(); | |
| item.row = model; | |
| } | |
| public read(key: string): any | undefined { | |
| const item = this._store[key]; | |
| if (!item) return; | |
| if (item.loaded + this._ttl < now()) { | |
| this.remove(key); | |
| return; | |
| } | |
| return item.row; | |
| } | |
| public remove(key: string): void { | |
| delete this._store[key]; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { CacheService } from "./CacheService"; | |
| const MAX_SIMULATED_LATENCY = 300; | |
| const ONE_SECOND = 1000; | |
| const ONE_MINUTE = ONE_SECOND * 60; | |
| const STORE_KEY = 'yourStoreKeyByContext'; | |
| const CACHE_KEY = 'yourValue'; | |
| const cacheService = new CacheService(); | |
| const cache = cacheService.getStore(STORE_KEY, ONE_SECOND); | |
| const sleep = (seconds: number) => { | |
| return new Promise((resolve, _) => { | |
| setTimeout(resolve, seconds * ONE_SECOND); | |
| }); | |
| }; | |
| const someAsyncOperationWhereTheValueMayChange = () => { | |
| return new Promise((resolve, _) => { | |
| setTimeout(() => { | |
| resolve(Math.round(Math.random() * 999999999)); | |
| }, Math.round(MAX_SIMULATED_LATENCY * Math.random())); | |
| }); | |
| }; | |
| console.log("Read 1:", await cache.readOrAdd(CACHE_KEY, async () => await someAsyncOperationWhereTheValueMayChange())); | |
| console.log("Read 2:", await cache.readOrAdd(CACHE_KEY, async () => await someAsyncOperationWhereTheValueMayChange())); | |
| console.log("Read 3:", await cache.readOrAdd(CACHE_KEY, async () => await someAsyncOperationWhereTheValueMayChange())); | |
| await sleep(2); | |
| console.log("Read 4:", await cache.readOrAdd(CACHE_KEY, async () => await someAsyncOperationWhereTheValueMayChange())); | |
| console.log("Read 5:", await cache.readOrAdd(CACHE_KEY, async () => await someAsyncOperationWhereTheValueMayChange())); | |
| console.log("Read 6:", await cache.readOrAdd(CACHE_KEY, async () => await someAsyncOperationWhereTheValueMayChange())); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment