Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions docs/migration-v8.md
Original file line number Diff line number Diff line change
Expand Up @@ -936,8 +936,23 @@ ResiliencePipeline<int> pipeline = new ResiliencePipelineBuilder<int>()

// Asynchronous execution
var context = ResilienceContextPool.Shared.Get();
Outcome<int> pipelineResult = await pipeline.ExecuteOutcomeAsync(
static async (ctx, state) => Outcome.FromResult(await MethodAsync(ctx.CancellationToken)), context, "state");

Outcome<int> pipelineResult =
await pipeline.ExecuteOutcomeAsync<int, string>(
static async (ctx, state) =>
{
try
{
return Outcome.FromResult(await MethodAsync(ctx.CancellationToken));
}
catch (Exception e)
{
return Outcome.FromException<int>(e);
}
},
context,
"state");

ResilienceContextPool.Shared.Return(context);

// Assess policy result
Expand All @@ -950,7 +965,6 @@ if (pipelineResult.Exception is null)
else
{
Exception exception = pipelineResult.Exception;

// Process failure

// If needed you can rethrow the exception
Expand All @@ -972,8 +986,22 @@ ResiliencePipeline<int> pipelineWithContext = new ResiliencePipelineBuilder<int>
.Build();

context = ResilienceContextPool.Shared.Get();
pipelineResult = await pipelineWithContext.ExecuteOutcomeAsync(
static async (ctx, state) => Outcome.FromResult(await MethodAsync(ctx.CancellationToken)), context, "state");

pipelineResult =
await pipelineWithContext.ExecuteOutcomeAsync<int, string>(
static async (ctx, state) =>
{
try
{
return Outcome.FromResult(await MethodAsync(ctx.CancellationToken));
}
catch (Exception e)
{
return Outcome.FromException<int>(e);
}
},
context,
"state");

context.Properties.TryGetValue(contextKey, out var ctxValue);
ResilienceContextPool.Shared.Return(context);
Expand Down
22 changes: 17 additions & 5 deletions docs/strategies/circuit-breaker.md
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ Use `ExecuteOutcomeAsync` to avoid throwing exception:
<!-- snippet: circuit-breaker-pattern-reduce-thrown-exceptions -->
```cs
var context = ResilienceContextPool.Shared.Get();

var circuitBreaker = new ResiliencePipelineBuilder()
.AddCircuitBreaker(new()
{
Expand All @@ -683,11 +684,22 @@ var circuitBreaker = new ResiliencePipelineBuilder()
})
.Build();

Outcome<HttpResponseMessage> outcome = await circuitBreaker.ExecuteOutcomeAsync(static async (ctx, state) =>
{
var response = await IssueRequest();
return Outcome.FromResult(response);
}, context, "state");
Outcome<HttpResponseMessage> outcome =
await circuitBreaker.ExecuteOutcomeAsync<HttpResponseMessage, string>(
static async (ctx, state) =>
{
try
{
var response = await IssueRequest(ctx.CancellationToken);
return Outcome.FromResult(response);
}
catch (Exception e)
{
return Outcome.FromException<HttpResponseMessage>(e);
}
},
context,
"state");

ResilienceContextPool.Shared.Return(context);

Expand Down
18 changes: 14 additions & 4 deletions docs/strategies/fallback.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,22 @@ This method lets you execute the strategy or pipeline smoothly, without unexpect
public static async ValueTask<HttpResponseMessage> Action()
{
var context = ResilienceContextPool.Shared.Get();

var outcome = await WhateverPipeline.ExecuteOutcomeAsync<HttpResponseMessage, string>(
async (ctx, state) =>
static async (ctx, state) =>
{
var result = await ActionCore();
return Outcome.FromResult(result);
}, context, "state");
try
{
var result = await ActionCore(ctx.CancellationToken);
return Outcome.FromResult(result);
}
catch (Exception e)
{
return Outcome.FromException<HttpResponseMessage>(e);
}
},
context,
"state");

if (outcome.Exception is HttpRequestException requestException)
{
Expand Down
7 changes: 5 additions & 2 deletions src/Polly.Core/ResiliencePipeline.AsyncT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ public partial class ResiliencePipeline
/// <returns>The instance of <see cref="ValueTask"/> that represents the asynchronous execution.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="callback"/> or <paramref name="context"/> is <see langword="null"/>.</exception>
/// <remarks>
/// This method is for advanced and high performance scenarios. The caller must make sure that the <paramref name="callback"/>
/// does not throw any exceptions. Instead, it converts them to <see cref="Outcome{TResult}"/>.
/// <para><strong>Important:</strong> This API targets advanced, low-allocation scenarios. The user callback
/// must not throw an exception. Wrap your code and return <see cref="Outcome{TResult}"/>:
/// use <see cref="Outcome.FromResult{TResult}(TResult)"/> on success, or <see cref="Outcome.FromException{TResult}(System.Exception)"/> on failure.
/// Do not rely on strategies to catch your exceptions; any such behavior is an implementation detail and is not
/// guaranteed across strategies or future versions.</para>
/// </remarks>
public ValueTask<Outcome<TResult>> ExecuteOutcomeAsync<TResult, TState>(
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
Expand Down
7 changes: 5 additions & 2 deletions src/Polly.Core/ResiliencePipelineT.Async.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,11 @@ public ValueTask<TResult> ExecuteAsync<TResult>(
/// <returns>The instance of <see cref="ValueTask"/> that represents the asynchronous execution.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="callback"/> or <paramref name="context"/> is <see langword="null"/>.</exception>
/// <remarks>
/// This method is for advanced and high performance scenarios. The caller must make sure that the <paramref name="callback"/>
/// does not throw any exceptions. Instead, it converts them to <see cref="Outcome{TResult}"/>.
/// <para><strong>Important:</strong> This method targets advanced, low-allocation scenarios. The user callback
/// must not throw an exception. Wrap your code and return <see cref="Outcome{TResult}"/>:
/// use <see cref="Outcome.FromResult{TResult}(TResult)"/> on success, or <see cref="Outcome.FromException{TResult}(System.Exception)"/> on failure.
/// Do not rely on strategies to catch your exceptions; any such behavior is an implementation detail and
/// is not guaranteed across strategies or future versions.</para>
/// </remarks>
public ValueTask<Outcome<TResult>> ExecuteOutcomeAsync<TResult, TState>(
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
Expand Down
22 changes: 17 additions & 5 deletions src/Snippets/Docs/CircuitBreaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ public static async ValueTask Pattern_ReduceThrownExceptions()
#region circuit-breaker-pattern-reduce-thrown-exceptions

var context = ResilienceContextPool.Shared.Get();

var circuitBreaker = new ResiliencePipelineBuilder()
.AddCircuitBreaker(new()
{
Expand All @@ -278,11 +279,22 @@ public static async ValueTask Pattern_ReduceThrownExceptions()
})
.Build();

Outcome<HttpResponseMessage> outcome = await circuitBreaker.ExecuteOutcomeAsync(static async (ctx, state) =>
{
var response = await IssueRequest();
return Outcome.FromResult(response);
}, context, "state");
Outcome<HttpResponseMessage> outcome =
await circuitBreaker.ExecuteOutcomeAsync<HttpResponseMessage, string>(
static async (ctx, state) =>
{
try
{
var response = await IssueRequest();
return Outcome.FromResult(response);
}
catch (Exception e)
{
return Outcome.FromException<HttpResponseMessage>(e);
}
},
context,
"state");

ResilienceContextPool.Shared.Return(context);

Expand Down
18 changes: 14 additions & 4 deletions src/Snippets/Docs/Fallback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,22 @@ public static async Task Pattern_ReplaceException()
public static async ValueTask<HttpResponseMessage> Action()
{
var context = ResilienceContextPool.Shared.Get();

var outcome = await WhateverPipeline.ExecuteOutcomeAsync<HttpResponseMessage, string>(
async (ctx, state) =>
static async (ctx, state) =>
{
var result = await ActionCore();
return Outcome.FromResult(result);
}, context, "state");
try
{
var result = await ActionCore();
return Outcome.FromResult(result);
}
catch (Exception e)
{
return Outcome.FromException<HttpResponseMessage>(e);
}
},
context,
"state");

if (outcome.Exception is HttpRequestException requestException)
{
Expand Down
38 changes: 33 additions & 5 deletions src/Snippets/Docs/Migration.Execute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,23 @@ public static async Task SafeExecute_V8()

// Asynchronous execution
var context = ResilienceContextPool.Shared.Get();
Outcome<int> pipelineResult = await pipeline.ExecuteOutcomeAsync(
static async (ctx, state) => Outcome.FromResult(await MethodAsync(ctx.CancellationToken)), context, "state");

Outcome<int> pipelineResult =
await pipeline.ExecuteOutcomeAsync<int, string>(
static async (ctx, state) =>
{
try
{
return Outcome.FromResult(await MethodAsync(ctx.CancellationToken));
}
catch (Exception e)
{
return Outcome.FromException<int>(e);
}
},
context,
"state");

ResilienceContextPool.Shared.Return(context);

// Assess policy result
Expand All @@ -73,7 +88,6 @@ public static async Task SafeExecute_V8()
else
{
Exception exception = pipelineResult.Exception;

// Process failure

// If needed you can rethrow the exception
Expand All @@ -95,8 +109,22 @@ public static async Task SafeExecute_V8()
.Build();

context = ResilienceContextPool.Shared.Get();
pipelineResult = await pipelineWithContext.ExecuteOutcomeAsync(
static async (ctx, state) => Outcome.FromResult(await MethodAsync(ctx.CancellationToken)), context, "state");

pipelineResult =
await pipelineWithContext.ExecuteOutcomeAsync<int, string>(
static async (ctx, state) =>
{
try
{
return Outcome.FromResult(await MethodAsync(ctx.CancellationToken));
}
catch (Exception e)
{
return Outcome.FromException<int>(e);
}
},
context,
"state");

context.Properties.TryGetValue(contextKey, out var ctxValue);
ResilienceContextPool.Shared.Return(context);
Expand Down
Loading