Functional Weave
Code in Rust

monitor.mttr@1.0.0

impl/typescript.ts

2,337 bytes · the TypeScript implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import { type Incident } from "./monitor_incidents_types.ts";
import { roundDiv } from "./math_round_div.ts";  ← from math.round-div ^1.0.0 · built alongside by fune
import { type ReliabilityStats } from "./monitor_mttr_types.ts";

/**
 * Reliability figures for the window from <= t < to. An incident counts when
 * it overlaps the window; downtime is clipped to it, but MTTR and the
 * longest incident use full durations, because an outage that began last
 * month still took as long as it took to fix.
 */
export function reliabilityStats(incidents: readonly Incident[], from: number, to: number): ReliabilityStats {
  if (!Number.isSafeInteger(from) || !Number.isSafeInteger(to)) throw new RangeError("from and to must be whole seconds");
  if (from > to) throw new RangeError(`from must not be after to: ${from} > ${to}`);
  let prevEnd: number | null = null;
  let count = 0;
  let resolved = 0;
  let downtime = 0;
  let repairTotal = 0;
  let longest = 0;
  for (const x of incidents) {
    if (x.durationSeconds < 0) throw new RangeError(`durationSeconds must not be negative, received ${x.durationSeconds}`);
    if (x.end !== null && x.end !== undefined) {
      if (x.end < x.start) throw new RangeError(`incident end must not be before its start: ${x.end} < ${x.start}`);
      if (x.durationSeconds !== x.end - x.start) {
        throw new RangeError(`durationSeconds must equal end - start: ${x.durationSeconds} != ${x.end - x.start}`);
      }
    }
    // An ongoing incident ends, for now, where its duration says.
    const end = x.start + x.durationSeconds;
    if (prevEnd !== null && x.start < prevEnd) {
      throw new RangeError(`incidents must be in time order and must not overlap: ${x.start} is before ${prevEnd}`);
    }
    prevEnd = end;
    if (!(x.start < to && (x.start >= from || end > from))) continue;
    count++;
    downtime += Math.max(0, Math.min(end, to) - Math.max(x.start, from));
    if (x.end !== null && x.end !== undefined) {
      resolved++;
      repairTotal += x.durationSeconds;
    }
    if (x.durationSeconds > longest) longest = x.durationSeconds;
  }
  return {
    incidents: count,
    resolved,
    downtimeSeconds: downtime,
    mttrSeconds: resolved > 0 ? roundDiv(repairTotal, resolved, "half-up") : null,
    mtbfSeconds: count > 0 ? roundDiv(to - from - downtime, count, "half-up") : null,
    longestSeconds: longest,
  };
}