use crate::options::{HedgeThreshold, Region};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct HedgingStrategyConfig {
threshold: HedgeThreshold,
}
impl HedgingStrategyConfig {
pub(crate) const fn new(threshold: HedgeThreshold) -> Self {
Self { threshold }
}
pub fn threshold(&self) -> HedgeThreshold {
self.threshold
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum HedgeTerminalState {
PrimaryWonPreThreshold,
DeadlineExceededPreThreshold,
PrimaryWonAfterHedge,
AlternateWon,
#[non_exhaustive]
BothTransient {
deadline_elapsed: bool,
},
CancelledAwaitingPartner,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct HedgeDiagnostics {
strategy_config: HedgingStrategyConfig,
primary_region: Region,
alternate_region: Option<Region>,
response_region: Option<Region>,
terminal_state: HedgeTerminalState,
}
impl HedgeDiagnostics {
pub(crate) const UNKNOWN_REGION_SENTINEL: &'static str = "(unknown)";
pub(crate) fn primary_only(
strategy_config: HedgingStrategyConfig,
primary_region: Region,
) -> Self {
Self {
strategy_config,
primary_region: primary_region.clone(),
alternate_region: None,
response_region: Some(primary_region),
terminal_state: HedgeTerminalState::PrimaryWonPreThreshold,
}
}
pub(crate) fn primary_only_deadline_exceeded(
strategy_config: HedgingStrategyConfig,
primary_region: Region,
) -> Self {
Self {
strategy_config,
primary_region,
alternate_region: None,
response_region: None,
terminal_state: HedgeTerminalState::DeadlineExceededPreThreshold,
}
}
pub(crate) fn primary_won_after_hedge(
strategy_config: HedgingStrategyConfig,
primary_region: Region,
alternate_region: Region,
) -> Self {
Self {
strategy_config,
primary_region: primary_region.clone(),
alternate_region: Some(alternate_region),
response_region: Some(primary_region),
terminal_state: HedgeTerminalState::PrimaryWonAfterHedge,
}
}
pub(crate) fn hedge_won(
strategy_config: HedgingStrategyConfig,
primary_region: Region,
alternate_region: Region,
) -> Self {
Self {
strategy_config,
primary_region,
alternate_region: Some(alternate_region.clone()),
response_region: Some(alternate_region),
terminal_state: HedgeTerminalState::AlternateWon,
}
}
pub(crate) fn both_transient(
strategy_config: HedgingStrategyConfig,
primary_region: Region,
alternate_region: Region,
deadline_elapsed: bool,
) -> Self {
Self {
strategy_config,
primary_region,
alternate_region: Some(alternate_region),
response_region: None,
terminal_state: HedgeTerminalState::BothTransient { deadline_elapsed },
}
}
pub(crate) fn cancelled_awaiting_partner(
strategy_config: HedgingStrategyConfig,
primary_region: Region,
alternate_region: Region,
) -> Self {
Self {
strategy_config,
primary_region,
alternate_region: Some(alternate_region),
response_region: None,
terminal_state: HedgeTerminalState::CancelledAwaitingPartner,
}
}
pub fn strategy_config(&self) -> HedgingStrategyConfig {
self.strategy_config
}
pub fn primary_region(&self) -> &Region {
&self.primary_region
}
pub fn alternate_region(&self) -> Option<&Region> {
self.alternate_region.as_ref()
}
pub fn response_region(&self) -> Option<&Region> {
self.response_region.as_ref()
}
pub fn terminal_state(&self) -> HedgeTerminalState {
self.terminal_state
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
fn threshold() -> HedgeThreshold {
HedgeThreshold::new(Duration::from_millis(500)).expect("500ms is non-zero")
}
fn config() -> HedgingStrategyConfig {
HedgingStrategyConfig::new(threshold())
}
#[test]
fn strategy_config_exposes_threshold() {
let cfg = config();
assert_eq!(cfg.threshold(), threshold());
}
#[test]
fn primary_only_constructor() {
let diag = HedgeDiagnostics::primary_only(config(), Region::EAST_US);
assert_eq!(diag.strategy_config(), config());
assert_eq!(diag.primary_region(), &Region::EAST_US);
assert_eq!(diag.alternate_region(), None);
assert_eq!(diag.response_region(), Some(&Region::EAST_US));
assert!(!matches!(
diag.terminal_state(),
HedgeTerminalState::AlternateWon
));
assert_eq!(
diag.terminal_state(),
HedgeTerminalState::PrimaryWonPreThreshold
);
}
#[test]
fn primary_only_deadline_exceeded_constructor() {
let diag = HedgeDiagnostics::primary_only_deadline_exceeded(config(), Region::EAST_US);
assert_eq!(diag.primary_region(), &Region::EAST_US);
assert_eq!(diag.alternate_region(), None);
assert_eq!(
diag.response_region(),
None,
"deadline-exceeded pre-threshold produced no final response"
);
assert!(
!matches!(diag.terminal_state(), HedgeTerminalState::AlternateWon),
"deadline-exceeded pre-threshold must not match terminal_state == AlternateWon"
);
assert_eq!(
diag.terminal_state(),
HedgeTerminalState::DeadlineExceededPreThreshold
);
}
#[test]
fn primary_won_after_hedge_constructor() {
let diag =
HedgeDiagnostics::primary_won_after_hedge(config(), Region::EAST_US, Region::WEST_US_2);
assert_eq!(diag.primary_region(), &Region::EAST_US);
assert_eq!(diag.alternate_region(), Some(&Region::WEST_US_2));
assert_eq!(diag.response_region(), Some(&Region::EAST_US));
assert!(!matches!(
diag.terminal_state(),
HedgeTerminalState::AlternateWon
));
assert_eq!(
diag.terminal_state(),
HedgeTerminalState::PrimaryWonAfterHedge
);
}
#[test]
fn hedge_won_constructor() {
let diag = HedgeDiagnostics::hedge_won(config(), Region::EAST_US, Region::WEST_US_2);
assert_eq!(diag.primary_region(), &Region::EAST_US);
assert_eq!(diag.alternate_region(), Some(&Region::WEST_US_2));
assert_eq!(diag.response_region(), Some(&Region::WEST_US_2));
assert!(matches!(
diag.terminal_state(),
HedgeTerminalState::AlternateWon
));
assert_eq!(diag.terminal_state(), HedgeTerminalState::AlternateWon);
}
#[test]
fn both_transient_constructor_with_deadline_elapsed() {
let diag =
HedgeDiagnostics::both_transient(config(), Region::EAST_US, Region::WEST_US_2, true);
assert_eq!(diag.primary_region(), &Region::EAST_US);
assert_eq!(diag.alternate_region(), Some(&Region::WEST_US_2));
assert_eq!(
diag.response_region(),
None,
"both-transient produced no final response"
);
assert!(
!matches!(diag.terminal_state(), HedgeTerminalState::AlternateWon),
"both-transient must not match terminal_state == AlternateWon — no leg won"
);
assert_eq!(
diag.terminal_state(),
HedgeTerminalState::BothTransient {
deadline_elapsed: true
}
);
}
#[test]
fn both_transient_constructor_without_deadline_elapsed() {
let diag =
HedgeDiagnostics::both_transient(config(), Region::EAST_US, Region::WEST_US_2, false);
assert!(!matches!(
diag.terminal_state(),
HedgeTerminalState::AlternateWon
));
assert_eq!(
diag.terminal_state(),
HedgeTerminalState::BothTransient {
deadline_elapsed: false
}
);
}
#[test]
fn cancelled_awaiting_partner_constructor() {
let diag = HedgeDiagnostics::cancelled_awaiting_partner(
config(),
Region::EAST_US,
Region::WEST_US_2,
);
assert_eq!(diag.primary_region(), &Region::EAST_US);
assert_eq!(diag.alternate_region(), Some(&Region::WEST_US_2));
assert_eq!(diag.response_region(), None);
assert!(
!matches!(diag.terminal_state(), HedgeTerminalState::AlternateWon),
"cancelled-awaiting-partner must not match terminal_state == AlternateWon — no leg won"
);
assert_eq!(
diag.terminal_state(),
HedgeTerminalState::CancelledAwaitingPartner
);
}
#[test]
fn only_alternate_won_records_alternate_terminal_state() {
let east = Region::EAST_US;
let west = Region::WEST_US_2;
let is_alternate_won = |diag: HedgeDiagnostics| {
matches!(diag.terminal_state(), HedgeTerminalState::AlternateWon)
};
assert!(is_alternate_won(HedgeDiagnostics::hedge_won(
config(),
east.clone(),
west.clone(),
)));
assert!(!is_alternate_won(HedgeDiagnostics::primary_only(
config(),
east.clone(),
)));
assert!(!is_alternate_won(
HedgeDiagnostics::primary_only_deadline_exceeded(config(), east.clone()),
));
assert!(!is_alternate_won(
HedgeDiagnostics::primary_won_after_hedge(config(), east.clone(), west.clone(),)
));
assert!(!is_alternate_won(HedgeDiagnostics::both_transient(
config(),
east.clone(),
west.clone(),
true,
)));
assert!(!is_alternate_won(HedgeDiagnostics::both_transient(
config(),
east.clone(),
west.clone(),
false,
)));
assert!(!is_alternate_won(
HedgeDiagnostics::cancelled_awaiting_partner(config(), east, west),
));
}
#[test]
fn debug_clone_round_trip() {
let diag = HedgeDiagnostics::hedge_won(config(), Region::EAST_US, Region::WEST_US_2);
let cloned = diag.clone();
assert_eq!(diag, cloned);
let dbg = format!("{:?}", diag);
assert!(
dbg.contains("westus2"),
"Debug output missing winner region: {dbg}"
);
}
#[test]
fn unknown_region_sentinel_constructs_diagnostics_for_global_endpoint() {
let unknown = Region::new("(unknown)");
let diag = HedgeDiagnostics::primary_only(config(), unknown.clone());
assert_eq!(diag.response_region(), Some(&unknown));
assert_eq!(diag.primary_region(), &unknown);
assert_eq!(diag.alternate_region(), None);
assert!(!matches!(
diag.terminal_state(),
HedgeTerminalState::AlternateWon
));
assert_eq!(
diag.terminal_state(),
HedgeTerminalState::PrimaryWonPreThreshold
);
assert_ne!(unknown, Region::EAST_US);
assert_ne!(unknown, Region::WEST_US_2);
}
}