///|
pub(all) struct RequestEvent {
minute : Int
succeeded : Bool
latency_ms : Int
} derive(Eq, Debug)
///|
pub fn RequestEvent::new(
minute : Int,
succeeded : Bool,
latency_ms : Int,
) -> RequestEvent {
{
minute,
succeeded,
latency_ms: if latency_ms >= 0 {
latency_ms
} else {
0
},
}
}
///|
pub fn RequestEvent::failed(self : RequestEvent) -> Bool {
!self.succeeded
}
///|
pub fn RequestEvent::latency_band(
self : RequestEvent,
slow_after_ms : Int,
) -> String {
if self.latency_ms >= slow_after_ms {
"slow"
} else {
"normal"
}
}
///|
pub(all) struct LatencySummary {
count : Int
average_ms : Int
min_ms : Int
max_ms : Int
slow_count : Int
} derive(Eq, Debug)
///|
pub fn LatencySummary::empty() -> LatencySummary {
{ count: 0, average_ms: 0, min_ms: 0, max_ms: 0, slow_count: 0 }
}
///|
pub fn LatencySummary::healthy(self : LatencySummary, budget_ms : Int) -> Bool {
self.count == 0 || self.average_ms <= budget_ms
}
///|
pub fn LatencySummary::to_json(self : LatencySummary) -> String {
"{\"count\":\{self.count},\"average_ms\":\{self.average_ms},\"min_ms\":\{self.min_ms},\"max_ms\":\{self.max_ms},\"slow_count\":\{self.slow_count}}"
}
///|
pub(all) struct RequestBatch {
name : String
events : Array[RequestEvent]
} derive(Eq, Debug)
///|
pub fn RequestBatch::new(name : String) -> RequestBatch {
{ name, events: [] }
}
///|
pub fn RequestBatch::add(
self : RequestBatch,
event : RequestEvent,
) -> RequestBatch {
self.events.push(event)
self
}
///|
pub fn RequestBatch::total(self : RequestBatch) -> Int {
self.events.length()
}
///|
pub fn RequestBatch::succeeded(self : RequestBatch) -> Int {
let mut count = 0
for event in self.events {
if event.succeeded {
count = count + 1
}
}
count
}
///|
pub fn RequestBatch::failed(self : RequestBatch) -> Int {
self.total() - self.succeeded()
}
///|
pub fn RequestBatch::minutes(self : RequestBatch) -> Int {
let mut first = 0
let mut last = 0
let mut initialized = false
for event in self.events {
if !initialized {
first = event.minute
last = event.minute
initialized = true
} else {
if event.minute < first {
first = event.minute
}
if event.minute > last {
last = event.minute
}
}
}
if initialized {
last - first + 1
} else {
0
}
}
///|
pub fn RequestBatch::slow_count(
self : RequestBatch,
slow_after_ms : Int,
) -> Int {
let mut count = 0
for event in self.events {
if event.latency_ms >= slow_after_ms {
count = count + 1
}
}
count
}
///|
pub fn RequestBatch::latency(
self : RequestBatch,
slow_after_ms : Int,
) -> LatencySummary {
if self.events.length() == 0 {
LatencySummary::empty()
} else {
let mut total = 0
let mut min_ms = self.events[0].latency_ms
let mut max_ms = self.events[0].latency_ms
for event in self.events {
total = total + event.latency_ms
if event.latency_ms < min_ms {
min_ms = event.latency_ms
}
if event.latency_ms > max_ms {
max_ms = event.latency_ms
}
}
{
count: self.events.length(),
average_ms: total / self.events.length(),
min_ms,
max_ms,
slow_count: self.slow_count(slow_after_ms),
}
}
}
///|
pub fn RequestBatch::to_window(
self : RequestBatch,
minutes : Int,
) -> RequestWindow {
RequestWindow::new(self.name, minutes, self.total(), self.succeeded())
}
///|
pub fn RequestBatch::to_series(
self : RequestBatch,
window_minutes : Int,
) -> ObservationSeries {
let series = ObservationSeries::new(self.name, window_minutes)
for event in self.events {
let good = if event.succeeded { 1 } else { 0 }
let _ = series.add(TrafficSample::new(event.minute, 1, good))
}
series
}
///|
pub fn RequestBatch::to_json(self : RequestBatch) -> String {
let result = StringBuilder()
result.write_string("{\"name\":\"\{escape_json(self.name)}\",\"events\":[")
for i, event in self.events {
if i > 0 {
result.write_string(",")
}
result.write_string(
"{\"minute\":\{event.minute},\"succeeded\":\{event.succeeded},\"latency_ms\":\{event.latency_ms}}",
)
}
result.write_string("]}")
result.to_string()
}
///|
pub fn merge_batches(left : RequestBatch, right : RequestBatch) -> RequestBatch {
let result = RequestBatch::new(left.name)
for event in left.events {
let _ = result.add(event)
}
for event in right.events {
let _ = result.add(event)
}
result
}
///|
pub fn batch_availability_bp(batch : RequestBatch) -> Int {
if batch.total() == 0 {
10000
} else {
batch.succeeded() * 10000 / batch.total()
}
}
///|
pub fn batch_error_rate_bp(batch : RequestBatch) -> Int {
10000 - batch_availability_bp(batch)
}