Polly

czyli Andrzeju nie denerwuj się!

Na początku świata...

...był chaos.

i zamieniliśmy go w..

A na początku IT...

...był porządek

A zamieniliśmy go w to

Dawno, dawno temu...

Sponsorzy

my widziani oczami eJaJ 🤦‍♂️

Devin is a lie

Dev Andrzej

Rider

2 tygodnie

3 miesiące później

Błędy, błędy wszędzie...

Nie czy ale kiedy...

Demo

Polly

polly, polly, polly, ...

Dwie opcje

  • reaktywne
  • proaktywne

reaktywne

  • retry
  • circuit breaker 🙈
  • fallback
  • hedging 🙈

proaktywne

  • timeout
  • rate limiter 🙈

Powtarzamy do upadłego

var retryStrategyOptions = new RetryStrategyOptions
{
	ShouldHandle = _ 
		=> ValueTask.FromResult(true),
};


var pipeline = new ResiliencePipelineBuilder()
	.AddRetry(retryStrategyOptions)
	.Build();

await pipeline.ExecuteAsync(async token =>
{    
	var c = new HttpClient();
	var r = await c.GetAsync("http://ooops.pl", token);
	r.EnsureSuccessStatusCode();
});

Zastępnik, substytut, surogat

var fallbackStrategyOptions = new FallbackStrategyOptions<HttpResponseMessage>
{
	FallbackAction = _ => Outcome.FromResultAsValueTask(
    			new HttpResponseMessage(HttpStatusCode.Gone)),    
};


var pipeline = new ResiliencePipelineBuilder<HttpResponseMessage>()
	.AddFallback(fallbackStrategyOptions)
	.Build();

await pipeline.ExecuteAsync<HttpResponseMessage>(async token =>
{
	var c = new HttpClient();
	var r = await c.GetAsync("http://ooops.pl", token);
	r.EnsureSuccessStatusCode();
	return r;
});

Kto pierwszy, ten lepszy

var hedgingStrategyOptions = new HedgingStrategyOptions<HttpResponseMessage>
{
    Delay = TimeSpan.FromSeconds(5), 
    ActionGenerator = static _ => () =>
        Outcome.FromResultAsValueTask(
        	new HttpResponseMessage(HttpStatusCode.Processing)),
};

var pipeline = new ResiliencePipelineBuilder<HttpResponseMessage>()
    .AddHedging(hedgingStrategyOptions)
    .Build();

await pipeline.ExecuteAsync<HttpResponseMessage>(async token =>
{
    var c = new HttpClient();
    var r = await c.GetAsync("http://ooops.pl", token);
    r.EnsureSuccessStatusCode();
    return r;
});

Times up! ⏱

var timeoutStrategyOptions = new TimeoutStrategyOptions
{
    Timeout = TimeSpan.FromSeconds(10),
};

var pipeline = 
    new ResiliencePipelineBuilder<HttpResponseMessage>()
    .AddTimeout(timeoutStrategyOptions)
    .Build();

await pipeline.ExecuteAsync<HttpResponseMessage>(async token =>
{
    var c = new HttpClient();
    var r = await c.GetAsync("http://ooops.pl", token);
    r.EnsureSuccessStatusCode();
    return r;
});

Szczypta chaosu

Szczypta chaosu

Demo

Simmy

arrrrrrrrrrrrrrr

Chaos w kilku smakach

  • Fault
  • Outcome
  • Latency
  • Behavior

throw new Exception();

var pipeline = new ResiliencePipelineBuilder()
    .AddChaosFault(new ChaosFaultStrategyOptions
    {
        FaultGenerator = _ => 
          		new ValueTask<Exception?>(new Exception()),
        InjectionRate = 0.2
    })
    .Build();   
            
await pipeline.ExecuteAsync(async token =>
{    
	var c = new HttpClient();
	var r = await c.GetAsync("http://ooops.pl", token);
	r.EnsureSuccessStatusCode();
});

Outcome

var pipeline = 
	new ResiliencePipelineBuilder<HttpResponseMessage>()
    .AddChaosOutcome(
    	new ChaosOutcomeStrategyOptions<HttpResponseMessage>()
    {
        InjectionRate = 0.2,
        OutcomeGenerator = _ => 
        	new ValueTask<HttpResponseMessage>(
        		new HttpResponseMessage(HttpStatusCode.Ambiguous)),
    }).Build();   
            
await pipeline.ExecuteAsync<HttpResponseMessage>(async token =>
{    
	var c = new HttpClient();
	var r = await c.GetAsync("http://ooops.pl", token);
	r.EnsureSuccessStatusCode();
    return r;
});
            

Despacito!

var pipeline = new ResiliencePipelineBuilder()
	.AddChaosLatency(new ChaosLatencyStrategyOptions()
    {
        InjectionRate = 0.1,
        Latency = TimeSpan.FromSeconds(3)
    })
	.Build();

await pipeline.ExecuteAsync(async token =>
{    
	var c = new HttpClient();
	var r = await c.GetAsync("http://ooops.pl", token);
	r.EnsureSuccessStatusCode();
});

Behavior Driven Chaos

var pipeline = new ResiliencePipelineBuilder()
	.AddChaosBehavior(new ChaosBehaviorStrategyOptions
    {
        InjectionRate = 0.3,
        BehaviorGenerator = _ => DoEpicShit()
    })
	.Build();

await pipeline.ExecuteAsync(async token =>
{    
	var c = new HttpClient();
	var r = await c.GetAsync("http://ooops.pl", token);
	r.EnsureSuccessStatusCode();
});

Matematyka

Na studiach kazali zapomnieć

  • A*B != B*A
  • A+B != B+A

Polly i DI

w jednym stali domu

Polly & DI

- wymaga Keyed services z .NET 8

- kontener DI z .NET 8 jest OK, ale np. Lamar z Wolverine'a nie wspiera 🤷‍♂️

Tematy zaawansowane*

Telemetria

.ConfigureTelemetry(
	new TelemetryOptions
	{
		LoggerFactory = 
        	LoggerFactory.Create(
            	builder => builder.AddConsole()),
		TelemetryListeners = 
        	{ new MyTelemetryListener() }
	})

Na koniec

- Jarek & Paweł

- OstraPiła podcast - 97 odc

- Ostra Uczy > 50 live'ów

Więcej info*?

* - używaliśmy wersji v7

Links

  • https://www.pollydocs.org

Dzięki za uwagę

Like & subscribe

@ostrapila

@j_stadnicki

@pawel_lukasik

Feedback!

Polly

By Pawel Lukasik