Skip to content

02 Getting Started

Marco Breveglieri edited this page Jul 21, 2026 · 3 revisions

Getting Started with Murphy

This guide will help you install Murphy for Delphi and create your first resilience policies.

Prerequisites

  • Delphi 11 Alexandria or later
  • Windows platform (Win32 or Win64)

Murphy makes use of modern Delphi features such as inline variables, generics, and anonymous methods.

Installation

Using Blocks Package Manager (Recommended)

The easiest way to install Murphy is using the Blocks package manager:

blocks install marcobreveglieri.murphy-delphi

Blocks will automatically download the library and configure your project paths.

Manual Installation

If you prefer manual installation:

  1. Download or clone the Murphy repository from GitHub:

    git clone https://github.com/marcobreveglieri/murphy-delphi.git
  2. Add the murphy-delphi/Source folder to your project's include path:

    • Open your project in Delphi
    • Go to Project > Options > Delphi Compiler > Search path
    • Add the full path to murphy-delphi/Source
  3. Alternatively, add the folder to the IDE's global library path:

    • Go to Tools > Options > Language > Delphi > Library
    • Add the path to Library path for your target platform

Your First Policy

Let's create a simple retry policy that automatically retries failed HTTP requests.

Step 1: Add the Unit

Add the Murphy unit to your uses clause:

uses
  Murphy.Policy.Retry;

Step 2: Create a Retry Policy

Create and configure a retry policy using the fluent builder API:

var
  RetryPolicy: IRetryPolicy;
begin
  RetryPolicy := TRetryBuilder.Create
    .Handle(EIdHTTPProtocolException)  // Handle HTTP exceptions
    .Retry(3)                           // Retry up to 3 times
    .Wait(TTimeSpan.FromSeconds(1))    // Wait 1 second between retries
    .Build;
end;

Step 3: Execute Code with the Policy

Use the policy to execute your code:

RetryPolicy.Execute(
  procedure
  begin
    // Your code here - will be automatically retried on failure
    MakeHttpRequest('https://api.example.com/data');
  end);

Complete Example

Here's a complete example:

unit MyUnit;

interface

uses
  System.SysUtils,
  System.TimeSpan,
  IdHTTP,
  IdHTTPProtocolException,
  Murphy.Policy.Retry;

procedure FetchDataWithRetry;

implementation

procedure FetchDataWithRetry;
var
  RetryPolicy: IRetryPolicy;
  HTTP: TIdHTTP;
  Response: string;
begin
  // Create HTTP client
  HTTP := TIdHTTP.Create(nil);
  try
    // Create retry policy
    RetryPolicy := TRetryBuilder.Create
      .Handle(EIdHTTPProtocolException)
      .Retry(3)
      .Wait(TTimeSpan.FromSeconds(2))
      .Build;

    // Execute with automatic retry
    RetryPolicy.Execute(
      procedure
      begin
        Response := HTTP.Get('https://api.example.com/data');
        WriteLn('Success: ', Response);
      end);
  finally
    HTTP.Free;
  end;
end;

end.

Exploring Other Patterns

Murphy includes four resilience patterns. Here are quick examples of each:

Circuit Breaker

Prevent cascading failures by blocking calls to failing services:

uses Murphy.Policy.CircuitBreaker;

var
  CircuitPolicy: ICircuitBreakerPolicy;
begin
  CircuitPolicy := TCircuitBreakerBuilder.Create
    .Handle(Exception)
    .Fail(5)                              // Open after 5 failures
    .Within(TTimeSpan.FromSeconds(30))   // Close after 30 seconds
    .Build;

  CircuitPolicy.Execute(
    procedure
    begin
      CallExternalService;
    end);
end;

Fallback

Provide alternative results when operations fail:

uses Murphy.Policy.Fallback;

var
  FallbackPolicy: IFallbackPolicy<string>;
  Result: string;
begin
  FallbackPolicy := TFallbackBuilder<string>.Create
    .Handle(Exception)
    .Fallback(function: string
              begin
                Result := 'Cached or default data';
              end)
    .Build;

  Result := FallbackPolicy.Execute(
    function: string
    begin
      Result := GetDataFromService;
    end);

  WriteLn('Result: ', Result);
