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  }