UNPKG

flexmonster-mongo-connector

Version:

MongoDB connector for Flexmonster Pivot Table and Charts

173 lines (142 loc) 7.18 kB
import { IDataCache } from "../IDataCache"; import { ICacheStrategie } from "../cacheStrategies/ICacheStrategie"; import { ConfigInterface } from "../../config/ConfigInterface"; import { AbstractDataObject } from "../dataObject/impl/AbstractDataObject"; import { DataRetrievalInterface } from "../dataObject/DataRetrievalInterface"; import { CacheKeyInterface } from "../DataManager"; import { HashGenerator } from "../../utils/HashGenerator"; export class LocalDataCache implements IDataCache<CacheKeyInterface, any> { private _cache: Map<string, CachedDataObject> = null; private timeToLive: number = 0; //TTL for data objects in minutes, default 0 means we are not going to update cache private _cacheSizeLimit: number = 0; // mb private _cacheStrategie: ICacheStrategie; private _currentCacheSize: number = 0; //bytes private readonly _garbageCollectingCoefficient: number = 0.6; // when reaching cache size limit we keep 60% of data and remove 40% constructor(cacheStrategie: ICacheStrategie, config: ConfigInterface) { this._cache = new Map<string, any>(); this._cacheStrategie = cacheStrategie; this.timeToLive = config.cacheTimeToLive; this._cacheSizeLimit = config.cacheMemoryLimit * 1024 * 1024; this._currentCacheSize = 0; } setTimeToLive(minutes: number): void { if (minutes < 0) return; this.timeToLive = minutes; } // public hasKey(key: CacheKeyInterface): boolean { // if (key == null) throw new Error("Null key exception"); // return this._cache.has(key); // } public getCache(key: CacheKeyInterface): DataRetrievalInterface { if (key == null) throw new Error("Null key exception"); const keyHash: string = this.getCacheKey(key); let cachedDataObject: CachedDataObject = this._cache.get(keyHash); if (typeof cachedDataObject !== "undefined" && this._cacheStrategie.isCacheStaled(cachedDataObject, this.timeToLive)) { // console.log(">>>>>stalled", ((new Date().getTime() - cachedDataObject.timeStamp) / (1000 * 60))); cachedDataObject = undefined; this.removeFromCache(key); } return typeof cachedDataObject === "undefined" ? undefined : cachedDataObject.data; } // private isDataObjectStaled(cachedDataObject: CachedDataObject): boolean { // if (cachedDataObject == null) return true; // const timeLive: number = (new Date().getTime() - cachedDataObject.timeStamp) / 1000; // in seconds // return this.timeToLive > 0 && timeLive > this.timeToLive; // } // private isProbabilisticCacheFlushNeeded(cachedDataObject: CachedDataObject, beta: number = 1): boolean { // if (cachedDataObject == null) return true; // return new Date().getTime() - cachedDataObject.computationTime * beta * Math.log(Math.random()) // > cachedDataObject.timeStamp + this.timeToLive * 1000; // } private removeFromCache(key: CacheKeyInterface): void { if (key == null) throw new Error("Null key exception"); const keyHash: string = this.getCacheKey(key); const cachedItem: CachedDataObject = this._cache.get(keyHash); if (typeof cachedItem === "undefined") return; this._removeFromCache(keyHash); this._currentCacheSize-= cachedItem.dataMemorySize; cachedItem.data = null; return; } private _removeFromCache(keyHash: string): void { this._cache.delete(keyHash); } public setCache(key: CacheKeyInterface, data: AbstractDataObject): void { const keyHash: string = this.getCacheKey(key); this._cache.set(keyHash, { data: data, timeStamp: new Date().getTime(), computationTime: data.computationTime, dataMemorySize: data.dataMemorySize } ); this._currentCacheSize+= data.dataMemorySize; if (this._cacheSizeLimit > 0 && this._currentCacheSize > this._cacheSizeLimit) this.collectGabbage(); } private collectGabbage(): void { //console.log(">>>>>>", this.getCacheMemoryStatus()); const entries = this._cache.entries(); let entriesList: GarbageColletingItemsInterface[] = []; for (const entry of entries) { entriesList.push({ key: entry[0], timeStamp: entry[1].timeStamp, computationTime: entry[1].computationTime, dataMemorySize: entry[1].dataMemorySize, isCacheStaled: this._cacheStrategie.isCacheStaled(entry[1], this.timeToLive) }); } entriesList.sort(this.garbageItemsComparator); let garbageCollectingItem: GarbageColletingItemsInterface = null; while (this._cacheSizeLimit * this._garbageCollectingCoefficient <= this._currentCacheSize) { garbageCollectingItem = entriesList.shift(); this._removeFromCache(garbageCollectingItem.key); } entriesList = null; //console.log(">>>>>>", this.getCacheMemoryStatus()); } private garbageItemsComparator: (a: GarbageColletingItemsInterface, b: GarbageColletingItemsInterface) => number = (a: GarbageColletingItemsInterface, b: GarbageColletingItemsInterface) => { const aScore: number = this.getGarbageItemScore(a); const bScore: number = this.getGarbageItemScore(b); return aScore - bScore; } private getGarbageItemScore(item: GarbageColletingItemsInterface): number { if (item.isCacheStaled) return cacheScoreMetrics.StaledCache; return item.computationTime; } //private refreshMemory(): void {} public setCacheStrategie(cacheStrategie: ICacheStrategie) { if (cacheStrategie == null) return; this._cacheStrategie = cacheStrategie; } public clearCache(): void { this._cache.clear(); this._currentCacheSize = 0; } private getCacheKey(objectKey: CacheKeyInterface): string { if (objectKey == null) throw new Error("Null cache object key exception"); const objectKeyString: string = JSON.stringify(objectKey.clientQuery) + objectKey.databaseName + objectKey.index; return HashGenerator.createHashFromString(objectKeyString); } public getCacheMemoryStatus(): string { return `Current cache size: ${(this._currentCacheSize / (1024 * 1024)).toFixed(2)} MB`; } } export interface CachedDataObject { data: DataRetrievalInterface; timeStamp: number;//timeStamp for data added to cache (in miliseconds) computationTime: number; dataMemorySize: number; } export interface GarbageColletingItemsInterface { key: string, timeStamp: number;//timeStamp for data added to cache (in miliseconds) computationTime: number; dataMemorySize: number; isCacheStaled: boolean; } const cacheScoreMetrics = { StaledCache: -300 }