/** * Copyright (c) 2020 TypeFox GmbH. All rights reserved. * Licensed under the GNU Affero General Public License (AGPL). * See License-AGPL.txt in the project root for license information. */ interface CacheEntry { key: string; value: T; expiryDate: number; } export class GarbageCollectedCache { protected readonly store = new Map>(); constructor( protected readonly defaultMaxAgeSeconds: number, protected readonly gcIntervalSeconds: number) { this.regularlyCollectGarbage(); } public set(key: string, value: T) { const oldValue = this.store.get(key); if (oldValue) { // We don't want values to be cached indefinitely just because their queried often return; } this.store.set(key, { key, value, expiryDate: this.calcExpiryDate(), }); } public get(key: string): T | undefined { const entry = this.store.get(key); if (!entry) { return undefined; } return entry.value; } protected regularlyCollectGarbage() { setInterval(() => this.collectGarbage(), this.gcIntervalSeconds * 1000); } protected collectGarbage() { const now = Date.now(); for (const entry of this.store.values()) { if (entry.expiryDate < now) { this.store.delete(entry.key); } } } protected calcExpiryDate(maxAgeSeconds?: number): number { return Date.now() + ((maxAgeSeconds || this.defaultMaxAgeSeconds) * 1000); } }