-
Notifications
You must be signed in to change notification settings - Fork 1
Timeout Policy
Unit: Murphy.Policy.Timeout
The Timeout policy aborts an operation that exceeds a configured duration, so a hung dependency cannot hang the caller.
The Timeout pattern is useful for bounding how long an operation is allowed to run, such as:
- Network calls without a reliable timeout of their own
- Enforcing an SLA on a critical code path
- Preventing a slow dependency from blocking a calling thread indefinitely
- TTimeoutContext Class
- ITimeoutPolicy Interface
- TTimeoutPolicy Class
- TTimeoutBuilder Class
- Usage Examples
Contains information about a timeout that occurred, passed to the OnTimeout callback.
type
TTimeoutContext = class(TObject)
private
FElapsedTime: TTimeSpan;
FTimeoutDuration: TTimeSpan;
public
property ElapsedTime: TTimeSpan read FElapsedTime;
property TimeoutDuration: TTimeSpan read FTimeoutDuration;
end;property ElapsedTime: TTimeSpan read FElapsedTime;Description: How much time actually elapsed before the timeout fired.
Type: TTimeSpan (read-only)
property TimeoutDuration: TTimeSpan read FTimeoutDuration;Description: The configured timeout duration that was exceeded.
Type: TTimeSpan (read-only)
The interface for the Timeout policy pattern. Extends IExecutablePolicy, so a Timeout policy can participate in a Policy Wrap.
type
ITimeoutPolicy = interface(IExecutablePolicy)
['{8B3E9F12-4A7C-4D1E-9B5A-2E8C7F6D4A91}']
function After(ADuration: TTimeSpan): ITimeoutPolicy;
function OnTimeout(ACallback: TProc<TTimeoutContext>): ITimeoutPolicy;
end;procedure Execute(AProc: TProc);Description: Executes AProc on a background task and waits up to the configured duration.
Behavior:
- Runs
AProcon aTTask - If it completes within the timeout, any exception it raised is re-raised as-is on the calling thread
- If it does not complete in time:
- The
OnTimeoutcallback (if configured) is invoked with aTTimeoutContext -
ETimeoutRejectedExceptionis raised - The background task is abandoned and keeps running; it cleans up its own exception if it eventually raises one
- The
Note: Exception filtering configured via Handle() is not applied by this policy - it is accepted only for API consistency with other builders. Any exception raised by AProc within the timeout is re-raised untouched, and the timeout itself fires regardless of exception type.
function After(ADuration: TTimeSpan): ITimeoutPolicy;Description: Configures the maximum duration allowed for the operation.
Parameters:
-
ADuration: TTimeSpan- Maximum time to wait before aborting
Returns: ITimeoutPolicy - Self for method chaining
Default: 30 seconds if not specified
Example:
Policy := TTimeoutBuilder
.Handle([])
.After(TTimeSpan.FromSeconds(5))
.Build;function OnTimeout(ACallback: TProc<TTimeoutContext>): ITimeoutPolicy;Description: Configures a callback invoked when the operation times out, before ETimeoutRejectedException is raised.
Parameters:
-
ACallback: TProc<TTimeoutContext>- Procedure receiving the timeout context
Returns: ITimeoutPolicy - Self for method chaining
Use Cases: Logging elapsed time, recording metrics, alerting on repeated timeouts
Example:
Policy := TTimeoutBuilder
.Handle([])
.After(TTimeSpan.FromSeconds(5))
.OnTimeout(procedure(Context: TTimeoutContext)
begin
Log(Format('Timed out after %s', [Context.ElapsedTime.ToString]));
end)
.Build;Concrete implementation of the Timeout policy pattern.
type
TTimeoutPolicy = class(TPolicy, ITimeoutPolicy)
private
FTimeoutDuration: TTimeSpan;
FOnTimeoutCallback: TProc<TTimeoutContext>;
public
constructor Create(AExceptionTypes: TArray<ExceptClass>); override;
procedure Execute(AProc: TProc);
function After(ADuration: TTimeSpan): ITimeoutPolicy;
function OnTimeout(ACallback: TProc<TTimeoutContext>): ITimeoutPolicy;
end;constructor Create(AExceptionTypes: TArray<ExceptClass>); override;Defaults:
-
FTimeoutDuration:30 seconds -
FOnTimeoutCallback:nil
Note: Typically called by TTimeoutBuilder, not directly by users.
Execute runs AProc on a TTask and waits with TTask.Wait(Timeout). If the wait times out, the task is abandoned rather than cancelled: it keeps executing, and if it later raises an exception or completes, that outcome is discarded because nobody is waiting for it anymore. This means the underlying operation must be safe to abandon.
Builder class for creating Timeout policies with fluent configuration.
type
TTimeoutBuilder = class sealed(TPolicyBuilder<ITimeoutPolicy>)
public
class function Handle(AExceptionTypes: TArray<ExceptClass>): ITimeoutPolicy; override;
end;class function Handle(AExceptionTypes: TArray<ExceptClass>): ITimeoutPolicy; override;Description: Creates a new Timeout policy instance. The exception types passed here are not used by this policy: any exception raised by the operation is re-raised as-is, and the timeout applies regardless of any exception filter. Pass [] for clarity.
Returns: ITimeoutPolicy - Policy instance ready for further configuration
uses
Murphy.Policy.Timeout;
var
Policy: ITimeoutPolicy;
begin
Policy := TTimeoutBuilder
.Handle([])
.After(TTimeSpan.FromSeconds(5))
.Build;
Policy.Execute(
procedure
begin
CallSlowService;
end);
end;var
Policy: ITimeoutPolicy;
begin
Policy := TTimeoutBuilder.Handle([]).After(TTimeSpan.FromSeconds(2)).Build;
try
Policy.Execute(procedure begin CallSlowService; end);
except
on E: ETimeoutRejectedException do
WriteLn('Operation aborted: took too long');
end;
end;uses Murphy.Policy.Retry, Murphy.Policy.Timeout, Murphy.Policy.Wrap;
var
Policy: IPolicyWrap;
begin
Policy := TPolicyWrapBuilder.Wrap([
TRetryBuilder.Handle(ETimeoutRejectedException).Retry(3),
TTimeoutBuilder.Handle([]).After(TTimeSpan.FromSeconds(1))
]);
Policy.Execute(procedure begin CallExternalService; end);
end;- Timeout Pattern Guide - Comprehensive usage guide with more examples
-
Base Policy API - Inherited functionality, including
IExecutablePolicy - Policy Wrap API - Composing Timeout with other policies
- Combining Patterns - Using Timeout with other policies
← Rate Limit Policy | API Reference Index | Bulkhead Policy →