tower-resilience-hedge 0.9.3

Hedging middleware for Tower services - reduces tail latency via parallel requests
Documentation
//! Tower Layer implementation for hedging.

use crate::config::{HedgeConfig, HedgeConfigBuilder};
use crate::Hedge;
use std::time::Duration;
use tower_layer::Layer;

/// A Tower [`Layer`] that applies hedging to a service.
///
/// No type parameters needed - types are inferred from the service.
///
/// See the [crate-level documentation](crate) for more details.
///
/// # Example
///
/// ```rust
/// use tower_resilience_hedge::HedgeLayer;
/// use std::time::Duration;
///
/// // No type parameters needed!
/// let layer = HedgeLayer::builder()
///     .delay(Duration::from_millis(100))
///     .max_hedged_attempts(3)
///     .build();
/// ```
#[derive(Clone)]
pub struct HedgeLayer {
    config: HedgeConfig,
}

impl HedgeLayer {
    /// Create a new `HedgeLayer` with the given delay.
    ///
    /// This creates a layer that will fire a single hedge request
    /// after the specified delay if the primary hasn't completed.
    ///
    /// # Example
    ///
    /// ```rust
    /// use tower_resilience_hedge::HedgeLayer;
    /// use std::time::Duration;
    ///
    /// // No type parameters needed!
    /// let layer = HedgeLayer::new(Duration::from_millis(100));
    /// ```
    pub fn new(delay: Duration) -> Self {
        Self::builder().delay(delay).build()
    }

    /// Preset: Conservative hedging for cost-sensitive scenarios.
    ///
    /// Configuration:
    /// - 500ms delay before hedge
    /// - 2 total attempts (1 original + 1 hedge)
    ///
    /// Use this when you want to reduce tail latency but need to
    /// minimize extra resource usage and duplicate requests.
    ///
    /// # Examples
    ///
    /// ```
    /// use tower_resilience_hedge::HedgeLayer;
    ///
    /// // Use as-is
    /// let layer = HedgeLayer::conservative();
    ///
    /// // Or customize via builder
    /// let layer = HedgeLayer::builder()
    ///     .delay(std::time::Duration::from_millis(500))
    ///     .max_hedged_attempts(2)
    ///     .name("my-hedge")
    ///     .build();
    /// ```
    pub fn conservative() -> Self {
        Self::builder()
            .delay(Duration::from_millis(500))
            .max_hedged_attempts(2)
            .build()
    }

    /// Preset: Standard hedging for general-purpose use.
    ///
    /// Configuration:
    /// - 100ms delay before hedge
    /// - 3 total attempts (1 original + 2 hedges)
    ///
    /// A balanced configuration that provides good tail latency
    /// reduction without excessive resource usage.
    ///
    /// # Examples
    ///
    /// ```
    /// use tower_resilience_hedge::HedgeLayer;
    ///
    /// let layer = HedgeLayer::standard();
    /// ```
    pub fn standard() -> Self {
        Self::builder()
            .delay(Duration::from_millis(100))
            .max_hedged_attempts(3)
            .build()
    }

    /// Preset: Aggressive hedging for latency-critical scenarios.
    ///
    /// Configuration:
    /// - 50ms delay before hedge
    /// - 5 total attempts (1 original + 4 hedges)
    ///
    /// Use this when latency is the top priority and you can afford
    /// the additional backend load from duplicate requests.
    ///
    /// # Examples
    ///
    /// ```
    /// use tower_resilience_hedge::HedgeLayer;
    ///
    /// let layer = HedgeLayer::aggressive();
    /// ```
    pub fn aggressive() -> Self {
        Self::builder()
            .delay(Duration::from_millis(50))
            .max_hedged_attempts(5)
            .build()
    }

    /// Create a builder for configuring the hedge layer.
    ///
    /// # Example
    ///
    /// ```rust
    /// use tower_resilience_hedge::HedgeLayer;
    /// use std::time::Duration;
    ///
    /// // No type parameters needed!
    /// let layer = HedgeLayer::builder()
    ///     .delay(Duration::from_millis(100))
    ///     .max_hedged_attempts(3)
    ///     .build();
    /// ```
    pub fn builder() -> HedgeConfigBuilder {
        HedgeConfigBuilder::new()
    }

    /// Create a `HedgeLayer` from a configuration.
    pub(crate) fn from_config(config: HedgeConfig) -> Self {
        Self { config }
    }
}

impl<S> Layer<S> for HedgeLayer {
    type Service = Hedge<S>;

    fn layer(&self, service: S) -> Self::Service {
        Hedge::new(service, self.config.clone())
    }
}