end;

Rate Limit

Control the rate of operations to prevent overload:

uses Murphy.Policy.RateLimit;

var
  RateLimitPolicy: IRateLimitPolicy;
begin
  RateLimitPolicy := TRateLimitBuilder.Create
    .Allow(10)                            // Allow 10 calls
    .Within(TTimeSpan.FromSeconds(1))    // Per second
    .Build;

  // This will throw ERateLimitRejectedException if limit exceeded
  RateLimitPolicy.Execute(
    procedure
    begin
      MakeApiCall;
    end);
end;

Working with Exceptions

Handling Specific Exception Types

You can configure policies to handle multiple exception types:

RetryPolicy := TRetryBuilder.Create
  .Handle(EIdHTTPProtocolException)
  .Handle(EIdSocketError)
  .Handle(EIdConnClosedGracefully)
  .Retry(3)
  .Build;

Catching Exceptions from Policies

Policies re-raise exceptions after exhausting retries or when not handled:

try
  RetryPolicy.Execute(
    procedure
    begin
      MakeRequest;
    end);
except
  on E: EIdHTTPProtocolException do
    WriteLn('Request failed after retries: ', E.Message);
end;

Best Practices for Getting Started

  1. Start Simple - Begin with a single pattern (e.g., Retry) before combining multiple patterns
  2. Use Specific Exceptions - Handle specific exception types rather than catching all exceptions
  3. Configure Appropriately - Adjust retry counts and delays based on your specific use case
  4. Test Your Policies - Enable test mode (MurphyTestModeEnabled := True) for unit testing
  5. Monitor Behavior - Log or track when policies are triggered to understand system behavior

Common Scenarios

Scenario 1: Database Connection Retry

RetryPolicy := TRetryBuilder.Create
  .Handle(EDatabaseError)
  .Retry(3)
  .Wait(TTimeSpan.FromSeconds(1))
  .Build;

RetryPolicy.Execute(
  procedure
  begin
    Database.Connect;
  end);

Scenario 2: API with Fallback

var
  FallbackPolicy: IFallbackPolicy<TJSONObject>;
  Data: TJSONObject;
begin
  FallbackPolicy := TFallbackBuilder<TJSONObject>.Create
    .Handle(Exception)
    .Fallback(function: TJSONObject
              begin
                Result := GetCachedData;
              end)
    .Build;

  Data := FallbackPolicy.Execute(
    function: TJSONObject
    begin
      Result := FetchDataFromAPI;
    end);
end;

Scenario 3: Rate-Limited API Client

var
  RateLimitPolicy: IRateLimitPolicy;
  I: Integer;
begin
  RateLimitPolicy := TRateLimitBuilder.Create
    .Allow(100)                           // 100 requests
    .Within(TTimeSpan.FromMinutes(1))    // Per minute
    .Build;

  for I := 1 to 1000 do
  begin
    try
      RateLimitPolicy.Execute(
        procedure
        begin
          MakeAPIRequest(I);
        end);
    except
      on E: ERateLimitRejectedException do
      begin
        WriteLn('Rate limit exceeded, waiting...');
        Sleep(1000);
      end;
    end;
  end;
end;

Demo Application

Murphy includes a comprehensive demo application with examples for all patterns. You can find it in the Demos/00_Primer folder:

  • Samples.Retry.pas - Retry pattern examples
  • Samples.CircuitBreaker.pas - Circuit breaker examples
  • Samples.Fallback.pas - Fallback examples
  • Samples.RateLimit.pas - Rate limit examples

Run the demo to see the patterns in action and explore the code for more examples.

Next Steps

Now that you've learned the basics:

Troubleshooting

"Unit not found" error

Make sure the murphy-delphi/Source folder is in your project's search path or library path.

Compilation errors about inline variables

Murphy requires Delphi 11 Alexandria or later. Check your Delphi version.

Policies not working as expected

Enable test mode and verify your exception types are correct. Check the Testing Guide for more information.


← Introduction | Back to Index | Chaos Engineering Basics →

Clone this wiki locally