Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge development branch for v5.6.0 #373

Merged
merged 24 commits into from
Nov 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fc18d01
Start v5.6.0 release notes
reisenberger Nov 12, 2017
2ac94cf
Prepare engines for handling InnerExceptions
reisenberger Nov 12, 2017
976676c
Add vscode files to gitignore
reisenberger Nov 17, 2017
c380eeb
Expose policies in a PolicyWrap
reisenberger Nov 17, 2017
9e3cd0c
(fix file references in .projitems)
reisenberger Nov 17, 2017
733f42e
Run in parallel all tests which do not manipulate the abstracted cloc…
jbergens Nov 17, 2017
8533508
Set version number to 5.6.0
reisenberger Nov 17, 2017
6791599
Implement HandleInner and OrInner
reisenberger Nov 18, 2017
ad533a0
Add HandleInner tests for retry policies
reisenberger Nov 18, 2017
c41be1f
Add HandleInner tests for circuit breaker policies
reisenberger Nov 18, 2017
cc33541
Refine HandleInner with ExecuteAndCapture functionality
reisenberger Nov 19, 2017
4a68cc6
Add HandleInner documentation
reisenberger Nov 19, 2017
014d121
Update changelogs and acknowledgement
MartinSStewart Nov 17, 2017
25c308e
Add IPolicyWrap.GetPolicy/ies<TPolicy> extension methods (#365)
reisenberger Nov 20, 2017
cb4be80
determine wait duration from error response (#366)
reisenberger Nov 20, 2017
9da3f7d
WaitAndRetry/Forever: calc wait from error response (#367)
reisenberger Nov 20, 2017
0cb4732
Merge remote-tracking branch 'refs/remotes/App-vNext/v560dev' into v5…
reisenberger Nov 21, 2017
8534455
Additional parallel test isolation
reisenberger Nov 21, 2017
e116443
Merge pull request #368 from reisenberger/v560InnerExceptions
reisenberger Nov 21, 2017
10eae40
Make Context wrap, not extend, Dictionary
reisenberger Nov 22, 2017
76124f3
Throw ArgumentNullException from assignment
reisenberger Nov 22, 2017
26a59ea
Make PolicyWrap take interfaces as parameters
reisenberger Nov 22, 2017
dfa8fc9
Add v560 doco
reisenberger Nov 24, 2017
3f76b98
Fix non-generic Fallback with generic Execute method (#372)
reisenberger Nov 26, 2017
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ TestResults
*.user
*.sln.docstates
.vs/
.vscode/

# Build results
[Dd]ebug/
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 5.6.0
- Add ability to handle inner exceptions natively: .HandleInner&lt;TEx&gt;()
- Allow WaitAndRetry policies to calculate wait based on the handled fault
- Add the ability to access the policies within an IPolicyWrap
- Allow PolicyWrap to configure policies expressed as interfaces
- Bug fix: set context keys for generic execute methods with PolicyWrap
- Bug fix: generic TResult method with non-generic fallback policy
- Performance improvements
- Multiple build speed improvements

## 5.5.0
- Bug fix: non-generic CachePolicy with PolicyWrap
- Add Cache interfaces
Expand Down
2 changes: 1 addition & 1 deletion GitVersionConfig.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
next-version: 5.5.0
next-version: 5.6.0
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ You can install the Strongly Named version via:
Install-Package Polly.Net40Async-Signed



# Resilience policies

Polly offers multiple resilience policies:
Expand All @@ -52,8 +51,6 @@ Fault-handling policies handle specific exceptions thrown by, or results returne

### (for fault-handling policies: Retry family, CircuitBreaker family and Fallback)



```csharp
// Single exception type
Policy
Expand All @@ -72,6 +69,11 @@ Policy
Policy
.Handle<SqlException>(ex => ex.Number == 1205)
.Or<ArgumentException>(ex => ex.ParamName == "example")

// Inner exceptions of ordinary exceptions or AggregateException, with or without conditions
Policy
.HandleInner<HttpResponseException>()
.OrInner<OperationCanceledException>(ex => ex.CancellationToken == myToken)
```

## Step 1b: (optionally) Specify return results you want to handle
Expand Down Expand Up @@ -938,6 +940,13 @@ For details of changes by release see the [change log](https://github.com/App-vN
* [@jiimaho](https://github.com/jiimaho) and [@Extremo75](https://github.com/ExtRemo75) - Provide public factory methods for PolicyResult, to support testing.
* [@Extremo75](https://github.com/ExtRemo75) - Allow fallback delegates to take handled fault as input parameter.
* [@reisenberger](https://github.com/reisenberger) and [@seanfarrow](https://github.com/SeanFarrow) - Add CachePolicy, with interfaces for pluggable cache providers and serializers.
* Thanks to the awesome devs at [@tretton37](https://github.com/tretton37) who delivered the following as part of a one-day in-company hackathon led by [@reisenberger](https://github.com/reisenberger), sponsored by [@tretton37](https://github.com/tretton37) and convened by [@thecodejunkie](https://github.com/thecodejunkie)
* [@matst80](https://github.com/matst80) - Allow WaitAndRetry to take handled fault as an input to the sleepDurationProvider, allowing WaitAndRetry to take account of systems which specify a duration to wait as part of a fault response; eg Azure CosmosDB may specify this in `x-ms-retry-after-ms` headers or in a property to an exception thrown by the Azure CosmosDB SDK.
* [@MartinSStewart](https://github.com/martinsstewart) - Add GetPolicies() extension methods to IPolicyWrap.
* [@jbergens37](https://github.com/jbergens37) - Parallelize test running where possible, to improve overall build speed.
* [@reisenberger](https://github.com/reisenberger) - Add new .HandleInner<TException>(...) syntax for handling inner exceptions natively.
* [@rjongeneelen](https://github.com/rjongeneelen) and [@reisenberger](https://github.com/reisenberger) - Allow PolicyWrap configuration to configure policies via interfaces.
* [@reisenberger](https://github.com/reisenberger) - Performance improvements.

# Sample Projects

Expand Down
2 changes: 1 addition & 1 deletion src/Polly.Net40Async.Specs/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
using Xunit;

[assembly: AssemblyTitle("Polly.Net40Async.Specs")]
[assembly: CollectionBehavior(DisableTestParallelization = true)]
[assembly: CollectionBehavior(DisableTestParallelization = false)]
12 changes: 11 additions & 1 deletion src/Polly.Net40Async.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,17 @@
<tags>Exception Handling Resilience Transient Fault Policy Circuit Breaker CircuitBreaker Retry Wait Cache Cache-aside Bulkhead Fallback Timeout Throttle Parallelization</tags>
<copyright>Copyright © 2017, App vNext</copyright>
<releaseNotes>
v5.0 is a major release with significant new resilience policies: Timeout; Bulkhead Isolation; Fallback; Cache; and PolicyWrap. See release notes back to v5.0.0 for full details. v5.0.5 includes important circuit-breaker fixes.
v5.0 is a major release with significant new resilience policies: Timeout; Bulkhead Isolation; Fallback; Cache; and PolicyWrap. See release notes back to v5.0.0 for full details.

5.6.0
---------------------
- Add ability to handle inner exceptions natively: .HandleInner&lt;TEx&gt;()
- Allow WaitAndRetry policies to calculate wait based on the handled fault
- Add the ability to access the policies within an IPolicyWrap
- Allow PolicyWrap to take interfaces as parameters
- Bug fix: set context keys for generic execute method with PolicyWrap
- Bug fix: generic TResult method with non-generic fallback policy
- Performance improvements

5.5.0
---------------------
Expand Down
2 changes: 1 addition & 1 deletion src/Polly.Net45.Specs/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
using Xunit;

[assembly: AssemblyTitle("Polly.Net45.Specs")]
[assembly: CollectionBehavior(DisableTestParallelization = true)]
[assembly: CollectionBehavior(DisableTestParallelization = false)]
2 changes: 1 addition & 1 deletion src/Polly.NetStandard11.Specs/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[assembly: Xunit.CollectionBehavior(DisableTestParallelization = true)]
[assembly: Xunit.CollectionBehavior(DisableTestParallelization = false)]
2 changes: 1 addition & 1 deletion src/Polly.NetStandard11/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using System.Runtime.CompilerServices;

[assembly: AssemblyTitle("Polly")]
[assembly: AssemblyVersion("5.5.0.0")]
[assembly: AssemblyVersion("5.6.0.0")]
[assembly: CLSCompliant(true)]

[assembly: InternalsVisibleTo("Polly.NetStandard11.Specs")]
16 changes: 14 additions & 2 deletions src/Polly.Shared/CircuitBreaker/CircuitBreakerEngine.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading;

#if NET40
using ExceptionDispatchInfo = Polly.Utilities.ExceptionDispatchInfo;
#endif

namespace Polly.CircuitBreaker
{
internal partial class CircuitBreakerEngine
Expand Down Expand Up @@ -36,13 +41,20 @@ internal static TResult Implementation<TResult>(
}
catch (Exception ex)
{
if (!shouldHandleExceptionPredicates.Any(predicate => predicate(ex)))
Exception handledException = shouldHandleExceptionPredicates
.Select(predicate => predicate(ex))
.FirstOrDefault(e => e != null);
if (handledException == null)
{
throw;
}

breakerController.OnActionFailure(new DelegateResult<TResult>(ex), context);
breakerController.OnActionFailure(new DelegateResult<TResult>(handledException), context);

if (handledException != ex)
{
ExceptionDispatchInfo.Capture(handledException).Throw();
}
throw;
}
}
Expand Down
20 changes: 15 additions & 5 deletions src/Polly.Shared/CircuitBreaker/CircuitBreakerEngineAsync.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@


using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;

#if NET40
using ExceptionDispatchInfo = Polly.Utilities.ExceptionDispatchInfo;
#endif

namespace Polly.CircuitBreaker
{
internal partial class CircuitBreakerEngine
Expand Down Expand Up @@ -40,13 +43,20 @@ internal static async Task<TResult> ImplementationAsync<TResult>(
}
catch (Exception ex)
{
if (!shouldHandleExceptionPredicates.Any(predicate => predicate(ex)))
Exception handledException = shouldHandleExceptionPredicates
.Select(predicate => predicate(ex))
.FirstOrDefault(e => e != null);
if (handledException == null)
{
throw;
}

breakerController.OnActionFailure(new DelegateResult<TResult>(ex), context);
breakerController.OnActionFailure(new DelegateResult<TResult>(handledException), context);

if (handledException != ex)
{
ExceptionDispatchInfo.Capture(handledException).Throw();
}
throw;
}

Expand Down
159 changes: 159 additions & 0 deletions src/Polly.Shared/Context.Dictionary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
using System;
using System.Collections;
using System.Collections.Generic;

namespace Polly
{
/// <summary>
/// Context that carries with a single execution through a Policy. Commonly-used properties are directly on the class. Backed by a dictionary of string key / object value pairs, to which user-defined values may be added.
/// <remarks>Do not re-use an instance of <see cref="Context"/> across more than one execution.</remarks>
/// </summary>
public partial class Context : IDictionary<string, object>, IDictionary
#if !NET40
, IReadOnlyDictionary<string, object>
#endif
{
// For an individual execution through a policy or policywrap, it is expected that all execution steps (for example executing the user delegate, invoking policy-activity delegates such as onRetry, onBreak, onTimeout etc) execute sequentially.
// Therefore, this class is intentionally not constructed to be safe for concurrent access from multiple threads.

private Dictionary<string, object> wrappedDictionary = null;

private Dictionary<string, object> WrappedDictionary => wrappedDictionary ?? (wrappedDictionary = new Dictionary<string, object>());

/// <summary>
/// Initializes a new instance of the <see cref="Context"/> class, with the specified <paramref name="executionKey" /> and the supplied <paramref name="contextData"/>.
/// </summary>
/// <param name="executionKey">The execution key.</param>
/// <param name="contextData">The context data.</param>
public Context(String executionKey, IDictionary<string, object> contextData) : this(contextData)
{
ExecutionKey = executionKey;
}

internal Context(IDictionary<string, object> contextData) : this()
{
if (contextData == null) throw new ArgumentNullException(nameof(contextData));
wrappedDictionary = new Dictionary<string, object>(contextData);
}

#region IDictionary<string,object> implementation

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
public ICollection<string> Keys => WrappedDictionary.Keys;

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
public ICollection<object> Values => WrappedDictionary.Values;

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
public int Count => WrappedDictionary.Count;

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
bool ICollection<KeyValuePair<string, object>>.IsReadOnly => ((IDictionary<string, object>)WrappedDictionary).IsReadOnly;

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
public object this[string key]
{
get => WrappedDictionary[key];
set => WrappedDictionary[key] = value;
}

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
public void Add(string key, object value)
{
WrappedDictionary.Add(key, value);
}

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
public bool ContainsKey(string key) => WrappedDictionary.ContainsKey(key);

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
public bool Remove(string key) => WrappedDictionary.Remove(key);

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
public bool TryGetValue(string key, out object value) => WrappedDictionary.TryGetValue(key, out value);

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item) => ((IDictionary<string, object>)WrappedDictionary).Add(item);

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
public void Clear() => WrappedDictionary.Clear();

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item) => ((IDictionary<string, object>)WrappedDictionary).Contains(item);

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) => ((IDictionary<string, object>) WrappedDictionary).CopyTo(array, arrayIndex);

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item) => ((IDictionary<string, object>)WrappedDictionary).Remove(item);

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
public IEnumerator<KeyValuePair<string, object>> GetEnumerator() => WrappedDictionary.GetEnumerator();

/// <inheritdoc cref="IDictionary{TKey,Value}"/>
IEnumerator IEnumerable.GetEnumerator() => WrappedDictionary.GetEnumerator();

/// <inheritdoc cref="IDictionary"/>
public void Add(object key, object value)
{
((IDictionary)WrappedDictionary).Add(key, value);
}

/// <inheritdoc cref="IDictionary"/>
public bool Contains(object key)
{
return ((IDictionary)WrappedDictionary).Contains(key);
}

/// <inheritdoc cref="IDictionary"/>
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return ((IDictionary)WrappedDictionary).GetEnumerator();
}

/// <inheritdoc cref="IDictionary"/>
public void Remove(object key)
{
((IDictionary)WrappedDictionary).Remove(key);
}

/// <inheritdoc cref="IDictionary"/>
public void CopyTo(Array array, int index)
{
((IDictionary)WrappedDictionary).CopyTo(array, index);
}

#endregion

#if !NET40
#region IReadOnlyDictionary<string, object> implementation
IEnumerable<string> IReadOnlyDictionary<string, object>.Keys => ((IReadOnlyDictionary<string, object>)WrappedDictionary).Keys;

IEnumerable<object> IReadOnlyDictionary<string, object>.Values => ((IReadOnlyDictionary<string, object>)WrappedDictionary).Values;
#endregion
#endif

#region IDictionary implementation

/// <inheritdoc cref="IDictionary"/>
bool IDictionary.IsFixedSize => ((IDictionary)WrappedDictionary).IsFixedSize;

/// <inheritdoc cref="IDictionary"/>
bool IDictionary.IsReadOnly => ((IDictionary)WrappedDictionary).IsReadOnly;

ICollection IDictionary.Keys => ((IDictionary)WrappedDictionary).Keys;

ICollection IDictionary.Values => ((IDictionary)WrappedDictionary).Values;

/// <inheritdoc cref="IDictionary"/>
bool ICollection.IsSynchronized => ((IDictionary)WrappedDictionary).IsSynchronized;

/// <inheritdoc cref="IDictionary"/>
object ICollection.SyncRoot => ((IDictionary)WrappedDictionary).SyncRoot;

/// <inheritdoc cref="IDictionary"/>
object IDictionary.this[object key] { get => ((IDictionary)WrappedDictionary)[key]; set => ((IDictionary)WrappedDictionary)[key] = value; }

#endregion
}
}
Loading