///|
pub(all) struct ObservationSeries {
name : String
window_minutes : Int
samples : Array[TrafficSample]
} derive(Eq, Debug)
///|
pub fn ObservationSeries::new(
name : String,
window_minutes : Int,
) -> ObservationSeries {
{
name,
window_minutes: if window_minutes > 0 {
window_minutes
} else {
1
},
samples: [],
}
}
///|
pub fn ObservationSeries::from_samples(
name : String,
window_minutes : Int,
samples : Array[TrafficSample],
) -> ObservationSeries {
let series = ObservationSeries::new(name, window_minutes)
for sample in samples {
series.samples.push(sample)
}
series
}
///|
pub fn ObservationSeries::add(
self : ObservationSeries,
sample : TrafficSample,
) -> ObservationSeries {
self.samples.push(sample)
self
}
///|
pub fn ObservationSeries::sample_count(self : ObservationSeries) -> Int {
self.samples.length()
}
///|
pub fn ObservationSeries::first_minute(self : ObservationSeries) -> Int {
if self.samples.length() == 0 {
0
} else {
self.samples[0].minute
}
}
///|
pub fn ObservationSeries::last_minute(self : ObservationSeries) -> Int {
let mut last = 0
for sample in self.samples {
if sample.minute > last {
last = sample.minute
}
}
last
}
///|
pub fn ObservationSeries::window_at(
self : ObservationSeries,
now_minute : Int,
) -> RequestWindow {
aggregate_recent_samples(
self.name,
now_minute,
self.window_minutes,
self.samples,
)
}
///|
pub fn ObservationSeries::window_from(
self : ObservationSeries,
start_minute : Int,
end_minute : Int,
) -> RequestWindow {
let mut total = 0
let mut good = 0
for sample in self.samples {
if sample.minute >= start_minute && sample.minute <= end_minute {
total = total + sample.total
good = good + sample.good
}
}
RequestWindow::new(
self.name,
if end_minute >= start_minute {
end_minute - start_minute + 1
} else {
1
},
total,
good,
)
}
///|
pub fn ObservationSeries::windows(
self : ObservationSeries,
) -> Array[RequestWindow] {
let result = []
for sample in self.samples {
result.push(self.window_at(sample.minute))
}
result
}
///|
pub fn ObservationSeries::budget_reports(
self : ObservationSeries,
target : SloTarget,
) -> Array[BudgetReport] {
let result = []
for window in self.windows() {
result.push(evaluate_budget(target, window))
}
result
}
///|
pub fn ObservationSeries::burn_reports(
self : ObservationSeries,
target : SloTarget,
) -> Array[BurnReport] {
let result = []
for window in self.windows() {
result.push(calculate_burn_rate(target, window))
}
result
}
///|
pub fn ObservationSeries::peak_burn(
self : ObservationSeries,
target : SloTarget,
) -> Int {
let mut peak = 0
for report in self.burn_reports(target) {
if report.burn_rate_x100 > peak {
peak = report.burn_rate_x100
}
}
peak
}
///|
pub fn ObservationSeries::to_json(self : ObservationSeries) -> String {
let result = StringBuilder()
result.write_string(
"{\"name\":\"\{escape_json(self.name)}\",\"window_minutes\":\{self.window_minutes},\"samples\":[",
)
for i, sample in self.samples {
if i > 0 {
result.write_string(",")
}
result.write_string(
"{\"minute\":\{sample.minute},\"total\":\{sample.total},\"good\":\{sample.good}}",
)
}
result.write_string("]}")
result.to_string()
}
///|
pub fn ObservationSeries::coverage_minutes(self : ObservationSeries) -> Int {
if self.samples.length() == 0 {
0
} else {
self.last_minute() - self.first_minute() + 1
}
}
///|
pub fn ObservationSeries::is_continuous(self : ObservationSeries) -> Bool {
if self.samples.length() <= 1 {
true
} else {
for i in 1..