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

Allow tracking of current CircuitControllers and output hystrix style… #149

Closed
wants to merge 1 commit into from
Closed
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
8 changes: 8 additions & 0 deletions src/Polly.Shared/CircuitBreaker/AdvancedCircuitController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ Action onHalfOpen
_minimumThroughput = minimumThroughput;
}

public override HealthCount HealthCount
{
get
{
return _metrics.GetHealthCount_NeedsLock();
}
}

public override void OnCircuitReset(Context context)
{
using (TimedLock.Lock(_lock))
Expand Down
8 changes: 8 additions & 0 deletions src/Polly.Shared/CircuitBreaker/CircuitBreakerPolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ public CircuitState CircuitState
get { return _breakerController.CircuitState; }
}

/// <summary>
/// Gets the state of the underlying circuit.
/// </summary>
public IHealthCount HealthCount
{
get { return _breakerController.HealthCount; }
}

/// <summary>
/// Gets the last exception handled by the circuit-breaker.
/// </summary>
Expand Down
8 changes: 5 additions & 3 deletions src/Polly.Shared/CircuitBreaker/CircuitStateController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ internal abstract class CircuitStateController<TResult> : ICircuitController<TRe
protected readonly object _lock = new object();

protected CircuitStateController(
TimeSpan durationOfBreak,
Action<DelegateResult<TResult>, TimeSpan, Context> onBreak,
Action<Context> onReset,
TimeSpan durationOfBreak,
Action<DelegateResult<TResult>, TimeSpan, Context> onBreak,
Action<Context> onReset,
Action onHalfOpen)
{
_durationOfBreak = durationOfBreak;
Expand Down Expand Up @@ -147,6 +147,8 @@ public void OnActionPreExecute()
public abstract void OnActionFailure(DelegateResult<TResult> outcome, Context context);

public abstract void OnCircuitReset(Context context);

public abstract HealthCount HealthCount { get; }
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ Action onHalfOpen
_exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
}

public override HealthCount HealthCount
{
get
{
return new HealthCount() { Failures = _count };
}
}

