From cf9490ce047bac880ebbfc80693b9dd5636f9764 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Mon, 17 Sep 2018 15:53:59 -0700 Subject: [PATCH] New service to manage stats trackers This solves two problems: 1. redundant trackers making redundant requests 2. trackers being obliterated as soon as the primary metric component is destroyed It introduces a new problem where visiting more and more node and allocation pages adds to an ever-growing list of trackers that can assume lots of memory, but it solves the problem by using a least-recently-used cache to limit the number of trackers tracked. --- ui/app/services/stats-trackers-registry.js | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 ui/app/services/stats-trackers-registry.js diff --git a/ui/app/services/stats-trackers-registry.js b/ui/app/services/stats-trackers-registry.js new file mode 100644 index 000000000..81a8540b0 --- /dev/null +++ b/ui/app/services/stats-trackers-registry.js @@ -0,0 +1,43 @@ +import Service, { inject as service } from '@ember/service'; +import { LRUMap } from 'lru_map'; +import NodeStatsTracker from 'nomad-ui/utils/classes/node-stats-tracker'; +import AllocationStatsTracker from 'nomad-ui/utils/classes/allocation-stats-tracker'; + +// An unbounded number of stat trackers is a great way to gobble up all the memory +// on a machine. This max number is unscientific, but aims to balance losing +// stat trackers a user is likely to return to with preventing gc from freeing +// memory occupied by stat trackers a user is likely to no longer care about +const MAX_STAT_TRACKERS = 10; +let registry; + +export default Service.extend({ + token: service(), + + init() { + // The LRUMap limits the number of trackers tracked by making room for + // new entries beyond the limit by removing the least recently used entry. + registry = new LRUMap(MAX_STAT_TRACKERS); + }, + + getTracker(resource) { + if (!resource) return; + + const type = resource && resource.constructor.modelName; + const key = `${type}:${resource.get('id')}`; + + const cachedTracker = registry.get(key); + if (cachedTracker) return cachedTracker; + + const Constructor = type === 'node' ? NodeStatsTracker : AllocationStatsTracker; + const resourceProp = type === 'node' ? 'node' : 'allocation'; + + const tracker = Constructor.create({ + fetch: url => this.get('token').authorizedRequest(url), + [resourceProp]: resource, + }); + + registry.set(key, tracker); + + return tracker; + }, +});