AxionserveSign in / RegisterEnglish/中文/cn繁体/tw

cost-calc.js (FIXED)

AxionServe-Docsai-code2026-10-13♥ 0
🤖 AIGC disclosure: created with “AI-assisted generation + human testing”, published after community-terms review.
#!/usr/bin/env node
// cost-calc.js — bandwidth / traffic / 95th-percentile billing calculator (zero dependencies, Node 18+)
// Usage:
//   node cost-calc.js traffic 100
//   node cost-calc.js p95 samples.txt
//   node cost-calc.js compare 85 2.5 "100:184.8,1000:1057.92"
"use strict";
const fs = require("fs");

const HOURS = 720; // a 30-day month

function traffic(portMbps) {
  const fullTB = (portMbps * 0.125 * 3600 * HOURS) / 1000 / 1000; // Mbps→MB/s→GB→TB
  return {
    port_mbps: portMbps,
    full_load_tb: +fullTB.toFixed(1),
    at_10pct_tb: +(fullTB * 0.1).toFixed(1),
    at_30pct_tb: +(fullTB * 0.3).toFixed(1),
    at_50pct_tb: +(fullTB * 0.5).toFixed(1),
  };
}

function p95(file) {
  const xs = fs.readFileSync(file, "utf8").split(/\s+/)
    .map(Number).filter((n) => Number.isFinite(n)).sort((a, b) => a - b);
  if (!xs.length) throw new Error("sample file is empty or not numeric");
  const q = (p) => xs[Math.min(xs.length - 1, Math.floor(xs.length * p))];
  return { n: xs.length, p50: q(0.5), p95: q(0.95), max: xs[xs.length - 1] };
}

function compare(p95Mbps, unit95, tiersStr) {
  const tiers = tiersStr.split(",").map((t) => {
    const [mbps, price] = t.split(":").map(Number);
    return { mbps, price };
  }).filter((t) => t.mbps >= p95Mbps).sort((a, b) => a.price - b.price);
  const flat = tiers[0] || null;               // smallest usable flat tier
  const billing95 = p95Mbps * unit95;          // 95th-percentile billing
  const peakEst = p95Mbps * 1.5 * unit95;      // rough peak-billing estimate (peak ≈ P95×1.5 rule of thumb)
  const opts = [
    { mode: "95th-percentile", monthly: +billing95.toFixed(2) },
    flat && { mode: `flat-port-${flat.mbps}M`, monthly: +flat.price.toFixed(2) },
    { mode: "peak-estimate", monthly: +peakEst.toFixed(2), note: "for reference, usually the most expensive" },
  ].filter(Boolean);
  const best = opts.slice().sort((a, b) => a.monthly - b.monthly)[0];
  return {
    inputs: { p95_mbps: p95Mbps, unit95_per_mbps: unit95 },
    options: opts,
    recommendation: best.mode,
    ratio_p95_over_peak: "P95/peak > 0.7 favours a flat port; < 0.4 favours 95th-percentile billing (see the bandwidth-market article)",
  };
}

// ── Built-in self-test (node cost-calc.js test) ─────────────
function test() {
  const assert = require("assert");
  assert.strictEqual(traffic(100).full_load_tb, 32.4);       // 100M at full load = 32.4 TB/month
  assert.strictEqual(traffic(8).full_load_tb, 2.6);
  const c = compare(85, 2.5, "100:184.8,1000:1057.92");
  assert.strictEqual(c.options[0].monthly, 212.5);            // 85×2.5
  assert.strictEqual(c.recommendation, "flat-port-100M");     // 184.8 < 212.5
  console.log("self-test ok");
}

if (require.main === module) {
  const [cmd, ...args] = process.argv.slice(2);
  const out =
    cmd === "traffic" ? traffic(+args[0]) :
    cmd === "p95"     ? p95(args[0]) :
    cmd === "compare" ? compare(+args[0], +args[1], args[2]) :
    cmd === "test"    ? test() :
    { error: "usage: traffic <Mbps> | p95 <file> | compare <P95> <unit price> <tier list> | test" };
  if (out !== undefined) console.log(JSON.stringify(out, null, 1));
} else {
  module.exports = { traffic, p95, compare };
}

──────────────────────
[Test log · fixed version] `node cost-calc.js test` now passes: traffic(100) = 32.4 TB, matching the hand calculation; the compare assertions hold (85 Mbps × $2.5 = $212.5 > the $184.8 flat tier → flat port recommended).
[What was fixed] The TB conversion divided by 1000 once instead of twice, so every traffic figure was 1000× too large (100 Mbps full load printed 32400 "TB" instead of 32.4 TB) and the built-in self-test failed. The fix is `/ 1000 / 1000` (Mbps → MB/s → GB → TB); no other line changed.
[Where the samples come from] A cron job writes one line every 5 minutes with the interface rate (vnstat, or a diff of /proc/net/dev); a week gives you 2,016 samples — after that, P95 is no longer a guess.
[Scope limits] The ×1.5 in peak-estimate is a rule of thumb, not a promise; the sampling granularity (5 minutes) and the exempted percentage (5%) of 95th-percentile billing follow the provider's contract.
Reproduction prompt (full)
Write a zero-dependency Node.js script cost-calc.js with three subcommands: (1) traffic <portMbps> — output the theoretical full-load monthly traffic and the monthly traffic at common duty cycles (10% / 30% / 50%); (2) p95 <sampleFile> — read a file with one Mbps sample per line and output P50 / P95 / peak; (3) compare <P95Mbps> <95th unit price $/Mbps> <flat tier list e.g. 100:184.8,1000:1057> — compare the three billing modes (95th percentile / flat port / peak estimated as P95×1.5) and give a recommendation. Usable both as a CLI and via require(), with unit-test assertions (node --test style or built-in assert).
♥ 0 likesMembers: sign in to likeReport
Related worksmtr-report.pyfirst-hour-init.shWhy datacenters are called HKG, SIN and NRT