{"id":"9GkN25dHc6","url":"https://pastebin.ca/9GkN25dHc6","raw_url":"https://raw.anybin.ca/9GkN25dHc6","visibility":"public","access":"public","created_at":1781150565693,"expires_at":null,"fetch_limit":null,"fetches_used":0,"reads_remaining":null,"size_bytes":2890,"syntax_hint":"hew","title":"Hew metrics aggregation demo","filename":"metrics-aggregator.hew","change_note":null,"cipher":null,"cipher_meta":null,"parent_id":null,"root_id":"9GkN25dHc6","version":1,"owner_id":"06F6D6Y710X9HQV90HF93KWQT8","recipient_id":null,"body":"//! A small metrics aggregation service: sources push samples to a\n//! collector actor, a periodic ticker counts snapshots, and asks read\n//! the aggregates back out.\n\ntype Sample {\n    name: string;\n    value: f64;\n}\n\nactor Collector {\n    var counts: HashMap<string, i64> = HashMap::new();\n    var sums: HashMap<string, f64> = HashMap::new();\n    var snapshots_taken: i64 = 0;\n\n    receive fn observe(name: string, value: f64) {\n        let prior_count = match counts.get(name) {\n            Some(c) => c,\n            None => 0,\n        };\n        counts.insert(name, prior_count + 1);\n        let prior_sum = match sums.get(name) {\n            Some(s) => s,\n            None => 0.0,\n        };\n        sums.insert(name, prior_sum + value);\n    }\n\n    #[every(40ms)]\n    receive fn snapshot() {\n        snapshots_taken = snapshots_taken + 1;\n    }\n\n    receive fn mean(name: string) -> f64 {\n        let c = match counts.get(name) {\n            Some(c) => c,\n            None => 0,\n        };\n        if c == 0 {\n            return 0.0;\n        }\n        let s = match sums.get(name) {\n            Some(s) => s,\n            None => 0.0,\n        };\n        s / (c as f64)\n    }\n\n    receive fn count(name: string) -> i64 {\n        match counts.get(name) {\n            Some(c) => c,\n            None => 0,\n        }\n    }\n\n    receive fn snapshots() -> i64 {\n        snapshots_taken\n    }\n}\n\nfn percentile_label(p: f64) -> string {\n    match p {\n        v if v >= 0.99 => \"p99\",\n        v if v >= 0.95 => \"p95\",\n        v if v >= 0.5 => \"p50\",\n        _ => \"low\",\n    }\n}\n\nfn main() {\n    let collector = spawn Collector();\n\n    // Sends from inside a function closure are not yet implemented\n    // (captured pid loses its actor layout in the closure's MIR context);\n    // push samples from plain loops instead.\n    var i = 0;\n    while i < 100 {\n        collector.observe(\"api.latency\", 10.0 + ((i % 50) as f64));\n        i = i + 1;\n    }\n    var j = 0;\n    while j < 50 {\n        collector.observe(\"db.latency\", 3.0 + (j as f64));\n        j = j + 1;\n    }\n\n    sleep_ms(200);\n\n    match await collector.count(\"api.latency\") {\n        Ok(n) => println(f\"api.latency samples: {n}\"),\n        Err(_) => println(\"ask failed: count\"),\n    }\n    match await collector.mean(\"api.latency\") {\n        Ok(m) => println(f\"api.latency mean: {m}\"),\n        Err(_) => println(\"ask failed: mean\"),\n    }\n    match await collector.mean(\"db.latency\") {\n        Ok(m) => println(f\"db.latency mean: {m}\"),\n        Err(_) => println(\"ask failed: mean\"),\n    }\n    match await collector.snapshots() {\n        Ok(s) => {\n            if s >= 3 {\n                println(\"periodic snapshots: ok\");\n            } else {\n                println(f\"periodic snapshots: only {s}\");\n            }\n        },\n        Err(_) => println(\"ask failed: snapshots\"),\n    }\n    println(f\"label: {percentile_label(0.97)}\");\n}\n"}