# Hedging Policy API **Unit**: `Murphy.Policy.Hedging` The Hedging policy launches parallel attempts when the primary one is too slow or fails, returning the first attempt that succeeds. It reduces tail latency at the cost of extra work. ## Overview The Hedging pattern is useful for: - Reducing tail latency for calls to replicated or redundant services - Riding through occasional slow or flaky calls without failing the whole request **Important**: Hedged actions must be idempotent. Losing attempts are abandoned but keep running in the background - they are not cancelled, only ignored. ## Table of Contents - [THedgingContext Class](#thedgingcontext-class) - [IHedgingPolicy Interface](#ihedgingpolicy-interface) - [THedgingPolicy Class](#thedgingpolicy-class) - [THedgingBuilder Class](#thedgingbuilder-class) - [Usage Examples](#usage-examples) --- ## THedgingContext Class Contains information about a hedged attempt being launched, passed to the `OnHedging` callback. ### Declaration ```pascal type THedgingContext = class(TObject) private FAttemptNumber: Integer; public property AttemptNumber: Integer read FAttemptNumber; end; ``` ### Properties #### AttemptNumber ```pascal property AttemptNumber: Integer read FAttemptNumber; ``` **Description**: The 1-based index of the hedged (extra, non-primary) attempt about to be launched. **Type**: `Integer` (read-only) --- ## IHedgingPolicy Interface The interface for the Hedging policy pattern. Extends `IExecutablePolicy`, so a Hedging policy can participate in a [Policy Wrap](PolicyWrap-Policy.md). ### Declaration ```pascal type IHedgingPolicy = interface(IExecutablePolicy) ['{5F746C39-BE24-4544-823E-FC7B7A4955D0}'] function Delay(ADelay: TTimeSpan): IHedgingPolicy; function MaxAttempts(ACount: Integer): IHedgingPolicy; function OnHedging(ACallback: TProc): IHedgingPolicy; end; ``` ### Methods #### Execute ```pascal procedure Execute(AProc: TProc); ``` **Description**: Executes `AProc`, launching additional parallel attempts if the primary one does not complete within the hedging delay, up to `MaxAttempts` extra attempts. **Behavior**: 1. Launches the primary attempt immediately, on its own task 2. If it does not complete within `Delay`, launches a hedged attempt in parallel, and keeps doing so (up to `MaxAttempts` extra attempts) each time the delay elapses without any attempt succeeding 3. A handled exception from any attempt triggers the next hedged attempt immediately, without waiting out the remaining delay 4. An unhandled exception is propagated immediately 5. The first attempt to succeed wins: `Execute` returns and all other attempts are abandoned (they keep running in the background) 6. If every attempt fails, the most recent failure is re-raised **Note**: There is no dedicated "all attempts failed" exception - the last underlying failure is re-raised as-is. #### Delay ```pascal function Delay(ADelay: TTimeSpan): IHedgingPolicy; ``` **Description**: Configures how long to wait before launching the next hedged attempt. **Parameters**: - `ADelay: TTimeSpan` - Time to wait before hedging **Returns**: `IHedgingPolicy` - Self for method chaining **Default**: `2 seconds` if not specified #### MaxAttempts ```pascal function MaxAttempts(ACount: Integer): IHedgingPolicy; ``` **Description**: Configures how many extra parallel attempts may be launched, in addition to the primary one. **Parameters**: - `ACount: Integer` - Number of extra attempts (must be `>= 0`) **Returns**: `IHedgingPolicy` - Self for method chaining **Default**: `1` if not specified **Raises**: `EArgumentException` if `ACount < 0` #### OnHedging ```pascal function OnHedging(ACallback: TProc): IHedgingPolicy; ``` **Description**: Configures a callback invoked just before each hedged (extra) attempt is launched. **Parameters**: - `ACallback: TProc` - Procedure receiving the hedging context **Returns**: `IHedgingPolicy` - Self for method chaining **Use Cases**: Logging or metrics on how often hedging kicks in --- ## THedgingPolicy Class Concrete implementation of the Hedging policy pattern. ### Declaration ```pascal type THedgingPolicy = class(TPolicy, IHedgingPolicy) private FHedgingDelay: TTimeSpan; FMaxAttempts: Integer; FOnHedgingCallback: TProc; public constructor Create(AExceptionTypes: TArray); override; procedure Execute(AProc: TProc); function Delay(ADelay: TTimeSpan): IHedgingPolicy; function MaxAttempts(ACount: Integer): IHedgingPolicy; function OnHedging(ACallback: TProc): IHedgingPolicy; end; ``` ### Constructor ```pascal constructor Create(AExceptionTypes: TArray); override; ``` **Defaults**: - `FHedgingDelay`: `2 seconds` - `FMaxAttempts`: `1` **Note**: Typically called by `THedgingBuilder`, not directly by users. ### Implementation Details Each attempt runs on its own `TTask`, coordinated through a shared, reference-counted execution state (`THedgingExecution`). The state tracks how many attempts have completed, whether a winner has already been declared, and the most recent error. Losing attempts release their reference to the shared state when they eventually finish, so the state outlives `Execute` if attempts are still abandoned in flight. --- ## THedgingBuilder Class Builder class for creating Hedging policies with fluent configuration. ### Declaration ```pascal type THedgingBuilder = class sealed(TPolicyBuilder) public class function Handle(AExceptionTypes: TArray): IHedgingPolicy; override; end; ``` #### Handle ```pascal class function Handle(AExceptionTypes: TArray): IHedgingPolicy; override; ``` **Description**: Creates a new Hedging policy instance. An attempt failing with a handled exception triggers the next parallel attempt immediately; an unhandled exception is propagated at once. **Returns**: `IHedgingPolicy` - Policy instance ready for further configuration --- ## Usage Examples ### Example 1: Basic Hedging ```pascal uses Murphy.Policy.Hedging; var Policy: IHedgingPolicy; begin Policy := THedgingBuilder .Handle(ENetworkError) .MaxAttempts(2) .Delay(TTimeSpan.FromMilliseconds(500)) .Build; Policy.Execute( procedure begin CallReplicatedService; end); end; ``` ### Example 2: Observing Hedged Attempts ```pascal var Policy: IHedgingPolicy; begin Policy := THedgingBuilder .Handle(ENetworkError) .MaxAttempts(3) .Delay(TTimeSpan.FromMilliseconds(200)) .OnHedging(procedure(Context: THedgingContext) begin WriteLn(Format('Launching hedged attempt #%d', [Context.AttemptNumber])); end) .Build; Policy.Execute(procedure begin CallReplicatedService; end); end; ``` ### Example 3: Handling Total Failure ```pascal var Policy: IHedgingPolicy; begin Policy := THedgingBuilder.Handle(ENetworkError).MaxAttempts(2).Build; try Policy.Execute(procedure begin CallReplicatedService; end); except on E: ENetworkError do WriteLn('All hedged attempts failed: ' + E.Message); end; end; ``` --- ## See Also - [Hedging Pattern Guide](../Patterns/Hedging.md) - Comprehensive usage guide with more examples - [Base Policy API](Base-Policy.md) - Inherited functionality, including `IExecutablePolicy` - [Policy Wrap API](PolicyWrap-Policy.md) - Composing Hedging with other policies - [Combining Patterns](../Patterns/Combining-Patterns.md) - Using Hedging with other policies --- [← Cache Policy](Cache-Policy.md) | [API Reference Index](README.md) | [Policy Wrap →](PolicyWrap-Policy.md)