pub mod alumet;
pub mod carbon;
mod carbon_data;
pub(crate) mod carbon_profiles;
pub mod cloud_energy;
pub mod electricity_maps;
#[cfg(feature = "daemon")]
pub(crate) mod energy_state;
pub mod kepler;
#[cfg(feature = "daemon")]
pub(crate) mod ops_snapshot_diff;
pub mod prom_parser;
pub mod redfish;
pub mod scaphandre;
#[cfg(feature = "daemon")]
pub(crate) mod canonical;
mod carbon_compute;
mod region_breakdown;
use std::collections::HashMap;
use crate::correlate::Trace;
use crate::detect::{Finding, FindingType, GreenImpact};
use crate::event::EventType;
use crate::report::{DatabaseWaste, GreenSummary, PerEndpointIoOps, TopOffender};
use carbon::CarbonContext;
#[cfg(test)]
use carbon::RegionBreakdown;
use carbon_compute::compute_carbon_report;
struct EndpointStats {
total_io_ops: usize,
invocation_count: usize,
last_seen_trace: usize,
}
type EndpointKey<'a> = (&'a str, &'a str);
fn count_endpoint_stats(
traces: &[Trace],
) -> (HashMap<EndpointKey<'_>, EndpointStats>, usize, usize) {
let mut endpoint_stats: HashMap<EndpointKey<'_>, EndpointStats> =
HashMap::with_capacity(traces.len().min(64));
let mut total_io_ops: usize = 0;
let mut total_sql_io_ops: usize = 0;
for (trace_idx, trace) in traces.iter().enumerate() {
for span in &trace.spans {
total_io_ops += 1;
if matches!(span.event.event_type, EventType::Sql) {
total_sql_io_ops += 1;
}
let key: EndpointKey<'_> = (
span.event.service.as_ref(),
span.event.source.endpoint.as_str(),
);
let stats = endpoint_stats.entry(key).or_insert_with(|| EndpointStats {
total_io_ops: 0,
invocation_count: 0,
last_seen_trace: usize::MAX,
});
stats.total_io_ops += 1;
if stats.last_seen_trace != trace_idx {
stats.invocation_count += 1;
stats.last_seen_trace = trace_idx;
}
}
}
(endpoint_stats, total_io_ops, total_sql_io_ops)
}
fn endpoint_stats_to_per_endpoint_io_ops(
endpoint_stats: &HashMap<EndpointKey<'_>, EndpointStats>,
) -> Vec<PerEndpointIoOps> {
let mut refs: Vec<(&str, &str, usize)> = endpoint_stats
.iter()
.map(|((service, endpoint), stats)| (*service, *endpoint, stats.total_io_ops))
.collect();
refs.sort_by(|a, b| a.0.cmp(b.0).then_with(|| a.1.cmp(b.1)));
refs.into_iter()
.map(|(service, endpoint, io_ops)| PerEndpointIoOps {
service: service.to_string(),
endpoint: endpoint.to_string(),
io_ops,
})
.collect()
}
#[must_use]
pub fn score_green(
traces: &[Trace],
findings: Vec<Finding>,
carbon: Option<&CarbonContext>,
) -> (Vec<Finding>, GreenSummary, Vec<PerEndpointIoOps>) {
let (endpoint_stats, total_io_ops, total_sql_io_ops) = count_endpoint_stats(traces);
let per_endpoint_io_ops = endpoint_stats_to_per_endpoint_io_ops(&endpoint_stats);
let avoidable = dedup_avoidable_io_ops(&findings);
let avoidable_io_ops = avoidable.total;
let iis_map = build_iis_map(&endpoint_stats);
let enriched = enrich_findings_with_iis(findings, &iis_map);
let carbon_outputs = match carbon {
Some(ctx) => compute_carbon_report(traces, ctx, total_io_ops, avoidable_io_ops),
None => carbon_compute::CarbonComputeOutputs {
report: None,
regions: Vec::new(),
multi_region_active: false,
per_service: std::collections::BTreeMap::new(),
window_model: "",
accounted_io_ops: total_io_ops,
sql_energy_kwh: 0.0,
sql_gco2: 0.0,
},
};
let default_region_lower = top_offender_co2_region(carbon, carbon_outputs.multi_region_active);
let top_offenders =
build_top_offenders(&endpoint_stats, &iis_map, default_region_lower.as_deref());
let io_waste_ratio = if total_io_ops > 0 {
avoidable_io_ops as f64 / total_io_ops as f64
} else {
0.0
};
let database_waste =
build_database_waste(carbon, &carbon_outputs, total_sql_io_ops, avoidable.sql);
let window_model = carbon_outputs.window_model;
let per_service = build_per_service_maps(carbon_outputs.per_service, window_model);
let energy_model = if per_service.energy_kwh > 0.0 {
window_model.to_string()
} else {
String::new()
};
let co2 = carbon_outputs.report;
let green_summary = GreenSummary {
total_io_ops,
avoidable_io_ops,
total_sql_io_ops,
avoidable_sql_io_ops: avoidable.sql,
accounted_io_ops: carbon_outputs.accounted_io_ops,
io_waste_ratio,
io_waste_ratio_band: crate::report::interpret::InterpretationLevel::for_waste_ratio(
io_waste_ratio,
),
top_offenders,
transport_gco2: co2.as_ref().and_then(|r| r.transport_gco2),
co2,
regions: carbon_outputs.regions,
scoring_config: carbon.and_then(|ctx| ctx.scoring_config.clone()),
energy_kwh: per_service.energy_kwh,
energy_model,
per_service_carbon_kgco2eq: per_service.carbon_kgco2eq,
per_service_energy_kwh: per_service.energy_kwh_by_service,
per_service_region: per_service.region,
per_service_energy_model: per_service.energy_model,
per_service_measured_ratio: per_service.measured_ratio,
database_waste,
};
(enriched, green_summary, per_endpoint_io_ops)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct AvoidableIoOps {
pub total: usize,
pub sql: usize,
}
pub(crate) fn dedup_avoidable_io_ops(findings: &[Finding]) -> AvoidableIoOps {
let capacity = findings
.iter()
.filter(|f| f.finding_type.is_avoidable_io())
.count();
let mut dedup: HashMap<(&str, &str, &str), (usize, bool)> = HashMap::with_capacity(capacity);
for f in findings {
if !f.finding_type.is_avoidable_io() {
continue;
}
let avoidable = f.pattern.occurrences.saturating_sub(1);
let is_sql = matches!(
f.finding_type,
FindingType::NPlusOneSql | FindingType::RedundantSql
);
let entry = dedup
.entry((&f.trace_id, &f.pattern.template, &f.source_endpoint))
.or_insert((avoidable, is_sql));
if avoidable > entry.0 {
*entry = (avoidable, is_sql);
}
}
let mut out = AvoidableIoOps { total: 0, sql: 0 };
for &(avoidable, is_sql) in dedup.values() {
out.total += avoidable;
if is_sql {
out.sql += avoidable;
}
}
out
}
fn build_database_waste(
carbon: Option<&CarbonContext>,
outputs: &carbon_compute::CarbonComputeOutputs,
total_sql_io_ops: usize,
avoidable_sql_io_ops: usize,
) -> Option<DatabaseWaste> {
let ctx = carbon?;
let sql_waste_ratio = if total_sql_io_ops == 0 {
0.0
} else {
(avoidable_sql_io_ops as f64 / total_sql_io_ops as f64).min(1.0)
};
if let Some(db) = ctx.db_energy.as_ref() {
if !db.window_kwh.is_finite() || db.window_kwh <= 0.0 {
return None;
}
let energy_gco2 = db
.region
.as_deref()
.and_then(|region| carbon::db_waste_gco2(db.window_kwh, region, ctx));
return Some(DatabaseWaste {
energy_kwh: db.window_kwh,
waste_kwh: db.window_kwh * sql_waste_ratio,
waste_gco2: energy_gco2.map(|g| g * sql_waste_ratio),
energy_gco2,
region: db.region.clone(),
sql_waste_ratio,
model: carbon::CO2_MODEL_ALUMET.to_string(),
});
}
if !outputs.sql_energy_kwh.is_finite() || outputs.sql_energy_kwh <= 0.0 || total_sql_io_ops == 0
{
return None;
}
let energy_gco2 =
(outputs.sql_gco2.is_finite() && outputs.sql_gco2 > 0.0).then_some(outputs.sql_gco2);
Some(DatabaseWaste {
energy_kwh: outputs.sql_energy_kwh,
waste_kwh: outputs.sql_energy_kwh * sql_waste_ratio,
waste_gco2: energy_gco2.map(|g| g * sql_waste_ratio),
energy_gco2,
region: None,
sql_waste_ratio,
model: crate::report::DB_WASTE_MODEL_ESTIMATED.to_string(),
})
}
fn build_iis_map<'a>(
endpoint_stats: &HashMap<EndpointKey<'a>, EndpointStats>,
) -> HashMap<EndpointKey<'a>, f64> {
endpoint_stats
.iter()
.map(|(&key, stats)| {
let invocations = stats.invocation_count.max(1) as f64;
(key, stats.total_io_ops as f64 / invocations)
})
.collect()
}
fn enrich_findings_with_iis(
mut findings: Vec<Finding>,
iis_map: &HashMap<EndpointKey<'_>, f64>,
) -> Vec<Finding> {
for f in &mut findings {
let iis = iis_map
.get(&(f.service.as_str(), f.source_endpoint.as_str()))
.copied()
.unwrap_or(0.0);
let extra = if f.finding_type.is_avoidable_io() {
f.pattern.occurrences.saturating_sub(1)
} else {
0
};
f.green_impact = Some(GreenImpact {
estimated_extra_io_ops: extra,
io_intensity_score: iis,
io_intensity_band: crate::report::interpret::InterpretationLevel::for_iis(iis),
});
}
findings
}
fn top_offender_co2_region(
carbon: Option<&CarbonContext>,
multi_region_active: bool,
) -> Option<String> {
let per_op_active = carbon.is_some_and(|ctx| ctx.per_operation_coefficients);
let has_energy_modifier = carbon.is_some_and(has_energy_modifier);
if multi_region_active || per_op_active || has_energy_modifier {
return None;
}
carbon
.and_then(|ctx| ctx.default_region.as_deref())
.map(str::to_ascii_lowercase)
}
fn has_energy_modifier(ctx: &CarbonContext) -> bool {
ctx.energy_snapshot.as_ref().is_some_and(|s| !s.is_empty())
|| ctx.calibration.is_some()
|| ctx
.real_time_intensity
.as_ref()
.is_some_and(|rt| !rt.is_empty())
}
fn build_top_offenders<'a>(
endpoint_stats: &HashMap<EndpointKey<'a>, EndpointStats>,
iis_map: &HashMap<EndpointKey<'a>, f64>,
default_region_lower: Option<&str>,
) -> Vec<TopOffender> {
let mut top_offenders: Vec<TopOffender> = endpoint_stats
.iter()
.map(|(&(service, endpoint), stats)| {
let iis = iis_map.get(&(service, endpoint)).copied().unwrap_or(0.0);
let co2_grams = default_region_lower
.and_then(|r| carbon::io_ops_to_co2_grams(stats.total_io_ops, r));
TopOffender {
endpoint: endpoint.to_string(),
service: service.to_string(),
io_intensity_score: iis,
io_intensity_band: crate::report::interpret::InterpretationLevel::for_iis(iis),
co2_grams,
}
})
.collect();
top_offenders.sort_by(|a, b| {
b.io_intensity_score
.total_cmp(&a.io_intensity_score)
.then_with(|| a.service.cmp(&b.service))
.then_with(|| a.endpoint.cmp(&b.endpoint))
});
top_offenders
}
struct PerServiceMaps {
energy_kwh: f64,
energy_kwh_by_service: std::collections::BTreeMap<String, f64>,
carbon_kgco2eq: std::collections::BTreeMap<String, f64>,
region: std::collections::BTreeMap<String, String>,
energy_model: std::collections::BTreeMap<String, String>,
measured_ratio: std::collections::BTreeMap<String, f64>,
}
fn build_per_service_maps(
per_service_runtime: std::collections::BTreeMap<
String,
carbon_compute::ServiceCarbonAccumulator,
>,
window_model: &'static str,
) -> PerServiceMaps {
let mut out = PerServiceMaps {
energy_kwh: 0.0,
energy_kwh_by_service: std::collections::BTreeMap::new(),
carbon_kgco2eq: std::collections::BTreeMap::new(),
region: std::collections::BTreeMap::new(),
energy_model: std::collections::BTreeMap::new(),
measured_ratio: std::collections::BTreeMap::new(),
};
for (svc, acc) in per_service_runtime {
out.energy_kwh += acc.energy_kwh;
out.energy_kwh_by_service
.insert(svc.clone(), acc.energy_kwh);
out.carbon_kgco2eq
.insert(svc.clone(), acc.operational_gco2 / 1000.0);
let svc_tag = acc.measured_model.unwrap_or(window_model);
out.energy_model.insert(svc.clone(), svc_tag.to_string());
let ratio = if acc.total_ops == 0 {
0.0
} else {
acc.measured_ops as f64 / acc.total_ops as f64
};
out.measured_ratio.insert(svc.clone(), ratio);
out.region.insert(
svc,
if acc.region.is_empty() {
carbon::UNKNOWN_REGION.to_string()
} else {
acc.region
},
);
}
out
}
#[cfg(test)]
mod tests;