github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/app/utils/classes/allocation-stats-tracker.js (about) 1 import EmberObject, { get, computed } from '@ember/object'; 2 import { alias } from '@ember/object/computed'; 3 import RollingArray from 'nomad-ui/utils/classes/rolling-array'; 4 import AbstractStatsTracker from 'nomad-ui/utils/classes/abstract-stats-tracker'; 5 import classic from 'ember-classic-decorator'; 6 7 const percent = (numerator, denominator) => { 8 if (!numerator || !denominator) { 9 return 0; 10 } 11 return numerator / denominator; 12 }; 13 14 const empty = (ts) => ({ timestamp: ts, used: null, percent: null }); 15 16 // Tasks are sorted by their lifecycle phase in this order: 17 const sortMap = [ 18 'main', 19 'prestart-sidecar', 20 'poststart-sidecar', 21 'prestart-ephemeral', 22 'poststart-ephemeral', 23 'poststop', 24 ].reduce((map, phase, index) => { 25 map[phase] = index; 26 return map; 27 }, {}); 28 29 const taskPrioritySort = (a, b) => 30 sortMap[a.lifecycleName] - sortMap[b.lifecycleName]; 31 32 @classic 33 class AllocationStatsTracker extends EmberObject.extend(AbstractStatsTracker) { 34 // Set via the stats computed property macro 35 allocation = null; 36 37 @computed('allocation.id') 38 get url() { 39 return `/v1/client/allocation/${this.get('allocation.id')}/stats`; 40 } 41 42 append(frame) { 43 const timestamp = new Date(Math.floor(frame.Timestamp / 1000000)); 44 45 const cpuUsed = Math.floor(frame.ResourceUsage.CpuStats.TotalTicks) || 0; 46 this.cpu.pushObject({ 47 timestamp, 48 used: cpuUsed, 49 percent: percent(cpuUsed, this.reservedCPU), 50 }); 51 52 const memoryUsed = 53 frame.ResourceUsage.MemoryStats.Usage || 54 frame.ResourceUsage.MemoryStats.RSS || 55 0; 56 57 this.memory.pushObject({ 58 timestamp, 59 used: memoryUsed, 60 percent: percent(memoryUsed / 1024 / 1024, this.reservedMemory), 61 }); 62 63 let aggregateCpu = 0; 64 let aggregateMemory = 0; 65 for (var stats of this.tasks) { 66 const taskFrame = frame.Tasks[stats.task]; 67 68 // If the task is not present in the frame data (because it hasn't started or 69 // it has already stopped), just keep going. 70 if (!taskFrame) continue; 71 72 const frameTimestamp = new Date( 73 Math.floor(taskFrame.Timestamp / 1000000) 74 ); 75 76 const taskCpuUsed = 77 Math.floor(taskFrame.ResourceUsage.CpuStats.TotalTicks) || 0; 78 const percentCpuTotal = percent(taskCpuUsed, this.reservedCPU); 79 stats.cpu.pushObject({ 80 timestamp: frameTimestamp, 81 used: taskCpuUsed, 82 percent: percent(taskCpuUsed, stats.reservedCPU), 83 percentTotal: percentCpuTotal, 84 percentStack: percentCpuTotal + aggregateCpu, 85 }); 86 87 const taskMemoryUsed = 88 taskFrame.ResourceUsage.MemoryStats.Usage || 89 taskFrame.ResourceUsage.MemoryStats.RSS || 90 0; 91 92 const percentMemoryTotal = percent( 93 taskMemoryUsed / 1024 / 1024, 94 this.reservedMemory 95 ); 96 stats.memory.pushObject({ 97 timestamp: frameTimestamp, 98 used: taskMemoryUsed, 99 percent: percent(taskMemoryUsed / 1024 / 1024, stats.reservedMemory), 100 percentTotal: percentMemoryTotal, 101 percentStack: percentMemoryTotal + aggregateMemory, 102 }); 103 104 aggregateCpu += percentCpuTotal; 105 aggregateMemory += percentMemoryTotal; 106 } 107 } 108 109 pause() { 110 const ts = new Date(); 111 this.memory.pushObject(empty(ts)); 112 this.cpu.pushObject(empty(ts)); 113 this.tasks.forEach((task) => { 114 task.memory.pushObject(empty(ts)); 115 task.cpu.pushObject(empty(ts)); 116 }); 117 } 118 119 // Static figures, denominators for stats 120 @alias('allocation.taskGroup.reservedCPU') reservedCPU; 121 @alias('allocation.taskGroup.reservedMemory') reservedMemory; 122 123 // Dynamic figures, collected over time 124 // []{ timestamp: Date, used: Number, percent: Number } 125 @computed('allocation', 'bufferSize') 126 get cpu() { 127 return RollingArray(this.bufferSize); 128 } 129 130 @computed('allocation', 'bufferSize') 131 get memory() { 132 return RollingArray(this.bufferSize); 133 } 134 135 @computed('allocation.taskGroup.tasks', 'bufferSize') 136 get tasks() { 137 const bufferSize = this.bufferSize; 138 const tasks = this.get('allocation.taskGroup.tasks') || []; 139 return tasks 140 .slice() 141 .sort(taskPrioritySort) 142 .map((task) => ({ 143 task: get(task, 'name'), 144 145 // Static figures, denominators for stats 146 reservedCPU: get(task, 'reservedCPU'), 147 reservedMemory: get(task, 'reservedMemory'), 148 149 // Dynamic figures, collected over time 150 // []{ timestamp: Date, used: Number, percent: Number } 151 cpu: RollingArray(bufferSize), 152 memory: RollingArray(bufferSize), 153 })); 154 } 155 } 156 157 export default AllocationStatsTracker; 158 159 export function stats(allocationProp, fetch) { 160 return computed(allocationProp, function () { 161 return AllocationStatsTracker.create({ 162 fetch: fetch.call(this), 163 allocation: this.get(allocationProp), 164 }); 165 }); 166 }