public override void OnCircuitReset(Context context)
{
using (TimedLock.Lock(_lock))
Expand Down
2 changes: 1 addition & 1 deletion src/Polly.Shared/CircuitBreaker/HealthCount.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Polly.CircuitBreaker
{
internal class HealthCount
internal class HealthCount : IHealthCount
{
public int Successes { get; set; }

Expand Down
1 change: 1 addition & 0 deletions src/Polly.Shared/CircuitBreaker/ICircuitController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace Polly.CircuitBreaker
internal interface ICircuitController<TResult>
{
CircuitState CircuitState { get; }
HealthCount HealthCount { get; }
Exception LastException { get; }
TResult LastHandledResult { get; }
void Isolate();
Expand Down
28 changes: 28 additions & 0 deletions src/Polly.Shared/CircuitBreaker/IHealthCount.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace Polly.CircuitBreaker
{
/// <summary>
/// Store and report health metrics
/// </summary>
public interface IHealthCount
{
/// <summary>
/// Success count
/// </summary>
int Successes { get; }

/// <summary>
/// Failure count
/// </summary>
int Failures { get; }

/// <summary>
/// Total count (probably success + failure)
/// </summary>
int Total { get; }

/// <summary>
/// Start time for metric collection
/// </summary>
long StartedAt { get; }
}
}
27 changes: 27 additions & 0 deletions src/Polly.Shared/CollectMetricsSyntax.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Linq;
using Polly.CircuitBreaker;
using Polly.Utilities;
using Polly.Shared;

namespace Polly
{
/// <summary>
/// Fluent API for adding a policy to metrics.
/// </summary>
public static class CollectMetricsSyntax
{
/// <summary>
/// <param name="policyBuilder">The policy builder.</param>
/// <para> Adds policy to metrics collection</para>
/// </summary>
/// <param name="name">The policy name.</param>
/// <returns>The policy instance.</returns>
public static CircuitBreakerPolicy CollectMetrics(this CircuitBreakerPolicy policyBuilder, string name)
{
CollectedPolicies.Add(name, policyBuilder);

return policyBuilder;
}
}
}
36 changes: 36 additions & 0 deletions src/Polly.Shared/CollectedPolicies.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Polly.CircuitBreaker;
using System;
using System.Collections.Generic;
using System.Text;

namespace Polly.Shared
{
/// <summary>
/// Static storage for the current collection of policies to report metrics on.
/// </summary>
public class CollectedPolicies
{
private static WeakDictionary<string, CircuitBreakerPolicy> _policies;
static CollectedPolicies() {
_policies = new WeakDictionary<string, CircuitBreakerPolicy>();
}

/// <summary>
/// Return the current list of policies
/// </summary>
public static IEnumerable<KeyValuePair<string, CircuitBreakerPolicy>> All
{
get { return _policies.All(); }
}

/// <summary>
/// Add a policy to the collection
/// </summary>
/// <param name="name">Unique name for this policy</param>
/// <param name="policy">The policy to record</param>
public static void Add(string name, CircuitBreakerPolicy policy)
{
_policies.Add(name, policy);
}
}
}
71 changes: 71 additions & 0 deletions src/Polly.Shared/HystrixCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Polly.Shared
{
/// <summary>
/// Data class for output to be consumed by Hystrix Dashboard (https://github.com/Netflix/Hystrix/tree/master/hystrix-dashboard)
/// </summary>
public class HystrixCommand
{
private static readonly DateTime Jan1St1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

public HystrixCommand()
{
currentTime = (long)((DateTime.UtcNow - Jan1St1970).TotalMilliseconds);
rollingCountTimeout = -1;
}

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public string type { get { return "HystrixCommand"; } }
public string name { get; set; }
public string group { get; set; }
public long currentTime { get; set; }
public bool isCircuitBreakerOpen { get; set; }
public int errorPercentage { get; set; }
public int errorCount { get; set; }
public int requestCount { get; set; }
public int rollingCountBadRequests { get; set; }
public int rollingCountCollapsedRequests { get; set; }
public int rollingCountEmit { get; set; }
public int rollingCountExceptionsThrown { get; set; }
public int rollingCountFailure { get; set; }
public int rollingCountFallbackEmit { get; set; }
public int rollingCountFallbackFailure { get; set; }
public int rollingCountFallbackMissing { get; set; }
public int rollingCountFallbackRejection { get; set; }
public int rollingCountFallbackSuccess { get; set; }
public int rollingCountResponsesFromCache { get; set; }
public int rollingCountSemaphoreRejected { get; set; }
public int rollingCountShortCircuited { get; set; }
public int rollingCountSuccess { get; set; }
public int rollingCountThreadPoolRejected { get; set; }
public int rollingCountTimeout { get; set; }
public int currentConcurrentExecutionCount { get; set; }
public int rollingMaxConcurrentExecutionCount { get; set; }
public int latencyExecute_mean { get; set; }
public Dictionary<string, int> latencyExecute { get; set; }
public int latencyTotal_mean { get; set; }
public Dictionary<string, int> latencyTotal { get; set; }
public int propertyValue_circuitBreakerRequestVolumeThreshold { get; set; }
public int propertyValue_circuitBreakerSleepWindowInMilliseconds { get; set; }
public int propertyValue_circuitBreakerErrorThresholdPercentage { get; set; }
public bool propertyValue_circuitBreakerForceOpen { get; set; }
public bool propertyValue_circuitBreakerForceClosed { get; set; }
public bool propertyValue_circuitBreakerEnabled { get; set; }
public string propertyValue_executionIsolationStrategy { get; set; }
public int propertyValue_executionIsolationThreadTimeoutInMilliseconds { get; set; }
public int propertyValue_executionTimeoutInMilliseconds { get; set; }
public bool propertyValue_executionIsolationThreadInterruptOnTimeout { get; set; }
public object propertyValue_executionIsolationThreadPoolKeyOverride { get; set; }
public int propertyValue_executionIsolationSemaphoreMaxConcurrentRequests { get; set; }
public int propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests { get; set; }
public int propertyValue_metricsRollingStatisticalWindowInMilliseconds { get; set; }
public bool propertyValue_requestCacheEnabled { get; set; }
public bool propertyValue_requestLogEnabled { get; set; }
public int reportingHosts { get; set; }
public string threadPool { get; set; }
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
}
}
38 changes: 38 additions & 0 deletions src/Polly.Shared/MetricStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Polly.Shared
{
/// Find and return the current metrics for the system (in Hystrix format)
public class MetricStream
{
/// an infinite stream of all metrics
/// <param name="sleepFunc">The method will be called between each set of outputs to slow the stream down. We suggest "() => Thread.Sleep(1000)"</param>
public static IEnumerable<object> All(Action sleepFunc)
{
while (true)
{
foreach (var policy in CollectedPolicies.All)
{
yield return new HystrixCommand
{
rollingCountSuccess = policy.Value.HealthCount.Successes,
rollingCountFailure = policy.Value.HealthCount.Failures,
isCircuitBreakerOpen = (policy.Value.CircuitState != CircuitBreaker.CircuitState.Closed),
name = policy.Key,
group = "Group",
latencyExecute = new Dictionary<string, int>() { { "0", 0 }, { "25", 0 }, { "50", 0 }, { "75", 0 }, { "90", 0 }, { "95", 0 }, { "99", 0 }, { "99.5", 0 }, { "100", 0 } },
latencyTotal = new Dictionary<string, int>() { { "0", 0 }, { "25", 0 }, { "50", 0 }, { "75", 0 }, { "90", 0 }, { "95", 0 }, { "99", 0 }, { "99.5", 0 }, { "100", 0 } },
propertyValue_executionIsolationStrategy = "THREAD",
threadPool = "ThreadPool"
};
}

// yield return new HystrixThreadPool { type = "HystrixThreadPool", name = "Order" };

sleepFunc.Invoke();
}
}
}
}
6 changes: 6 additions & 0 deletions src/Polly.Shared/Polly.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
<Import_RootNamespace>Polly.Shared</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)CircuitBreaker\IHealthCount.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CollectedPolicies.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CollectMetricsSyntax.cs" />
<Compile Include="$(MSBuildThisFileDirectory)AdvancedCircuitBreakerTResultSyntax.cs" />
<Compile Include="$(MSBuildThisFileDirectory)AdvancedCircuitBreakerTResultSyntaxAsync.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CircuitBreakerSyntax.cs" />
Expand All @@ -35,6 +38,8 @@
<Compile Include="$(MSBuildThisFileDirectory)ContextualPolicyAsync.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ExceptionPredicate.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DelegateResult.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HystrixCommand.cs" />
<Compile Include="$(MSBuildThisFileDirectory)MetricStream.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Policy.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Policy.HandleSyntax.cs" />
<Compile Include="$(MSBuildThisFileDirectory)PolicyAsync.cs" />
Expand Down Expand Up @@ -66,5 +71,6 @@
<Compile Include="$(MSBuildThisFileDirectory)Utilities\ReadOnlyDictionary.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\SystemClock.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\TimedLock.cs" />
<Compile Include="$(MSBuildThisFileDirectory)WeakDictionary.cs" />
</ItemGroup>
</Project>
39 changes: 39 additions & 0 deletions src/Polly.Shared/WeakDictionary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace Polly.Shared
{
internal class WeakDictionary<K, V>
{
private HashSet<KeyValuePair<K, WeakReference>> list;

public WeakDictionary()
{
list = new HashSet<KeyValuePair<K, WeakReference>>();
}

public void Add(K k, V v)
{
list.Add(new KeyValuePair<K, WeakReference>(k, new WeakReference(v)));
}

public IEnumerable<KeyValuePair<K, V>> All()
{
var cloneList = new HashSet<KeyValuePair<K, WeakReference>>(list);
foreach (var pair in cloneList)
{
object value = pair.Value.Target;
if (value != null)
{
yield return new KeyValuePair<K,V>(pair.Key, (V)value);
}
else
{
list.Remove(pair);
}
}
}
}
}
10 changes: 10 additions & 0 deletions src/Polly.SharedSpecs/AdvancedCircuitBreakerSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,16 @@ public void Should_initialise_to_closed_state()
breaker.CircuitState.Should().Be(CircuitState.Closed);
}

[Fact]
public void Should_return_health_count()
{
CircuitBreakerPolicy breaker = Policy
.Handle<DivideByZeroException>()
.AdvancedCircuitBreaker(0.5, TimeSpan.FromSeconds(10), 4, TimeSpan.FromSeconds(30));

breaker.HealthCount.StartedAt.Should().BeGreaterThan(0);
}

#endregion

#region Circuit-breaker threshold-to-break tests
Expand Down
Loading